1 /**
2  *  Copyright Terracotta, Inc.
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.management;
18
19 import net.sf.ehcache.CacheException;
20 import net.sf.ehcache.Ehcache;
21 import net.sf.ehcache.Statistics;
22 import net.sf.ehcache.hibernate.management.impl.EhcacheHibernateMbeanNames;
23
24 import javax.management.MalformedObjectNameException;
25 import javax.management.ObjectName;
26
27 import java.io.Serializable;
28 import java.util.concurrent.TimeUnit;
29
30
31 /**
32  * A JMX CacheStatistics decorator for an ehcache Statistics class.
33  * <p/>
34  * An immutable Cache statistics implementation}
35  * <p/>
36  * This is like a value object, with the added ability to clear cache statistics on the cache.
37  * That ability does not survive any Serialization of this class. On deserialization the cache
38  * can be considered disconnected.
39  * <p/>
40  * The accuracy of these statistics are determined by the value of {#getStatisticsAccuracy()}
41  * at the time the statistic was computed. This can be changed by setting {@link net.sf.ehcache.Cache#setStatisticsAccuracy}.
42  * <p/>
43  * Because this class maintains a reference to an Ehcache, any references held to this class will precent the Ehcache
44  * from getting garbage collected.
45  *
46  * @author Greg Luck
47  * @version $Id: CacheStatistics.java 5631 2012-05-10 08:31:33Z teck $
48  * @since 1.3
49  */

50 public class CacheStatistics implements CacheStatisticsMBean, Serializable {
51
52     private static final long serialVersionUID = 8085302752781762030L;
53
54     private final transient Ehcache ehcache;
55     private final ObjectName objectName;
56     private final long ttlInMillis;
57     
58     private Statistics statistics;
59     private long lastUpdated;
60
61     /**
62      * Constructs an object from an ehcache statistics object
63      *
64      * @param ehcache the backing ehcache
65      * @param ttl statistics ttl value
66      * @param unit statistics ttl unit
67      */

68     public CacheStatistics(Ehcache ehcache, long ttl, TimeUnit unit) {
69         this.ttlInMillis = unit.toMillis(ttl);
70         this.ehcache = ehcache;
71         this.objectName = createObjectName(ehcache.getCacheManager().getName(), ehcache.getName());
72     }
73
74     /**
75      * Constructs an object from an ehcache statistics object using the default statistics ttl.
76      *
77      * @param ehcache the backing ehcache
78      */

79     public CacheStatistics(Ehcache ehcache) {
80         this(ehcache, 0, TimeUnit.MILLISECONDS);
81     }
82
83     /**
84      * Creates an object name using the scheme "net.sf.ehcache:type=CacheStatistics,CacheManager=<cacheManagerName>,name=<cacheName>"
85      */

86     static ObjectName createObjectName(String cacheManagerName, String cacheName) {
87         ObjectName objectName;
88         try {
89             objectName = new ObjectName("net.sf.ehcache:type=CacheStatistics,CacheManager=" + cacheManagerName + ",name="
90                     + EhcacheHibernateMbeanNames.mbeanSafe(cacheName));
91         } catch (MalformedObjectNameException e) {
92             throw new CacheException(e);
93         }
94         return objectName;
95     }
96
97     /**
98      * Accurately measuring statistics can be expensive. Returns the current accuracy setting used
99      * in the creation of these statistics.
100      *
101      * @return one of {@link Statistics#STATISTICS_ACCURACY_BEST_EFFORT}, {@link Statistics#STATISTICS_ACCURACY_GUARANTEED}, {@link Statistics#STATISTICS_ACCURACY_NONE}
102      */

103     public int getStatisticsAccuracy() {
104         updateIfNeeded();
105         return statistics.getStatisticsAccuracy();
106     }
107
108     private void updateIfNeeded() {
109         long statsAge = System.currentTimeMillis() - lastUpdated;
110         if (statsAge < 0 || statsAge > ttlInMillis) {
111             statistics = ehcache.getStatistics();
112             lastUpdated = System.currentTimeMillis();
113         }
114     }
115
116     /**
117      * Accurately measuring statistics can be expensive. Returns the current accuracy setting.
118      *
119      * @return a human readable description of the accuracy setting. One of "None""Best Effort" or "Guaranteed".
120      */

121     public String getStatisticsAccuracyDescription() {
122         updateIfNeeded();
123         return statistics.getStatisticsAccuracyDescription();
124     }
125
126     /**
127      * @return the name of the Ehcache, or null is there no associated cache
128      */

129     public String getAssociatedCacheName() {
130         if (statistics == null) {
131             return null;
132         } else {
133             return statistics.getAssociatedCacheName();
134         }
135     }
136
137     /**
138      * Clears the statistic counters to 0 for the associated Cache.
139      */

140     public void clearStatistics() {
141         statistics.clearStatistics();
142     }
143
144     /**
145      * The number of times a requested item was found in the cache.
146      * <p/>
147      * Warning. This statistic is recorded as a long. If the statistic is large than Integer#MAX_VALUE
148      * precision will be lost.
149      *
150      * @return the number of times a requested item was found in the cache
151      */

152     public long getCacheHits() {
153         updateIfNeeded();
154         return statistics.getCacheHits();
155     }
156
157     /**
158      * Number of times a requested item was found in the Memory Store.
159      *
160      * @return the number of times a requested item was found in memory
161      */

162     public long getInMemoryHits() {
163         updateIfNeeded();
164         return statistics.getInMemoryHits();
165     }
166
167     /**
168      * Number of times a requested item was found in the off-heap store.
169      *
170      * @return the number of times a requested item was found in off-heap
171      */

172     public long getOffHeapHits() {
173         updateIfNeeded();
174         return statistics.getOffHeapHits();
175     }
176
177     /**
178      * Number of times a requested item was found in the Disk Store.
179      *
180      * @return the number of times a requested item was found on Disk, or 0 if there is no disk storage configured.
181      */

182     public long getOnDiskHits() {
183         updateIfNeeded();
184         return statistics.getOnDiskHits();
185     }
186
187     /**
188      * Warning. This statistic is recorded as a long. If the statistic is large than Integer#MAX_VALUE
189      * precision will be lost.
190      *
191      * @return the number of times a requested element was not found in the cache
192      */

193     public long getCacheMisses() {
194         updateIfNeeded();
195         return statistics.getCacheMisses();
196     }
197
198     /** {@inheritDoc} */
199     public long getInMemoryMisses() {
200         updateIfNeeded();
201         return statistics.getInMemoryMisses();
202     }
203
204     /** {@inheritDoc} */
205     public long getOffHeapMisses() {
206         updateIfNeeded();
207         return statistics.getOffHeapMisses();
208     }
209
210     /** {@inheritDoc} */
211     public long getOnDiskMisses() {
212         updateIfNeeded();
213         return statistics.getOnDiskMisses();
214     }
215
216     /**
217      * Gets the number of elements stored in the cache. Caclulating this can be expensive. Accordingly,
218      * this method will return three different values, depending on the statistics accuracy setting.
219      * <h3>Best Effort Size</h3>
220      * This result is returned when the statistics accuracy setting is {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_BEST_EFFORT}.
221      * <p/>
222      * The size is the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.MemoryStore} plus
223      * the number of {@link net.sf.ehcache.Element}s in the {@link net.sf.ehcache.store.disk.DiskStore}.
224      * <p/>
225      * This number is the actual number of elements, including expired elements that have
226      * not been removed. Any duplicates between stores are accounted for.
227      * <p/>
228      * Expired elements are removed from the the memory store when
229      * getting an expired element, or when attempting to spool an expired element to
230      * disk.
231      * <p/>
232      * Expired elements are removed from the disk store when getting an expired element,
233      * or when the expiry thread runs, which is once every five minutes.
234      * <p/>
235      * <h3>Guaranteed Accuracy Size</h3>
236      * This result is returned when the statistics accuracy setting is {@link net.sf.ehcache.Statistics#STATISTICS_ACCURACY_GUARANTEED}.
237      * <p/>
238      * This method accounts for elements which might be expired or duplicated between stores. It take approximately
239      * 200ms per 1000 elements to execute.
240      * <h3>Fast but non-accurate Size</h3>
241      * This result is returned when the statistics accuracy setting is {@link Statistics#STATISTICS_ACCURACY_NONE}.
242      * <p/>
243      * The number given may contain expired elements. In addition if the DiskStore is used it may contain some double
244      * counting of elements. It takes 6ms for 1000 elements to execute. Time to execute is O(log n). 50,000 elements take
245      * 36ms.
246      *
247      * @return the number of elements in the ehcache, with a varying degree of accuracy, depending on accuracy setting.
248      */

249     public long getObjectCount() {
250         updateIfNeeded();
251         return statistics.getObjectCount();
252     }
253
254     /**
255      * Gets the size of the write-behind queue, if any.
256      * The value is for all local buckets
257      * @return Elements waiting to be processed by the write behind writer. -1 if no write-behind
258      */

259     public long getWriterQueueLength() {
260         updateIfNeeded();
261         return statistics.getWriterQueueSize();
262     }
263
264     /**
265      * {@inheritDoc}
266      */

267     public int getWriterMaxQueueSize() {
268         return ehcache.getCacheConfiguration().getCacheWriterConfiguration().getWriteBehindMaxQueueSize();
269     }
270
271     /**
272      * Gets the number of objects in the MemoryStore
273      * @return the MemoryStore size which is always a count unadjusted for duplicates or expiries
274      */

275     public long getMemoryStoreObjectCount() {
276         updateIfNeeded();
277         return statistics.getMemoryStoreObjectCount();
278     }
279
280     /**
281      * {@inheritDoc}
282      */

283     public long getOffHeapStoreObjectCount() {
284         updateIfNeeded();
285         return statistics.getOffHeapStoreObjectCount();
286     }
287
288     /**
289      * Gets the number of objects in the DiskStore
290      * @return the DiskStore size which is always a count unadjusted for duplicates or expiries
291      */

292     public long getDiskStoreObjectCount() {
293         updateIfNeeded();
294         return statistics.getDiskStoreObjectCount();
295     }
296
297
298     /**
299      * @return the object name for this MBean
300      */

301     ObjectName getObjectName() {
302         return objectName;
303     }
304
305     /**
306      * Return the backing cache.
307      * @return the backing cache, if one is connected. On Serialization
308      * the transient Ehcache reference is dropped.
309      */

310     public Ehcache getEhcache() {
311         return ehcache;
312     }
313
314     private static double getPercentage(long number, long total) {
315         if (total == 0) {
316             return 0.0;
317         } else {
318             return number / (double)total;
319         }
320     }
321
322     /**
323      * {@inheritDoc}
324      */

325     public double getCacheHitPercentage() {
326         updateIfNeeded();
327         long hits = statistics.getCacheHits();
328         long misses = statistics.getCacheMisses();
329
330         long total = hits + misses;
331         return getPercentage(hits, total);
332     }
333
334     /**
335      * {@inheritDoc}
336      */

337     public double getCacheMissPercentage() {
338         updateIfNeeded();
339         long hits = statistics.getCacheHits();
340         long misses = statistics.getCacheMisses();
341
342         long total = hits + misses;
343         return getPercentage(misses, total);
344     }
345
346     /**
347      * {@inheritDoc}
348      */

349     public double getInMemoryHitPercentage() {
350         updateIfNeeded();
351         long hits = statistics.getInMemoryHits();
352         long misses = statistics.getInMemoryMisses();
353
354         long total = hits + misses;
355         return getPercentage(hits, total);
356     }
357
358     /**
359      * {@inheritDoc}
360      */

361     public double getOffHeapHitPercentage() {
362         updateIfNeeded();
363         long hits = statistics.getOffHeapHits();
364         long misses = statistics.getOffHeapMisses();
365
366         long total = hits + misses;
367         return getPercentage(hits, total);
368     }
369
370     /**
371      * {@inheritDoc}
372      */

373     public double getOnDiskHitPercentage() {
374         updateIfNeeded();
375         long hits = statistics.getOnDiskHits();
376         long misses = statistics.getOnDiskMisses();
377
378         long total = hits + misses;
379         return getPercentage(hits, total);
380     }
381 }
382