View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.graph.observe;
25  
26  import java.lang.ref.WeakReference;
27  import java.util.Iterator;
28  import java.util.concurrent.CopyOnWriteArrayList;
29  import java.util.concurrent.atomic.AtomicBoolean;
30  import net.jcip.annotations.ThreadSafe;
31  import org.modeshape.common.util.CheckArg;
32  import org.modeshape.common.util.Logger;
33  
34  /**
35   * Reusable manager of change listeners, typically employed by another {@link Observable} implementation.
36   */
37  @ThreadSafe
38  public class ChangeObservers implements Observable {
39  
40      private final CopyOnWriteArrayList<ObserverReference> observers = new CopyOnWriteArrayList<ObserverReference>();
41      private final AtomicBoolean shutdown = new AtomicBoolean(false);
42  
43      public ChangeObservers() {
44      }
45  
46      /**
47       * {@inheritDoc}
48       * 
49       * @see org.modeshape.graph.observe.Observable#register(org.modeshape.graph.observe.Observer)
50       */
51      public boolean register( Observer observer ) {
52          if (observer != null && !shutdown.get() && observers.addIfAbsent(new ObserverReference(observer))) {
53              if (observer instanceof ChangeObserver) ((ChangeObserver)observer).registeredWith(this);
54              return true;
55          }
56          return false;
57      }
58  
59      /**
60       * {@inheritDoc}
61       * 
62       * @see org.modeshape.graph.observe.Observable#unregister(org.modeshape.graph.observe.Observer)
63       */
64      public boolean unregister( Observer observer ) {
65          if (observer != null && observers.remove(observer)) {
66              if (observer instanceof ChangeObserver) ((ChangeObserver)observer).unregisteredWith(this);
67              return true;
68          }
69          return false;
70      }
71  
72      /**
73       * Unregister all registered observers, and mark this as no longer accepting new registered observers.
74       */
75      public void shutdown() {
76          shutdown.set(true);
77          while (!observers.isEmpty()) {
78              Iterator<ObserverReference> iter = observers.iterator(); // gets snapshot
79              observers.clear();
80              while (iter.hasNext()) {
81                  ObserverReference reference = iter.next();
82                  if (reference.get() != null) {
83                      Observer observer = reference.get();
84                      if (observer instanceof ChangeObserver) ((ChangeObserver)observer).unregisteredWith(this);
85                  }
86              }
87          }
88      }
89  
90      /**
91       * Determine whether there are any observers at the time this method is called.
92       * 
93       * @return true if there are currently no observers, or false if there is at least one observer
94       */
95      public boolean isEmpty() {
96          return observers.isEmpty();
97      }
98  
99      /**
100      * Broadcast the supplied changes to the registered observers.
101      * 
102      * @param changes the changes to broadcast
103      * @throws IllegalArgumentException if the changes reference is null
104      */
105     public void broadcast( Changes changes ) {
106         CheckArg.isNotNull(changes, "changes");
107         for (ObserverReference observerReference : observers) {
108             Observer observer = observerReference.get();
109             if (observer == null) {
110                 observers.remove(observerReference);
111                 continue;
112             }
113             try {
114                 observer.notify(changes);
115             } catch (Throwable t) {
116                 Logger.getLogger(getClass()).debug(t, "Exception while notifying");
117             }
118         }
119     }
120 
121     /**
122      * A {@link WeakReference} implementation that provides a valid
123      */
124     protected final class ObserverReference extends WeakReference<Observer> {
125         final int hc;
126 
127         protected ObserverReference( Observer source ) {
128             super(source);
129             this.hc = source.hashCode();
130         }
131 
132         /**
133          * {@inheritDoc}
134          * 
135          * @see java.lang.Object#hashCode()
136          */
137         @Override
138         public int hashCode() {
139             return hc;
140         }
141 
142         /**
143          * {@inheritDoc}
144          * 
145          * @see java.lang.Object#equals(java.lang.Object)
146          */
147         @Override
148         public boolean equals( Object obj ) {
149             if (obj == this) return true;
150             if (obj instanceof ObserverReference) {
151                 ObserverReference that = (ObserverReference)obj;
152                 Observer thisSource = this.get();
153                 Observer thatSource = that.get();
154                 return thisSource == thatSource; // reference equality, not object equality!
155             }
156             if (obj instanceof Observer) {
157                 Observer that = (Observer)obj;
158                 return this.get() == that; // reference equality, not object equality!
159             }
160             return false;
161         }
162     }
163 }