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 }