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 }