1 /**
2 * Copyright 2003-2007 Greg Luck
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.event;
18
19 import net.sf.ehcache.CacheException;
20 import net.sf.ehcache.Ehcache;
21 import net.sf.ehcache.Element;
22 import net.sf.ehcache.jcache.JCacheListenerAdaptor;
23 import net.sf.ehcache.distribution.CacheReplicator;
24
25 import net.sf.jsr107cache.CacheListener;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.Set;
29
30 /**
31 * Registered listeners for registering and unregistering CacheEventListeners and multicasting notifications to registrants.
32 * <p/>
33 * There is one of these per Cache
34 *
35 * @author Greg Luck
36 * @version $Id: RegisteredEventListeners.java 512 2007-07-10 09:18:45Z gregluck $
37 */
38 public final class RegisteredEventListeners {
39
40 /**
41 * A Set of CacheEventListeners keyed by listener instance.
42 * CacheEventListener implementations that will be notified of this cache's events.
43 *
44 * @see CacheEventListener
45 */
46 private final Set cacheEventListeners = new HashSet();
47 private final Ehcache cache;
48
49 /**
50 * Constructs a new notification service
51 *
52 * @param cache
53 */
54 public RegisteredEventListeners(Ehcache cache) {
55 this.cache = cache;
56 }
57
58
59 /**
60 * Notifies all registered listeners, in no guaranteed order, that an element was removed
61 *
62 * @param element
63 * @param remoteEvent whether the event came from a remote cache peer
64 * @see CacheEventListener#notifyElementRemoved
65 */
66 public final void notifyElementRemoved(Element element, boolean remoteEvent) throws CacheException {
67 if (hasCacheEventListeners()) {
68 Iterator iterator = cacheEventListeners.iterator();
69 while (iterator.hasNext()) {
70 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
71 if (!isCircularNotification(remoteEvent, cacheEventListener)) {
72 cacheEventListener.notifyElementRemoved(cache, element);
73 }
74 }
75 }
76 }
77
78 /**
79 * Notifies all registered listeners, in no guaranteed order, that an element was put into the cache
80 *
81 * @param element
82 * @param remoteEvent whether the event came from a remote cache peer
83 * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element)
84 */
85 public final void notifyElementPut(Element element, boolean remoteEvent) throws CacheException {
86 if (hasCacheEventListeners()) {
87 Iterator iterator = cacheEventListeners.iterator();
88 while (iterator.hasNext()) {
89 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
90 if (!isCircularNotification(remoteEvent, cacheEventListener)) {
91 cacheEventListener.notifyElementPut(cache, element);
92 }
93 }
94 }
95 }
96
97 /**
98 * Notifies all registered listeners, in no guaranteed order, that an element in the cache was updated
99 *
100 * @param element
101 * @param remoteEvent whether the event came from a remote cache peer
102 * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element)
103 */
104 public final void notifyElementUpdated(Element element, boolean remoteEvent) {
105 if (hasCacheEventListeners()) {
106 Iterator iterator = cacheEventListeners.iterator();
107 while (iterator.hasNext()) {
108 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
109 if (!isCircularNotification(remoteEvent, cacheEventListener)) {
110 cacheEventListener.notifyElementUpdated(cache, element);
111 }
112 }
113 }
114 }
115
116 /**
117 * Notifies all registered listeners, in no guaranteed order, that an element has expired
118 *
119 * @param element the Element to perform the notification on
120 * @param remoteEvent whether the event came from a remote cache peer
121 * @see CacheEventListener#notifyElementExpired
122 */
123 public final void notifyElementExpiry(Element element, boolean remoteEvent) {
124 if (hasCacheEventListeners()) {
125 Iterator iterator = cacheEventListeners.iterator();
126 while (iterator.hasNext()) {
127 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
128 if (!isCircularNotification(remoteEvent, cacheEventListener)) {
129 cacheEventListener.notifyElementExpired(cache, element);
130 }
131 }
132 }
133 }
134
135 /**
136 * Returns whether or not at least one cache event listeners has been registered.
137 *
138 * @return true if a one or more listeners have registered, otherwise false
139 */
140 public final boolean hasCacheEventListeners() {
141 return cacheEventListeners.size() > 0;
142 }
143
144 /**
145 * Notifies all registered listeners, in no guaranteed order, that an element has been
146 * evicted from the cache
147 *
148 * @param element the Element to perform the notification on
149 * @param remoteEvent whether the event came from a remote cache peer
150 * @see CacheEventListener#notifyElementEvicted
151 */
152 public void notifyElementEvicted(Element element, boolean remoteEvent) {
153 if (hasCacheEventListeners()) {
154 Iterator iterator = cacheEventListeners.iterator();
155 while (iterator.hasNext()) {
156 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
157 if (!isCircularNotification(remoteEvent, cacheEventListener)) {
158 cacheEventListener.notifyElementEvicted(cache, element);
159 }
160 }
161 }
162 }
163
164
165 /**
166 * Notifies all registered listeners, in no guaranteed order, that removeAll
167 * has been called and all elements cleared
168 *
169 * @param remoteEvent whether the event came from a remote cache peer
170 * @see CacheEventListener#notifyElementEvicted
171 */
172 public void notifyRemoveAll(boolean remoteEvent) {
173 if (hasCacheEventListeners()) {
174
175 Iterator iterator = cacheEventListeners.iterator();
176 while (iterator.hasNext()) {
177 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
178 if (!isCircularNotification(remoteEvent, cacheEventListener)) {
179 cacheEventListener.notifyRemoveAll(cache);
180 }
181 }
182 }
183 }
184
185
186 /**
187 * CacheReplicators should not be notified of events received remotely, as this would cause
188 * a circular notification
189 *
190 * @param remoteEvent
191 * @param cacheEventListener
192 * @return true is notifiying the listener would cause a circular notification
193 */
194 private static boolean isCircularNotification(boolean remoteEvent, CacheEventListener cacheEventListener) {
195 return remoteEvent && cacheEventListener instanceof CacheReplicator;
196 }
197
198
199 /**
200 * Adds a listener to the notification service. No guarantee is made that listeners will be
201 * notified in the order they were added.
202 *
203 * @param cacheEventListener
204 * @return true if the listener is being added and was not already added
205 */
206 public final boolean registerListener(CacheEventListener cacheEventListener) {
207 if (cacheEventListener == null) {
208 return false;
209 }
210 return cacheEventListeners.add(cacheEventListener);
211 }
212
213 /**
214 * Removes a listener from the notification service.
215 *
216 * @param cacheEventListener
217 * @return true if the listener was present
218 */
219 public final boolean unregisterListener(CacheEventListener cacheEventListener) {
220 if (cacheEventListener instanceof JCacheListenerAdaptor) {
221 removeCacheListenerAdaptor(((JCacheListenerAdaptor)cacheEventListener).getCacheListener());
222 }
223 return cacheEventListeners.remove(cacheEventListener);
224 }
225
226 private void removeCacheListenerAdaptor(CacheListener cacheListener) {
227 for (Iterator iterator = cacheEventListeners.iterator(); iterator.hasNext();) {
228 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
229 if (cacheEventListener instanceof JCacheListenerAdaptor) {
230 if (((JCacheListenerAdaptor)cacheEventListener).getCacheListener() == cacheListener) {
231 cacheEventListeners.remove(cacheEventListener);
232 break;
233 }
234 }
235 }
236 }
237
238 /**
239 * Gets a list of the listeners registered to this class
240 *
241 * @return a list of type <code>CacheEventListener</code>
242 */
243 public final Set getCacheEventListeners() {
244 return cacheEventListeners;
245 }
246
247 /**
248 * Tell listeners to dispose themselves.
249 * Because this method is only ever called from a synchronized cache method, it does not itself need to be
250 * synchronized.
251 */
252 public final void dispose() {
253 Iterator iterator = cacheEventListeners.iterator();
254 while (iterator.hasNext()) {
255 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
256 cacheEventListener.dispose();
257 }
258
259 cacheEventListeners.clear();
260 }
261
262 /**
263 * Returns a string representation of the object. In general, the
264 * <code>toString</code> method returns a string that
265 * "textually represents" this object. The result should
266 * be a concise but informative representation that is easy for a
267 * person to read.
268 *
269 * @return a string representation of the object.
270 */
271 public final String toString() {
272 StringBuffer stringBuffer = new StringBuffer(" cacheEventListeners: ");
273 for (Iterator iterator = cacheEventListeners.iterator(); iterator.hasNext();) {
274 CacheEventListener cacheEventListener = (CacheEventListener) iterator.next();
275 stringBuffer.append(cacheEventListener.getClass().getName()).append(" ");
276 }
277 return stringBuffer.toString();
278 }
279
280
281 }