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  
23  import java.io.Serializable;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  
28  /**
29   * Counts listener notifications.
30   * <p/>
31   * The methods also check that we hold the Cache lock.
32   *
33   * @author Greg Luck
34   * @version $Id: CountingCacheEventListener.java 512 2007-07-10 09:18:45Z gregluck $
35   */
36  public class CountingCacheEventListener implements CacheEventListener {
37  
38      private static final List CACHE_ELEMENTS_PUT = Collections.synchronizedList(new ArrayList());
39      private static final List CACHE_ELEMENTS_UPDATED = Collections.synchronizedList(new ArrayList());
40      private static final List CACHE_ELEMENTS_REMOVED = Collections.synchronizedList(new ArrayList());
41      private static final List CACHE_ELEMENTS_EXPIRED = Collections.synchronizedList(new ArrayList());
42      private static final List CACHE_ELEMENTS_EVICTED = Collections.synchronizedList(new ArrayList());
43      private static final List CACHE_REMOVE_ALLS = Collections.synchronizedList(new ArrayList());
44  
45  
46      /**
47       * Accessor
48       */
49      public static List getCacheElementsRemoved(Ehcache cache) {
50          return extractListForGivenCache(CACHE_ELEMENTS_REMOVED, cache);
51      }
52  
53  
54      /**
55       * Accessor
56       */
57      public static List getCacheElementsPut(Ehcache cache) {
58          return extractListForGivenCache(CACHE_ELEMENTS_PUT, cache);
59      }
60  
61      /**
62       * Accessor
63       */
64      public static List getCacheElementsUpdated(Ehcache cache) {
65          return extractListForGivenCache(CACHE_ELEMENTS_UPDATED, cache);
66      }
67  
68      /**
69       * Accessor
70       */
71      public static List getCacheElementsExpired(Ehcache cache) {
72          return extractListForGivenCache(CACHE_ELEMENTS_EXPIRED, cache);
73      }
74  
75      /**
76       * Accessor
77       */
78      public static List getCacheElementsEvicted(Ehcache cache) {
79          return extractListForGivenCache(CACHE_ELEMENTS_EVICTED, cache);
80      }
81  
82      /**
83       * Accessor
84       */
85      public static List getCacheRemoveAlls(Ehcache cache) {
86          return extractListForGivenCache(CACHE_REMOVE_ALLS, cache);
87      }
88  
89  
90  
91      /**
92       * Resets the counters to 0
93       */
94      public static void resetCounters() {
95          synchronized (CACHE_ELEMENTS_REMOVED) {
96              CACHE_ELEMENTS_REMOVED.clear();
97          }
98          synchronized (CACHE_ELEMENTS_PUT) {
99              CACHE_ELEMENTS_PUT.clear();
100         }
101         synchronized (CACHE_ELEMENTS_UPDATED) {
102             CACHE_ELEMENTS_UPDATED.clear();
103         }
104         synchronized (CACHE_ELEMENTS_EXPIRED) {
105             CACHE_ELEMENTS_EXPIRED.clear();
106         }
107         synchronized (CACHE_ELEMENTS_EVICTED) {
108             CACHE_ELEMENTS_EVICTED.clear();
109         }
110         synchronized (CACHE_REMOVE_ALLS) {
111             CACHE_REMOVE_ALLS.clear();
112         }
113     }
114 
115 
116     /**
117      * @param notificationList
118      * @param cache            the cache to filter on. If null, there is not filtering and all entries are returned.
119      * @return a list of notifications for the cache
120      */
121     private static List extractListForGivenCache(List notificationList, Ehcache cache) {
122         ArrayList list = new ArrayList();
123         synchronized (notificationList) {
124             for (int i = 0; i < notificationList.size(); i++) {
125                 CounterEntry counterEntry = (CounterEntry) notificationList.get(i);
126                 if (counterEntry.cache.equals(cache)) {
127                     list.add(counterEntry.getElement());
128                 } else if (cache == null) {
129                     list.add(counterEntry.getElement());
130                 }
131             }
132         }
133         return list;
134     }
135 
136 
137     /**
138      * {@inheritDoc}
139      */
140     public void notifyElementRemoved(final Ehcache cache, final Element element) {
141         checkSynchronizedAccessToCacheOk(cache);
142         CACHE_ELEMENTS_REMOVED.add(new CounterEntry(cache, element));
143     }
144 
145     /**
146      * Called immediately after an element has been put into the cache. The {@link net.sf.ehcache.Cache#put(net.sf.ehcache.Element)} method
147      * will block until this method returns.
148      * <p/>
149      * Implementers may wish to have access to the Element's fields, including value, so the element is provided.
150      * Implementers should be careful not to modify the element. The effect of any modifications is undefined.
151      *
152      * @param cache
153      * @param element the element which was just put into the cache.
154      */
155     public void notifyElementPut(final Ehcache cache, final Element element) {
156         checkSynchronizedAccessToCacheOk(cache);
157         CACHE_ELEMENTS_PUT.add(new CounterEntry(cache, element));
158     }
159 
160 
161     /**
162      * Called immediately after an element has been put into the cache and the element already
163      * existed in the cache. This is thus an update.
164      * <p/>
165      * The {@link net.sf.ehcache.Cache#put(net.sf.ehcache.Element)} method
166      * will block until this method returns.
167      * <p/>
168      * Implementers may wish to have access to the Element's fields, including value, so the element is provided.
169      * Implementers should be careful not to modify the element. The effect of any modifications is undefined.
170      *
171      * @param cache   the cache emitting the notification
172      * @param element the element which was just put into the cache.
173      */
174     public void notifyElementUpdated(final Ehcache cache, final Element element) throws CacheException {
175         CACHE_ELEMENTS_UPDATED.add(new CounterEntry(cache, element));
176     }
177 
178     /**
179      * {@inheritDoc}
180      */
181     public void notifyElementExpired(final Ehcache cache, final Element element) {
182         CACHE_ELEMENTS_EXPIRED.add(new CounterEntry(cache, element));
183     }
184 
185 
186     /**
187      * {@inheritDoc}
188      */
189     public void notifyElementEvicted(final Ehcache cache, final Element element) {
190         CACHE_ELEMENTS_EVICTED.add(new CounterEntry(cache, element));
191     }
192 
193     /**
194      * {@inheritDoc}
195      */
196     public void notifyRemoveAll(final Ehcache cache) {
197         CACHE_REMOVE_ALLS.add(new CounterEntry(cache, null));
198     }
199 
200     /**
201      * Give the replicator a chance to cleanup and free resources when no longer needed
202      * <p/>
203      * Clean up static counters
204      */
205     public void dispose() {
206         resetCounters();
207     }
208 
209     /**
210      * This counter should be called from calls synchonized on Cache. These methods should hold the lock
211      * therefore this is ok.
212      *
213      * @param cache
214      */
215     private void checkSynchronizedAccessToCacheOk(Ehcache cache) {
216         try {
217             cache.get("justasyncrhonizationtest");
218         } catch (CacheException e) {
219             throw new RuntimeException(e);
220         }
221     }
222 
223     /**
224      * A Counter entry
225      */
226     public static class CounterEntry {
227 
228         private Ehcache cache;
229         private Element element;
230 
231         /**
232          * Construct a new event
233          *
234          * @param cache
235          * @param element
236          */
237         public CounterEntry(Ehcache cache, Element element) {
238             this.cache = cache;
239             this.element = element;
240         }
241 
242         /**
243          * @return the cache the event relates to
244          */
245         public Ehcache getCache() {
246             return cache;
247         }
248 
249         /**
250          * @return the payload
251          */
252         public Serializable getElement() {
253             return element;
254         }
255 
256 
257     }
258 
259 
260     /**
261      * Creates a clone of this listener. This method will only be called by ehcache before a cache is initialized.
262      * <p/>
263      * This may not be possible for listeners after they have been initialized. Implementations should throw
264      * CloneNotSupportedException if they do not support clone.
265      * <p/>
266      * This class uses static counters. Clones will share the same counters.
267      * @return a clone
268      * @throws CloneNotSupportedException if the listener could not be cloned.
269      */
270     public Object clone() throws CloneNotSupportedException {
271         return super.clone();
272     }
273 
274 
275 }