001: /**
002: * Copyright 2003-2007 Luck Consulting Pty Ltd
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package net.sf.ehcache.event;
016:
017: import net.sf.ehcache.CacheException;
018: import net.sf.ehcache.Ehcache;
019: import net.sf.ehcache.Element;
020: import net.sf.ehcache.distribution.CacheReplicator;
021: import net.sf.ehcache.jcache.JCacheListenerAdaptor;
022: import net.sf.jsr107cache.CacheListener;
023:
024: import java.util.HashSet;
025: import java.util.Iterator;
026: import java.util.Set;
027:
028: /**
029: * Registered listeners for registering and unregistering CacheEventListeners and multicasting notifications to registrants.
030: * <p/>
031: * There is one of these per Cache.
032: * <p/>
033: * This class also has counters to accumulate the numbers of each type of event for statistics purposes.
034: *
035: * @author Greg Luck
036: * @version $Id: RegisteredEventListeners.java 558 2007-10-29 07:14:38Z gregluck $
037: */
038: public class RegisteredEventListeners {
039:
040: /**
041: * A Set of CacheEventListeners keyed by listener instance.
042: * CacheEventListener implementations that will be notified of this cache's events.
043: *
044: * @see CacheEventListener
045: */
046: private final Set cacheEventListeners = new HashSet();
047: private final Ehcache cache;
048:
049: private long elementsRemovedCounter;
050: private long elementsPutCounter;
051: private long elementsUpdatedCounter;
052: private long elementsExpiredCounter;
053: private long elementsEvictedCounter;
054: private long elementsRemoveAllCounter;
055:
056: /**
057: * Constructs a new notification service
058: *
059: * @param cache
060: */
061: public RegisteredEventListeners(Ehcache cache) {
062: this .cache = cache;
063: }
064:
065: /**
066: * Notifies all registered listeners, in no guaranteed order, that an element was removed
067: *
068: * @param element
069: * @param remoteEvent whether the event came from a remote cache peer
070: * @see CacheEventListener#notifyElementRemoved
071: */
072: public final void notifyElementRemoved(Element element,
073: boolean remoteEvent) throws CacheException {
074: elementsRemovedCounter++;
075: if (hasCacheEventListeners()) {
076: Iterator iterator = createThreadSafeIterator();
077: while (iterator.hasNext()) {
078: CacheEventListener cacheEventListener = (CacheEventListener) iterator
079: .next();
080: if (!isCircularNotification(remoteEvent,
081: cacheEventListener)) {
082: cacheEventListener.notifyElementRemoved(cache,
083: element);
084: }
085: }
086: }
087: }
088:
089: /**
090: * Notifies all registered listeners, in no guaranteed order, that an element was put into the cache
091: *
092: * @param element
093: * @param remoteEvent whether the event came from a remote cache peer
094: * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element)
095: */
096: public final void notifyElementPut(Element element,
097: boolean remoteEvent) throws CacheException {
098: elementsPutCounter++;
099: if (hasCacheEventListeners()) {
100: Iterator iterator = createThreadSafeIterator();
101: while (iterator.hasNext()) {
102: CacheEventListener cacheEventListener = (CacheEventListener) iterator
103: .next();
104: if (!isCircularNotification(remoteEvent,
105: cacheEventListener)) {
106: cacheEventListener.notifyElementPut(cache, element);
107: }
108: }
109:
110: }
111: }
112:
113: /**
114: * Notifies all registered listeners, in no guaranteed order, that an element in the cache was updated
115: *
116: * @param element
117: * @param remoteEvent whether the event came from a remote cache peer
118: * @see CacheEventListener#notifyElementPut(net.sf.ehcache.Ehcache,net.sf.ehcache.Element)
119: */
120: public final void notifyElementUpdated(Element element,
121: boolean remoteEvent) {
122: elementsUpdatedCounter++;
123: if (hasCacheEventListeners()) {
124: Iterator iterator = createThreadSafeIterator();
125:
126: while (iterator.hasNext()) {
127: CacheEventListener cacheEventListener = (CacheEventListener) iterator
128: .next();
129: if (!isCircularNotification(remoteEvent,
130: cacheEventListener)) {
131: cacheEventListener.notifyElementUpdated(cache,
132: element);
133: }
134: }
135: }
136: }
137:
138: /**
139: * Notifies all registered listeners, in no guaranteed order, that an element has expired
140: *
141: * @param element the Element to perform the notification on
142: * @param remoteEvent whether the event came from a remote cache peer
143: * @see CacheEventListener#notifyElementExpired
144: */
145: public final void notifyElementExpiry(Element element,
146: boolean remoteEvent) {
147: elementsExpiredCounter++;
148: if (hasCacheEventListeners()) {
149: Iterator iterator = createThreadSafeIterator();
150: while (iterator.hasNext()) {
151: CacheEventListener cacheEventListener = (CacheEventListener) iterator
152: .next();
153: if (!isCircularNotification(remoteEvent,
154: cacheEventListener)) {
155: cacheEventListener.notifyElementExpired(cache,
156: element);
157: }
158: }
159: }
160: }
161:
162: /**
163: * Returns whether or not at least one cache event listeners has been registered.
164: *
165: * @return true if a one or more listeners have registered, otherwise false
166: */
167: public final boolean hasCacheEventListeners() {
168: return cacheEventListeners.size() > 0;
169: }
170:
171: /**
172: * Notifies all registered listeners, in no guaranteed order, that an element has been
173: * evicted from the cache
174: *
175: * @param element the Element to perform the notification on
176: * @param remoteEvent whether the event came from a remote cache peer
177: * @see CacheEventListener#notifyElementEvicted
178: */
179: public void notifyElementEvicted(Element element,
180: boolean remoteEvent) {
181: elementsEvictedCounter++;
182: if (hasCacheEventListeners()) {
183: Iterator iterator = createThreadSafeIterator();
184: while (iterator.hasNext()) {
185: CacheEventListener cacheEventListener = (CacheEventListener) iterator
186: .next();
187: if (!isCircularNotification(remoteEvent,
188: cacheEventListener)) {
189: cacheEventListener.notifyElementEvicted(cache,
190: element);
191: }
192: }
193: }
194: }
195:
196: /**
197: * Notifies all registered listeners, in no guaranteed order, that removeAll
198: * has been called and all elements cleared
199: *
200: * @param remoteEvent whether the event came from a remote cache peer
201: * @see CacheEventListener#notifyElementEvicted
202: */
203: public void notifyRemoveAll(boolean remoteEvent) {
204: elementsRemoveAllCounter++;
205: if (hasCacheEventListeners()) {
206: Iterator iterator = createThreadSafeIterator();
207: while (iterator.hasNext()) {
208: CacheEventListener cacheEventListener = (CacheEventListener) iterator
209: .next();
210: if (!isCircularNotification(remoteEvent,
211: cacheEventListener)) {
212: cacheEventListener.notifyRemoveAll(cache);
213: }
214: }
215: }
216: }
217:
218: private Iterator createThreadSafeIterator() {
219: HashSet copy;
220: synchronized (cacheEventListeners) {
221: copy = new HashSet(cacheEventListeners);
222: }
223: return copy.iterator();
224: }
225:
226: /**
227: * CacheReplicators should not be notified of events received remotely, as this would cause
228: * a circular notification
229: *
230: * @param remoteEvent
231: * @param cacheEventListener
232: * @return true is notifiying the listener would cause a circular notification
233: */
234: private static boolean isCircularNotification(boolean remoteEvent,
235: CacheEventListener cacheEventListener) {
236: return remoteEvent
237: && cacheEventListener instanceof CacheReplicator;
238: }
239:
240: /**
241: * Adds a listener to the notification service. No guarantee is made that listeners will be
242: * notified in the order they were added.
243: *
244: * @param cacheEventListener
245: * @return true if the listener is being added and was not already added
246: */
247: public final boolean registerListener(
248: CacheEventListener cacheEventListener) {
249: if (cacheEventListener == null) {
250: return false;
251: }
252: boolean added;
253: synchronized (cacheEventListeners) {
254: added = cacheEventListeners.add(cacheEventListener);
255: }
256: return added;
257: }
258:
259: /**
260: * Removes a listener from the notification service.
261: *
262: * @param cacheEventListener
263: * @return true if the listener was present
264: */
265: public final boolean unregisterListener(
266: CacheEventListener cacheEventListener) {
267: if (cacheEventListener instanceof JCacheListenerAdaptor) {
268: removeCacheListenerAdaptor(((JCacheListenerAdaptor) cacheEventListener)
269: .getCacheListener());
270: }
271: boolean removed;
272: synchronized (cacheEventListeners) {
273: removed = cacheEventListeners.remove(cacheEventListener);
274: }
275: return removed;
276: }
277:
278: private void removeCacheListenerAdaptor(CacheListener cacheListener) {
279: synchronized (cacheEventListeners) {
280: for (Iterator iterator = createThreadSafeIterator(); iterator
281: .hasNext();) {
282: CacheEventListener cacheEventListener = (CacheEventListener) iterator
283: .next();
284: if (cacheEventListener instanceof JCacheListenerAdaptor) {
285: if (((JCacheListenerAdaptor) cacheEventListener)
286: .getCacheListener() == cacheListener) {
287: synchronized (cacheEventListeners) {
288: cacheEventListeners
289: .remove(cacheEventListener);
290: }
291: break;
292: }
293: }
294: }
295: }
296: }
297:
298: /**
299: * Gets a list of the listeners registered to this class
300: *
301: * @return a list of type <code>CacheEventListener</code>
302: */
303: public final Set getCacheEventListeners() {
304: return cacheEventListeners;
305: }
306:
307: /**
308: * Tell listeners to dispose themselves.
309: * Because this method is only ever called from a synchronized cache method, it does not itself need to be
310: * synchronized.
311: */
312: public final void dispose() {
313: Iterator iterator = createThreadSafeIterator();
314: while (iterator.hasNext()) {
315: CacheEventListener cacheEventListener = (CacheEventListener) iterator
316: .next();
317: cacheEventListener.dispose();
318: }
319:
320: synchronized (cacheEventListeners) {
321: cacheEventListeners.clear();
322: }
323: }
324:
325: /**
326: * Returns a string representation of the object. In general, the
327: * <code>toString</code> method returns a string that
328: * "textually represents" this object. The result should
329: * be a concise but informative representation that is easy for a
330: * person to read.
331: *
332: * @return a string representation of the object.
333: */
334: public final String toString() {
335: StringBuffer stringBuffer = new StringBuffer(
336: " cacheEventListeners: ");
337: for (Iterator iterator = createThreadSafeIterator(); iterator
338: .hasNext();) {
339: CacheEventListener cacheEventListener = (CacheEventListener) iterator
340: .next();
341: stringBuffer
342: .append(cacheEventListener.getClass().getName())
343: .append(" ");
344: }
345: return stringBuffer.toString();
346: }
347:
348: /**
349: * Clears all event counters
350: */
351: public void clearCounters() {
352: elementsRemovedCounter = 0;
353: elementsPutCounter = 0;
354: elementsUpdatedCounter = 0;
355: elementsExpiredCounter = 0;
356: elementsEvictedCounter = 0;
357: elementsRemoveAllCounter = 0;
358: }
359:
360: /**
361: * Gets the number of events, irrespective of whether there are any registered listeners.
362: *
363: * @return the number of events since cache creation or last clearing of counters
364: */
365: public long getElementsRemovedCounter() {
366: return elementsRemovedCounter;
367: }
368:
369: /**
370: * Gets the number of events, irrespective of whether there are any registered listeners.
371: *
372: * @return the number of events since cache creation or last clearing of counters
373: */
374: public long getElementsPutCounter() {
375: return elementsPutCounter;
376: }
377:
378: /**
379: * Gets the number of events, irrespective of whether there are any registered listeners.
380: *
381: * @return the number of events since cache creation or last clearing of counters
382: */
383: public long getElementsUpdatedCounter() {
384: return elementsUpdatedCounter;
385: }
386:
387: /**
388: * Gets the number of events, irrespective of whether there are any registered listeners.
389: *
390: * @return the number of events since cache creation or last clearing of counters
391: */
392: public long getElementsExpiredCounter() {
393: return elementsExpiredCounter;
394: }
395:
396: /**
397: * Gets the number of events, irrespective of whether there are any registered listeners.
398: *
399: * @return the number of events since cache creation or last clearing of counters
400: */
401: public long getElementsEvictedCounter() {
402: return elementsEvictedCounter;
403: }
404:
405: /**
406: * Gets the number of events, irrespective of whether there are any registered listeners.
407: *
408: * @return the number of events since cache creation or last clearing of counters
409: */
410: public long getElementsRemoveAllCounter() {
411: return elementsRemoveAllCounter;
412: }
413: }
|