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 net.jcip.annotations.ThreadSafe;
27  import org.modeshape.common.util.Logger;
28  
29  import java.lang.ref.WeakReference;
30  import java.util.concurrent.CopyOnWriteArraySet;
31  
32  /**
33   * Abstract class that is used to signal that a change set has occurred. This class is typically subclassed by those that wish to
34   * observe changes in content, and {@link Observable#register(Observer) registered} with a {@link Observable}.
35   * <p>
36   * This class maintains a (weak) reference to the ChangeSource instances with which it is registered. Therefore, the observers
37   * will not keep a ChangeSource from being garbage collected. And, if a change source is garbage collected, calling
38   * {@link #unregister()} will clean up naturally.
39   * </p>
40   */
41  @ThreadSafe
42  public abstract class ChangeObserver implements Observer {
43  
44      private final CopyOnWriteArraySet<ChangeSourceReference> sources = new CopyOnWriteArraySet<ChangeSourceReference>();
45  
46      protected ChangeObserver() {
47      }
48  
49      /**
50       * Records that this listener has successfully registered by the supplied {@link Observable}.
51       * 
52       * @param source the source with which this listener was registered
53       */
54      final void registeredWith( Observable source ) {
55          sources.add(new ChangeSourceReference(source));
56      }
57  
58      /**
59       * Records that this listener has successfully unregistered by the supplied {@link Observable}.
60       * 
61       * @param source the source with which this listener was registered
62       */
63      final void unregisteredWith( Observable source ) {
64          sources.remove(new ChangeSourceReference(source));
65      }
66  
67      /**
68       * Unregister this listener from all {@link Observable sources} that it was registered with. This is preferred over calling
69       * {@link Observable#unregister(Observer)} directly.
70       */
71      public void unregister() {
72          doUnregister();
73      }
74  
75      /**
76       * Method called by {@link #unregister()} that actually does the unregistering. This method is final.
77       */
78      protected final void doUnregister() {
79          // Unregister this listener from each source ...
80          for (ChangeSourceReference sourceReference : sources) {
81              Observable source = sourceReference.get();
82              if (source != null) {
83                  try {
84                      source.unregister(this);
85                  } catch (Throwable t) {
86                      Logger.getLogger(getClass()).debug(t, "Error while unregistering {0} from {1}", this, source);
87                  }
88              }
89          }
90      }
91  
92      /**
93       * Determine whether this observer is currently registered with any {@link Observable} instances.
94       * <p>
95       * Although an observer might be registered with an {@link Observable}, if that Observable is garbage collected, then this
96       * observer will no longer be registered with it.
97       * </p>
98       * 
99       * @return true if this observer is registered with at least one {@link Observable} instance, or false if this observer is not
100      *         currently registered with any {@link Observable} instances.
101      */
102     public boolean isRegistered() {
103         for (ChangeSourceReference reference : sources) {
104             if (reference.get() != null) return true;
105         }
106         return false;
107     }
108 
109     /**
110      * Method that is called for each {@link Changes} from the {@link Observable} instance(s) with which this listener is
111      * registered.
112      * 
113      * @param changeSet the change set
114      */
115     public abstract void notify( Changes changeSet );
116 
117     /**
118      * A {@link WeakReference} implementation that provides a valid
119      */
120     protected final class ChangeSourceReference extends WeakReference<Observable> {
121         final int hc;
122 
123         protected ChangeSourceReference( Observable source ) {
124             super(source);
125             this.hc = source.hashCode();
126         }
127 
128         /**
129          * {@inheritDoc}
130          * 
131          * @see java.lang.Object#hashCode()
132          */
133         @Override
134         public int hashCode() {
135             return hc;
136         }
137 
138         /**
139          * {@inheritDoc}
140          * 
141          * @see java.lang.Object#equals(java.lang.Object)
142          */
143         @Override
144         public boolean equals( Object obj ) {
145             if (obj == this) return true;
146             if (obj instanceof ChangeSourceReference) {
147                 ChangeSourceReference that = (ChangeSourceReference)obj;
148                 Observable thisSource = this.get();
149                 Observable thatSource = that.get();
150                 return thisSource == thatSource; // reference equality, not object equality!
151             }
152             if (obj instanceof Observable) {
153                 Observable that = (Observable)obj;
154                 return this.get() == that; // reference equality, not object equality!
155             }
156             return false;
157         }
158     }
159 }