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 package net.sf.ehcache.statistics;
17
18 import java.util.List;
19 import java.util.concurrent.CopyOnWriteArrayList;
20 import java.util.concurrent.atomic.AtomicBoolean;
21 import java.util.concurrent.atomic.AtomicInteger;
22 import java.util.concurrent.atomic.AtomicLong;
23
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 import net.sf.ehcache.CacheException;
28 import net.sf.ehcache.Ehcache;
29 import net.sf.ehcache.Element;
30 import net.sf.ehcache.Statistics;
31 import net.sf.ehcache.writer.CacheWriterManager;
32 import net.sf.ehcache.writer.writebehind.WriteBehindManager;
33
34 /**
35  * Implementation which can be used both as a {@link LiveCacheStatistics} and {@link LiveCacheStatisticsData}
36  *
37  * @author <a href="mailto:asanoujam@terracottatech.com">Abhishek Sanoujam</a>
38  * @since 1.7
39  */

40 public class LiveCacheStatisticsImpl implements LiveCacheStatistics, LiveCacheStatisticsData {
41
42     private static final Logger LOG = LoggerFactory.getLogger(LiveCacheStatisticsImpl.class.getName());
43
44     private static final int MIN_MAX_DEFAULT_VALUE = -1;
45     private static final int MILLIS_PER_SECOND = 1000;
46     private static final int NANOS_PER_MILLI = MILLIS_PER_SECOND * MILLIS_PER_SECOND;
47     private static final int HIT_RATIO_MULTIPLIER = 100;
48
49     private final AtomicBoolean statisticsEnabled = new AtomicBoolean(true);
50     private final AtomicLong cacheHitInMemoryCount = new AtomicLong();
51     private final AtomicLong cacheHitOffHeapCount = new AtomicLong();
52     private final AtomicLong cacheHitOnDiskCount = new AtomicLong();
53     private final AtomicLong cacheMissNotFound = new AtomicLong();
54     private final AtomicLong cacheMissInMemoryCount = new AtomicLong();
55     private final AtomicLong cacheMissOffHeapCount = new AtomicLong();
56     private final AtomicLong cacheMissOnDiskCount = new AtomicLong();
57     private final AtomicLong cacheMissExpired = new AtomicLong();
58     private final AtomicLong cacheElementEvictedCount = new AtomicLong();
59     private final AtomicLong totalGetTimeTakenNanos = new AtomicLong();
60     private final AtomicLong cacheElementRemoved = new AtomicLong();
61     private final AtomicLong cacheElementExpired = new AtomicLong();
62     private final AtomicLong cacheElementPut = new AtomicLong();
63     private final AtomicLong cacheElementUpdated = new AtomicLong();
64     private final AtomicInteger statisticsAccuracy = new AtomicInteger();
65     private final AtomicLong minGetTimeNanos = new AtomicLong(MIN_MAX_DEFAULT_VALUE);
66     private final AtomicLong maxGetTimeNanos = new AtomicLong(MIN_MAX_DEFAULT_VALUE);
67     private final AtomicLong xaCommitCount = new AtomicLong();
68     private final AtomicLong xaRollbackCount = new AtomicLong();
69     private final AtomicLong xaRecoveredCount = new AtomicLong();
70
71     private final List<CacheUsageListener> listeners = new CopyOnWriteArrayList<CacheUsageListener>();
72
73     private final Ehcache cache;
74
75     /**
76      * Constructor that accepts the backing {@link Ehcache}, needed for {@link #getSize()}
77      *
78      * @param cache
79      */

80     public LiveCacheStatisticsImpl(Ehcache cache) {
81         this.cache = cache;
82     }
83
84     /**
85      * {@inheritDoc}
86      */

87     public void clearStatistics() {
88         cacheHitInMemoryCount.set(0);
89         cacheHitOffHeapCount.set(0);
90         cacheHitOnDiskCount.set(0);
91         cacheMissExpired.set(0);
92         cacheMissNotFound.set(0);
93         cacheMissInMemoryCount.set(0);
94         cacheMissOffHeapCount.set(0);
95         cacheMissOnDiskCount.set(0);
96         cacheElementEvictedCount.set(0);
97         totalGetTimeTakenNanos.set(0);
98         cacheElementRemoved.set(0);
99         cacheElementExpired.set(0);
100         cacheElementPut.set(0);
101         cacheElementUpdated.set(0);
102         minGetTimeNanos.set(MIN_MAX_DEFAULT_VALUE);
103         maxGetTimeNanos.set(MIN_MAX_DEFAULT_VALUE);
104         xaCommitCount.set(0);
105         xaRollbackCount.set(0);
106         for (CacheUsageListener l : listeners) {
107             l.notifyStatisticsCleared();
108         }
109     }
110
111     /**
112      * {@inheritDoc}
113      */

114     public void xaCommit() {
115         if (!statisticsEnabled.get()) {
116             return;
117         }
118         xaCommitCount.incrementAndGet();
119         for (CacheUsageListener l : listeners) {
120             l.notifyXaCommit();
121         }
122     }
123
124     /**
125      * {@inheritDoc}
126      */

127     public void xaRollback() {
128         if (!statisticsEnabled.get()) {
129             return;
130         }
131         xaRollbackCount.incrementAndGet();
132         for (CacheUsageListener l : listeners) {
133             l.notifyXaRollback();
134         }
135     }
136
137     /**
138      * {@inheritDoc}
139      */

140     public void xaRecovered(int count) {
141         if (!statisticsEnabled.get()) {
142             return;
143         }
144         xaRecoveredCount.addAndGet(count);
145     }
146
147     /**
148      * {@inheritDoc}
149      */

150     public boolean isStatisticsEnabled() {
151         return statisticsEnabled.get();
152     }
153
154     /**
155      * {@inheritDoc}
156      */

157     public void setStatisticsEnabled(boolean enableStatistics) {
158         if (enableStatistics) {
159             clearStatistics();
160         }
161         statisticsEnabled.set(enableStatistics);
162         for (CacheUsageListener l : listeners) {
163             l.notifyStatisticsEnabledChanged(enableStatistics);
164         }
165     }
166
167     /**
168      * {@inheritDoc}
169      */

170     public void addGetTimeNanos(long nanos) {
171         if (!statisticsEnabled.get()) {
172             return;
173         }
174         totalGetTimeTakenNanos.addAndGet(nanos);
175         for (CacheUsageListener l : listeners) {
176             l.notifyGetTimeNanos(nanos);
177             l.notifyTimeTakenForGet(nanos / NANOS_PER_MILLI);
178         }
179         if (minGetTimeNanos.get() == MIN_MAX_DEFAULT_VALUE || (nanos < minGetTimeNanos.get())) {
180             minGetTimeNanos.set(nanos);
181         }
182         if (maxGetTimeNanos.get() == MIN_MAX_DEFAULT_VALUE || (nanos > maxGetTimeNanos.get() && nanos > 0)) {
183             maxGetTimeNanos.set(nanos);
184         }
185     }
186
187     /**
188      * {@inheritDoc}
189      */

190     public void addGetTimeMillis(long millis) {
191         if (!statisticsEnabled.get()) {
192             return;
193         }
194         totalGetTimeTakenNanos.addAndGet(millis);
195         for (CacheUsageListener l : listeners) {
196             l.notifyTimeTakenForGet(millis);
197         }
198         if (minGetTimeNanos.get() == MIN_MAX_DEFAULT_VALUE || (millis < minGetTimeNanos.get() /*&& millis > 0*/)) {
199             minGetTimeNanos.set(millis);
200         }
201         if (maxGetTimeNanos.get() == MIN_MAX_DEFAULT_VALUE || (millis > maxGetTimeNanos.get() && millis > 0)) {
202             maxGetTimeNanos.set(millis);
203         }
204     }
205
206     /**
207      * {@inheritDoc}
208      */

209     public void cacheHitInMemory() {
210         if (!statisticsEnabled.get()) {
211             return;
212         }
213         cacheHitInMemoryCount.incrementAndGet();
214         for (CacheUsageListener l : listeners) {
215             l.notifyCacheHitInMemory();
216         }
217     }
218
219     /**
220      * {@inheritDoc}
221      */

222     public void cacheHitOffHeap() {
223         if (!statisticsEnabled.get()) {
224             return;
225         }
226         cacheHitOffHeapCount.incrementAndGet();
227         for (CacheUsageListener l : listeners) {
228             l.notifyCacheHitOffHeap();
229         }
230     }
231
232     /**
233      * {@inheritDoc}
234      */

235     public void cacheHitOnDisk() {
236         if (!statisticsEnabled.get()) {
237             return;
238         }
239         cacheHitOnDiskCount.incrementAndGet();
240         for (CacheUsageListener l : listeners) {
241             l.notifyCacheHitOnDisk();
242         }
243     }
244
245     /**
246      * {@inheritDoc}
247      */

248     public void cacheMissExpired() {
249         if (!statisticsEnabled.get()) {
250             return;
251         }
252         cacheMissExpired.incrementAndGet();
253         for (CacheUsageListener l : listeners) {
254             l.notifyCacheMissedWithExpired();
255         }
256     }
257
258     /**
259      * {@inheritDoc}
260      */

261     public void cacheMissNotFound() {
262         if (!statisticsEnabled.get()) {
263             return;
264         }
265         cacheMissNotFound.incrementAndGet();
266         for (CacheUsageListener l : listeners) {
267             l.notifyCacheMissedWithNotFound();
268         }
269     }
270
271     /**
272      * {@inheritDoc}
273      */

274     public void cacheMissInMemory() {
275         if (!statisticsEnabled.get()) {
276             return;
277         }
278         cacheMissInMemoryCount.incrementAndGet();
279         for (CacheUsageListener l : listeners) {
280             l.notifyCacheMissInMemory();
281         }
282     }
283
284     /**
285      * {@inheritDoc}
286      */

287     public void cacheMissOffHeap() {
288         if (!statisticsEnabled.get()) {
289             return;
290         }
291         cacheMissOffHeapCount.incrementAndGet();
292         for (CacheUsageListener l : listeners) {
293             l.notifyCacheMissOffHeap();
294         }
295     }
296
297     /**
298      * {@inheritDoc}
299      */

300     public void cacheMissOnDisk() {
301         if (!statisticsEnabled.get()) {
302             return;
303         }
304         cacheMissOnDiskCount.incrementAndGet();
305         for (CacheUsageListener l : listeners) {
306             l.notifyCacheMissOnDisk();
307         }
308     }
309
310     /**
311      * {@inheritDoc}
312      */

313     public void setStatisticsAccuracy(int statisticsAccuracy) {
314         if (!Statistics.isValidStatisticsAccuracy(statisticsAccuracy)) {
315             throw new IllegalArgumentException("Invalid statistics accuracy value: " + statisticsAccuracy);
316         }
317         this.statisticsAccuracy.set(statisticsAccuracy);
318         for (CacheUsageListener l : listeners) {
319             l.notifyStatisticsAccuracyChanged(statisticsAccuracy);
320         }
321     }
322
323     /**
324      * {@inheritDoc}
325      */

326     public void dispose() {
327         for (CacheUsageListener l : listeners) {
328             l.dispose();
329         }
330     }
331
332     /**
333      * {@inheritDoc}
334      */

335     public void notifyElementEvicted(Ehcache cache, Element element) {
336         if (!statisticsEnabled.get()) {
337             return;
338         }
339         cacheElementEvictedCount.incrementAndGet();
340         for (CacheUsageListener l : listeners) {
341             l.notifyCacheElementEvicted();
342         }
343     }
344
345     /**
346      * {@inheritDoc}
347      */

348     public void notifyElementExpired(Ehcache cache, Element element) {
349         if (!statisticsEnabled.get()) {
350             return;
351         }
352         cacheElementExpired.incrementAndGet();
353         for (CacheUsageListener l : listeners) {
354             l.notifyCacheElementExpired();
355         }
356     }
357
358     /**
359      * {@inheritDoc}
360      */

361     public void notifyElementPut(Ehcache cache, Element element) throws CacheException {
362         if (!statisticsEnabled.get()) {
363             return;
364         }
365         cacheElementPut.incrementAndGet();
366         for (CacheUsageListener l : listeners) {
367             l.notifyCacheElementPut();
368         }
369     }
370
371     /**
372      * {@inheritDoc}
373      */

374     public void notifyElementRemoved(Ehcache cache, Element element) throws CacheException {
375         if (!statisticsEnabled.get()) {
376             return;
377         }
378         cacheElementRemoved.incrementAndGet();
379         for (CacheUsageListener l : listeners) {
380             l.notifyCacheElementRemoved();
381         }
382     }
383
384     /**
385      * {@inheritDoc}
386      */

387     public void notifyElementUpdated(Ehcache cache, Element element) throws CacheException {
388         if (!statisticsEnabled.get()) {
389             return;
390         }
391         cacheElementUpdated.incrementAndGet();
392         for (CacheUsageListener l : listeners) {
393             l.notifyCacheElementUpdated();
394         }
395     }
396
397     /**
398      * {@inheritDoc}
399      */

400     public void notifyRemoveAll(Ehcache cache) {
401         if (!statisticsEnabled.get()) {
402             return;
403         }
404         for (CacheUsageListener l : listeners) {
405             l.notifyRemoveAll();
406         }
407     }
408
409     /**
410      * {@inheritDoc}
411      */

412     @Override
413     public Object clone() throws CloneNotSupportedException {
414         // to shut up check-style, why do we need this ?
415         super.clone();
416         throw new CloneNotSupportedException();
417     }
418
419     /**
420      * {@inheritDoc}
421      */

422     public float getAverageGetTimeMillis() {
423         return getAverageGetTimeNanos() / (float) NANOS_PER_MILLI;
424     }
425
426     /**
427      * {@inheritDoc}
428      */

429     public long getAverageGetTimeNanos() {
430         long accessCount = getCacheHitCount() + getCacheMissCount();
431         if (accessCount == 0) {
432             return 0;
433         }
434         return totalGetTimeTakenNanos.get() / accessCount;
435     }
436
437     /**
438      * {@inheritDoc}
439      */

440     public void registerCacheUsageListener(CacheUsageListener cacheUsageListener) throws IllegalStateException {
441         if (!isStatisticsEnabled()) {
442             LOG.warn("Registering a CacheUsageListener on {} whose statistics are currently disabled.  "
443                     + "No events will be fired until statistics are enabled.", cache.getName());
444         }
445         listeners.add(cacheUsageListener);
446     }
447
448     /**
449      * {@inheritDoc}
450      */

451     public void removeCacheUsageListener(CacheUsageListener cacheUsageListener) throws IllegalStateException {
452         listeners.remove(cacheUsageListener);
453     }
454
455     /**
456      * {@inheritDoc}
457      */

458     public long getCacheHitCount() {
459         return cacheHitInMemoryCount.get() + cacheHitOffHeapCount.get() + cacheHitOnDiskCount.get();
460     }
461
462     /**
463      * {@inheritDoc}
464      */

465     public long getCacheMissCount() {
466         return cacheMissNotFound.get() + cacheMissExpired.get();
467     }
468
469     /**
470      * {@inheritDoc}
471      */

472     public long getInMemoryMissCount() {
473         return cacheMissInMemoryCount.get();
474     }
475
476     /**
477      * {@inheritDoc}
478      */

479     public long getOffHeapMissCount() {
480         return cacheMissOffHeapCount.get();
481     }
482
483     /**
484      * {@inheritDoc}
485      */

486     public long getOnDiskMissCount() {
487         return cacheMissOnDiskCount.get();
488     }
489
490
491     /**
492      * {@inheritDoc}
493      */

494     public long getCacheMissCountExpired() {
495         return cacheMissExpired.get();
496     }
497
498     /**
499      * {@inheritDoc}
500      */

501     public int getCacheHitRatio() {
502         long hits = getCacheHitCount();
503         long accesses = hits + getCacheMissCount();
504
505         return (int) (accesses == 0 ? 0 : ((hits / (double) accesses) * HIT_RATIO_MULTIPLIER));
506     }
507
508     /**
509      * {@inheritDoc}
510      */

511     public long getEvictedCount() {
512         return cacheElementEvictedCount.get();
513     }
514
515     /**
516      * {@inheritDoc}
517      */

518     public long getInMemoryHitCount() {
519         return cacheHitInMemoryCount.get();
520     }
521
522     /**
523      * {@inheritDoc}
524      */

525     public long getOffHeapHitCount() {
526         return cacheHitOffHeapCount.get();
527     }
528
529     /**
530      * {@inheritDoc}
531      */

532     public long getOnDiskHitCount() {
533         return cacheHitOnDiskCount.get();
534     }
535
536     /**
537      * {@inheritDoc}
538      */

539     public long getSize() {
540         if (!statisticsEnabled.get()) {
541             return 0;
542         }
543         return cache.getSizeBasedOnAccuracy(statisticsAccuracy.get());
544     }
545
546     /**
547      * {@inheritDoc}
548      * @deprecated see {@link #getLocalHeapSize()}
549      */

550     @Deprecated
551     public long getInMemorySize() {
552         return getLocalHeapSize();
553     }
554
555     /**
556      * {@inheritDoc}
557      * @deprecated see {@link #getLocalOffHeapSize()}
558      */

559     @Deprecated
560     public long getOffHeapSize() {
561         return getLocalOffHeapSize();
562     }
563
564     /**
565      * {@inheritDoc}
566      * @deprecated see {@link #getLocalDiskSize()}
567      */

568     @Deprecated
569     public long getOnDiskSize() {
570         return getLocalDiskSize();
571     }
572
573     /**
574      * {@inheritDoc}
575      */

576     public long getLocalHeapSize() {
577         if (!statisticsEnabled.get()) {
578             return 0;
579         }
580         return cache.getMemoryStoreSize();
581     }
582
583     /**
584      * {@inheritDoc}
585      */

586     public long getLocalOffHeapSize() {
587         if (!statisticsEnabled.get()) {
588             return 0;
589         }
590         return cache.getOffHeapStoreSize();
591     }
592
593     /**
594      * {@inheritDoc}
595      */

596     public long getLocalDiskSize() {
597         if (!statisticsEnabled.get()) {
598             return 0;
599         }
600         return cache.getDiskStoreSize();
601     }
602
603     /**
604      * {@inheritDoc}
605      */

606     public long getLocalDiskSizeInBytes() {
607         if (!statisticsEnabled.get()) {
608             return 0;
609         }
610         return cache.calculateOnDiskSize();
611     }
612
613     /**
614      * {@inheritDoc}
615      */

616     public long getLocalHeapSizeInBytes() {
617         if (!statisticsEnabled.get()) {
618             return 0;
619         }
620         return cache.calculateInMemorySize();
621     }
622
623     /**
624      * {@inheritDoc}
625      */

626     public long getLocalOffHeapSizeInBytes() {
627         if (!statisticsEnabled.get()) {
628             return 0;
629         }
630         return cache.calculateOffHeapSize();
631     }
632
633
634     /**
635      * {@inheritDoc}
636      */

637     public String getCacheName() {
638         return cache.getName();
639     }
640
641     /**
642      * {@inheritDoc}
643      */

644     public int getStatisticsAccuracy() {
645         return statisticsAccuracy.get();
646     }
647
648     /**
649      * {@inheritDoc}
650      */

651     public long getExpiredCount() {
652         return cacheElementExpired.get();
653     }
654
655     /**
656      * {@inheritDoc}
657      */

658     public long getPutCount() {
659         return cacheElementPut.get();
660     }
661
662     /**
663      * {@inheritDoc}
664      */

665     public long getRemovedCount() {
666         return cacheElementRemoved.get();
667     }
668
669     /**
670      * {@inheritDoc}
671      */

672     public long getUpdateCount() {
673         return cacheElementUpdated.get();
674     }
675
676     /**
677      * {@inheritDoc}
678      */

679     public String getStatisticsAccuracyDescription() {
680         int value = statisticsAccuracy.get();
681         if (value == 0) {
682             return "None";
683         } else if (value == 1) {
684             return "Best Effort";
685         } else {
686             return "Guaranteed";
687         }
688     }
689
690     /**
691      * {@inheritDoc}
692      *
693      * @see net.sf.ehcache.statistics.LiveCacheStatistics#getMaxGetTimeMillis()
694      */

695     public long getMaxGetTimeMillis() {
696         return maxGetTimeNanos.get() / NANOS_PER_MILLI;
697     }
698
699     /**
700      * {@inheritDoc}
701      *
702      * @see net.sf.ehcache.statistics.LiveCacheStatistics#getMinGetTimeMillis()
703      */

704     public long getMinGetTimeMillis() {
705         return minGetTimeNanos.get() / NANOS_PER_MILLI;
706     }
707
708     /**
709      * {@inheritDoc}
710      *
711      * @see net.sf.ehcache.statistics.LiveCacheStatistics#getMaxGetTimeNanos()
712      */

713     public long getMaxGetTimeNanos() {
714         return maxGetTimeNanos.get();
715     }
716
717     /**
718      * {@inheritDoc}
719      *
720      * @see net.sf.ehcache.statistics.LiveCacheStatistics#getMinGetTimeNanos()
721      */

722     public long getMinGetTimeNanos() {
723         return minGetTimeNanos.get();
724     }
725
726     /**
727      * {@inheritDoc}
728      *
729      * @see net.sf.ehcache.statistics.LiveCacheStatistics#getXaCommitCount()
730      */

731     public long getXaCommitCount() {
732         return xaCommitCount.get();
733     }
734
735     /**
736      * {@inheritDoc}
737      *
738      * @see net.sf.ehcache.statistics.LiveCacheStatistics#getXaRollbackCount()
739      */

740     public long getXaRollbackCount() {
741         return xaRollbackCount.get();
742     }
743
744     /**
745      * {@inheritDoc}
746      *
747      * @see net.sf.ehcache.statistics.LiveCacheStatistics#getXaRecoveredCount()
748      */

749     public long getXaRecoveredCount() {
750         return xaRecoveredCount.get();
751     }
752
753     /**
754      * {@inheritDoc}
755      */

756     public long getWriterQueueLength() {
757         CacheWriterManager writerManager = cache.getWriterManager();
758         if (writerManager instanceof WriteBehindManager) {
759             return ((WriteBehindManager)writerManager).getQueueSize();
760         }
761         return 0;
762     }
763 }
764