1 package org.apache.commons.jcs3.engine.control;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.ListIterator;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Objects;
32 import java.util.Set;
33 import java.util.concurrent.CopyOnWriteArrayList;
34 import java.util.concurrent.ScheduledExecutorService;
35 import java.util.concurrent.ScheduledFuture;
36 import java.util.concurrent.TimeUnit;
37 import java.util.concurrent.atomic.AtomicBoolean;
38 import java.util.concurrent.atomic.AtomicLong;
39 import java.util.stream.Collectors;
40 import java.util.stream.Stream;
41
42 import org.apache.commons.jcs3.access.exception.CacheException;
43 import org.apache.commons.jcs3.access.exception.ObjectNotFoundException;
44 import org.apache.commons.jcs3.auxiliary.AuxiliaryCache;
45 import org.apache.commons.jcs3.engine.CacheStatus;
46 import org.apache.commons.jcs3.engine.behavior.ICache;
47 import org.apache.commons.jcs3.engine.behavior.ICacheElement;
48 import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes;
49 import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes.DiskUsagePattern;
50 import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
51 import org.apache.commons.jcs3.engine.behavior.IRequireScheduler;
52 import org.apache.commons.jcs3.engine.control.event.ElementEvent;
53 import org.apache.commons.jcs3.engine.control.event.behavior.ElementEventType;
54 import org.apache.commons.jcs3.engine.control.event.behavior.IElementEvent;
55 import org.apache.commons.jcs3.engine.control.event.behavior.IElementEventHandler;
56 import org.apache.commons.jcs3.engine.control.event.behavior.IElementEventQueue;
57 import org.apache.commons.jcs3.engine.control.group.GroupId;
58 import org.apache.commons.jcs3.engine.match.KeyMatcherPatternImpl;
59 import org.apache.commons.jcs3.engine.match.behavior.IKeyMatcher;
60 import org.apache.commons.jcs3.engine.memory.behavior.IMemoryCache;
61 import org.apache.commons.jcs3.engine.memory.lru.LRUMemoryCache;
62 import org.apache.commons.jcs3.engine.memory.shrinking.ShrinkerThread;
63 import org.apache.commons.jcs3.engine.stats.CacheStats;
64 import org.apache.commons.jcs3.engine.stats.StatElement;
65 import org.apache.commons.jcs3.engine.stats.behavior.ICacheStats;
66 import org.apache.commons.jcs3.engine.stats.behavior.IStats;
67 import org.apache.commons.jcs3.log.Log;
68 import org.apache.commons.jcs3.log.LogManager;
69
70
71
72
73
74
75
76 public class CompositeCache<K, V>
77 implements ICache<K, V>, IRequireScheduler
78 {
79
80 private static final Log log = LogManager.getLog(CompositeCache.class);
81
82
83
84
85
86 private IElementEventQueue elementEventQ;
87
88
89 private CopyOnWriteArrayList<AuxiliaryCache<K, V>> auxCaches = new CopyOnWriteArrayList<>();
90
91
92 private final AtomicBoolean alive;
93
94
95 private IElementAttributes attr;
96
97
98 private ICompositeCacheAttributes cacheAttr;
99
100
101 private final AtomicLong updateCount;
102
103
104 private final AtomicLong removeCount;
105
106
107 private final AtomicLong hitCountRam;
108
109
110 private final AtomicLong hitCountAux;
111
112
113 private final AtomicLong missCountNotFound;
114
115
116 private final AtomicLong missCountExpired;
117
118
119 private CompositeCacheManager cacheManager;
120
121
122
123
124
125 private IMemoryCache<K, V> memCache;
126
127
128 private IKeyMatcher<K> keyMatcher = new KeyMatcherPatternImpl<>();
129
130 private ScheduledFuture<?> future;
131
132
133
134
135
136
137
138 public CompositeCache(final ICompositeCacheAttributes cattr, final IElementAttributes attr)
139 {
140 this.attr = attr;
141 this.cacheAttr = cattr;
142 this.alive = new AtomicBoolean(true);
143 this.updateCount = new AtomicLong();
144 this.removeCount = new AtomicLong();
145 this.hitCountRam = new AtomicLong();
146 this.hitCountAux = new AtomicLong();
147 this.missCountNotFound = new AtomicLong();
148 this.missCountExpired = new AtomicLong();
149
150 createMemoryCache(cattr);
151
152 log.info("Constructed cache with name [{0}] and cache attributes {1}",
153 cacheAttr.getCacheName(), cattr);
154 }
155
156
157
158
159
160
161 public void setElementEventQueue(final IElementEventQueue queue)
162 {
163 this.elementEventQ = queue;
164 }
165
166
167
168
169
170
171 public void setCompositeCacheManager(final CompositeCacheManager manager)
172 {
173 this.cacheManager = manager;
174 }
175
176
177
178
179 @Override
180 public void setScheduledExecutorService(final ScheduledExecutorService scheduledExecutor)
181 {
182 if (cacheAttr.isUseMemoryShrinker())
183 {
184 future = scheduledExecutor.scheduleAtFixedRate(
185 new ShrinkerThread<>(this), 0, cacheAttr.getShrinkerIntervalSeconds(),
186 TimeUnit.SECONDS);
187 }
188
189 if (memCache instanceof IRequireScheduler)
190 {
191 ((IRequireScheduler) memCache).setScheduledExecutorService(scheduledExecutor);
192 }
193 }
194
195
196
197
198
199
200
201
202 public void setAuxCaches(final List<AuxiliaryCache<K, V>> auxCaches)
203 {
204 this.auxCaches = auxCaches.stream()
205 .filter(Objects::nonNull)
206 .collect(Collectors.toCollection(CopyOnWriteArrayList::new));
207 }
208
209
210
211
212
213
214
215 @Deprecated
216 public void setAuxCaches(final AuxiliaryCache<K, V>[] auxCaches)
217 {
218 setAuxCaches(Arrays.asList(auxCaches));
219 }
220
221
222
223
224
225
226
227 public List<AuxiliaryCache<K, V>> getAuxCacheList()
228 {
229 return this.auxCaches;
230 }
231
232
233
234
235
236
237
238 @SuppressWarnings("unchecked")
239 @Deprecated
240 public AuxiliaryCache<K, V>[] getAuxCaches()
241 {
242 return getAuxCacheList().toArray(new AuxiliaryCache[0]);
243 }
244
245
246
247
248
249
250
251 @Override
252 public void update(final ICacheElement<K, V> ce)
253 throws IOException
254 {
255 update(ce, false);
256 }
257
258
259
260
261
262
263
264 public void localUpdate(final ICacheElement<K, V> ce)
265 throws IOException
266 {
267 update(ce, true);
268 }
269
270
271
272
273
274
275
276
277
278 protected void update(final ICacheElement<K, V> cacheElement, final boolean localOnly)
279 throws IOException
280 {
281
282 if (cacheElement.getKey() instanceof String
283 && cacheElement.getKey().toString().endsWith(NAME_COMPONENT_DELIMITER))
284 {
285 throw new IllegalArgumentException("key must not end with " + NAME_COMPONENT_DELIMITER
286 + " for a put operation");
287 }
288 if (cacheElement.getKey() instanceof GroupId)
289 {
290 throw new IllegalArgumentException("key cannot be a GroupId " + " for a put operation");
291 }
292
293 log.debug("Updating memory cache {0}", cacheElement::getKey);
294
295 updateCount.incrementAndGet();
296 memCache.update(cacheElement);
297 updateAuxiliaries(cacheElement, localOnly);
298
299 cacheElement.getElementAttributes().setLastAccessTimeNow();
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319 protected void updateAuxiliaries(final ICacheElement<K, V> cacheElement, final boolean localOnly)
320 throws IOException
321 {
322
323
324
325
326
327 if (!auxCaches.isEmpty())
328 {
329 log.debug("Updating auxiliary caches");
330 }
331 else
332 {
333 log.debug("No auxiliary cache to update");
334 }
335
336 for (final ICache<K, V> aux : auxCaches)
337 {
338 if (aux == null)
339 {
340 continue;
341 }
342
343 log.debug("Auxiliary cache type: {0}", aux.getCacheType());
344
345 switch (aux.getCacheType())
346 {
347
348 case REMOTE_CACHE:
349 log.debug("ce.getElementAttributes().getIsRemote() = {0}",
350 cacheElement.getElementAttributes()::getIsRemote);
351
352 if (cacheElement.getElementAttributes().getIsRemote() && !localOnly)
353 {
354 try
355 {
356
357
358 aux.update(cacheElement);
359 log.debug("Updated remote store for {0} {1}",
360 cacheElement.getKey(), cacheElement);
361 }
362 catch (final IOException ex)
363 {
364 log.error("Failure in updateExclude", ex);
365 }
366 }
367 break;
368
369
370 case LATERAL_CACHE:
371
372
373 log.debug("lateralcache in aux list: cattr {0}", cacheAttr::isUseLateral);
374 if (cacheAttr.isUseLateral() && cacheElement.getElementAttributes().getIsLateral() && !localOnly)
375 {
376
377
378
379 aux.update(cacheElement);
380 log.debug("updated lateral cache for {0}", cacheElement::getKey);
381 }
382 break;
383
384
385 case DISK_CACHE:
386 log.debug("diskcache in aux list: cattr {0}", cacheAttr::isUseDisk);
387 if (cacheAttr.isUseDisk()
388 && cacheAttr.getDiskUsagePattern() == DiskUsagePattern.UPDATE
389 && cacheElement.getElementAttributes().getIsSpool())
390 {
391 aux.update(cacheElement);
392 log.debug("updated disk cache for {0}", cacheElement::getKey);
393 }
394 break;
395
396 default:
397 break;
398 }
399 }
400 }
401
402
403
404
405
406
407
408
409
410
411 public void spoolToDisk(final ICacheElement<K, V> ce)
412 {
413
414 if (!ce.getElementAttributes().getIsSpool())
415 {
416
417 handleElementEvent(ce, ElementEventType.SPOOLED_NOT_ALLOWED);
418 return;
419 }
420
421 boolean diskAvailable = false;
422
423
424 for (final ICache<K, V> aux : auxCaches)
425 {
426 if (aux.getCacheType() == CacheType.DISK_CACHE)
427 {
428 diskAvailable = true;
429
430 if (cacheAttr.getDiskUsagePattern() == DiskUsagePattern.SWAP)
431 {
432
433 try
434 {
435 handleElementEvent(ce, ElementEventType.SPOOLED_DISK_AVAILABLE);
436 aux.update(ce);
437 }
438 catch (final IOException ex)
439 {
440
441 log.error("Problem spooling item to disk cache.", ex);
442 throw new IllegalStateException(ex.getMessage());
443 }
444
445 log.debug("spoolToDisk done for: {0} on disk cache[{1}]",
446 ce::getKey, aux::getCacheName);
447 }
448 else
449 {
450 log.debug("DiskCache available, but JCS is not configured "
451 + "to use the DiskCache as a swap.");
452 }
453 }
454 }
455
456 if (!diskAvailable)
457 {
458 handleElementEvent(ce, ElementEventType.SPOOLED_DISK_NOT_AVAILABLE);
459 }
460 }
461
462
463
464
465
466
467
468
469 @Override
470 public ICacheElement<K, V> get(final K key)
471 {
472 return get(key, false);
473 }
474
475
476
477
478
479
480
481 public ICacheElement<K, V> localGet(final K key)
482 {
483 return get(key, true);
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497 protected ICacheElement<K, V> get(final K key, final boolean localOnly)
498 {
499 ICacheElement<K, V> element = null;
500
501 boolean found = false;
502
503 log.debug("get: key = {0}, localOnly = {1}", key, localOnly);
504
505 try
506 {
507
508 element = memCache.get(key);
509
510 if (element != null)
511 {
512
513 if (isExpired(element))
514 {
515 log.debug("{0} - Memory cache hit, but element expired",
516 () -> cacheAttr.getCacheName());
517
518 doExpires(element);
519 element = null;
520 }
521 else
522 {
523 log.debug("{0} - Memory cache hit", () -> cacheAttr.getCacheName());
524
525
526 hitCountRam.incrementAndGet();
527 }
528
529 found = true;
530 }
531 else
532 {
533
534
535 for (final AuxiliaryCache<K, V> aux : auxCaches)
536 {
537 final CacheType cacheType = aux.getCacheType();
538
539 if (!localOnly || cacheType == CacheType.DISK_CACHE)
540 {
541 log.debug("Attempting to get from aux [{0}] which is of type: {1}",
542 aux::getCacheName, () -> cacheType);
543
544 try
545 {
546 element = aux.get(key);
547 }
548 catch (final IOException e)
549 {
550 log.error("Error getting from aux", e);
551 }
552 }
553
554 log.debug("Got CacheElement: {0}", element);
555
556
557 if (element != null)
558 {
559 if (isExpired(element))
560 {
561 log.debug("{0} - Aux cache[{1}] hit, but element expired.",
562 () -> cacheAttr.getCacheName(), aux::getCacheName);
563
564
565
566
567
568 doExpires(element);
569 element = null;
570 }
571 else
572 {
573 log.debug("{0} - Aux cache[{1}] hit.",
574 () -> cacheAttr.getCacheName(), aux::getCacheName);
575
576
577 hitCountAux.incrementAndGet();
578 copyAuxiliaryRetrievedItemToMemory(element);
579 }
580
581 found = true;
582
583 break;
584 }
585 }
586 }
587 }
588 catch (final IOException e)
589 {
590 log.error("Problem encountered getting element.", e);
591 }
592
593 if (!found)
594 {
595 missCountNotFound.incrementAndGet();
596
597 log.debug("{0} - Miss", () -> cacheAttr.getCacheName());
598 }
599
600 if (element != null)
601 {
602 element.getElementAttributes().setLastAccessTimeNow();
603 }
604
605 return element;
606 }
607
608 protected void doExpires(final ICacheElement<K, V> element)
609 {
610 missCountExpired.incrementAndGet();
611 remove(element.getKey());
612 }
613
614
615
616
617
618
619
620
621 @Override
622 public Map<K, ICacheElement<K, V>> getMultiple(final Set<K> keys)
623 {
624 return getMultiple(keys, false);
625 }
626
627
628
629
630
631
632
633
634
635 public Map<K, ICacheElement<K, V>> localGetMultiple(final Set<K> keys)
636 {
637 return getMultiple(keys, true);
638 }
639
640
641
642
643
644
645
646
647
648
649
650
651
652 protected Map<K, ICacheElement<K, V>> getMultiple(final Set<K> keys, final boolean localOnly)
653 {
654 final Map<K, ICacheElement<K, V>> elements = new HashMap<>();
655
656 log.debug("get: key = {0}, localOnly = {1}", keys, localOnly);
657
658 try
659 {
660
661 elements.putAll(getMultipleFromMemory(keys));
662
663
664 if (elements.size() != keys.size())
665 {
666 final Set<K> remainingKeys = pruneKeysFound(keys, elements);
667 elements.putAll(getMultipleFromAuxiliaryCaches(remainingKeys, localOnly));
668 }
669 }
670 catch (final IOException e)
671 {
672 log.error("Problem encountered getting elements.", e);
673 }
674
675
676 if (elements.size() != keys.size())
677 {
678 missCountNotFound.addAndGet(keys.size() - elements.size());
679
680 log.debug("{0} - {1} Misses", () -> cacheAttr.getCacheName(),
681 () -> keys.size() - elements.size());
682 }
683
684 return elements;
685 }
686
687
688
689
690
691
692
693
694 private Map<K, ICacheElement<K, V>> getMultipleFromMemory(final Set<K> keys)
695 throws IOException
696 {
697 final Map<K, ICacheElement<K, V>> elementsFromMemory = memCache.getMultiple(keys);
698 elementsFromMemory.entrySet().removeIf(entry -> {
699 final ICacheElement<K, V> element = entry.getValue();
700 if (isExpired(element))
701 {
702 log.debug("{0} - Memory cache hit, but element expired",
703 () -> cacheAttr.getCacheName());
704
705 doExpires(element);
706 return true;
707 }
708 log.debug("{0} - Memory cache hit", () -> cacheAttr.getCacheName());
709
710
711 hitCountRam.incrementAndGet();
712 return false;
713 });
714
715 return elementsFromMemory;
716 }
717
718
719
720
721
722
723
724
725
726 private Map<K, ICacheElement<K, V>> getMultipleFromAuxiliaryCaches(final Set<K> keys, final boolean localOnly)
727 throws IOException
728 {
729 final Map<K, ICacheElement<K, V>> elements = new HashMap<>();
730 Set<K> remainingKeys = new HashSet<>(keys);
731
732 for (final AuxiliaryCache<K, V> aux : auxCaches)
733 {
734 final Map<K, ICacheElement<K, V>> elementsFromAuxiliary =
735 new HashMap<>();
736
737 final CacheType cacheType = aux.getCacheType();
738
739 if (!localOnly || cacheType == CacheType.DISK_CACHE)
740 {
741 log.debug("Attempting to get from aux [{0}] which is of type: {1}",
742 aux::getCacheName, () -> cacheType);
743
744 try
745 {
746 elementsFromAuxiliary.putAll(aux.getMultiple(remainingKeys));
747 }
748 catch (final IOException e)
749 {
750 log.error("Error getting from aux", e);
751 }
752 }
753
754 log.debug("Got CacheElements: {0}", elementsFromAuxiliary);
755
756 processRetrievedElements(aux, elementsFromAuxiliary);
757 elements.putAll(elementsFromAuxiliary);
758
759 if (elements.size() == keys.size())
760 {
761 break;
762 }
763 remainingKeys = pruneKeysFound(keys, elements);
764 }
765
766 return elements;
767 }
768
769
770
771
772
773
774
775
776 @Override
777 public Map<K, ICacheElement<K, V>> getMatching(final String pattern)
778 {
779 return getMatching(pattern, false);
780 }
781
782
783
784
785
786
787
788
789
790 public Map<K, ICacheElement<K, V>> localGetMatching(final String pattern)
791 {
792 return getMatching(pattern, true);
793 }
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808 protected Map<K, ICacheElement<K, V>> getMatching(final String pattern, final boolean localOnly)
809 {
810 log.debug("get: pattern [{0}], localOnly = {1}", pattern, localOnly);
811
812 try
813 {
814 return Stream.concat(
815 getMatchingFromMemory(pattern).entrySet().stream(),
816 getMatchingFromAuxiliaryCaches(pattern, localOnly).entrySet().stream())
817 .collect(Collectors.toMap(
818 Entry::getKey,
819 Entry::getValue,
820
821 (mem, aux) -> mem));
822 }
823 catch (final IOException e)
824 {
825 log.error("Problem encountered getting elements.", e);
826 }
827
828 return new HashMap<>();
829 }
830
831
832
833
834
835
836
837
838
839
840 protected Map<K, ICacheElement<K, V>> getMatchingFromMemory(final String pattern)
841 throws IOException
842 {
843
844
845 final Set<K> keyArray = memCache.getKeySet();
846 final Set<K> matchingKeys = getKeyMatcher().getMatchingKeysFromArray(pattern, keyArray);
847
848
849 return getMultipleFromMemory(matchingKeys);
850 }
851
852
853
854
855
856
857
858
859
860
861
862
863
864 private Map<K, ICacheElement<K, V>> getMatchingFromAuxiliaryCaches(final String pattern, final boolean localOnly)
865 throws IOException
866 {
867 final Map<K, ICacheElement<K, V>> elements = new HashMap<>();
868
869 for (ListIterator<AuxiliaryCache<K, V>> i = auxCaches.listIterator(auxCaches.size()); i.hasPrevious();)
870 {
871 final AuxiliaryCache<K, V> aux = i.previous();
872
873 final Map<K, ICacheElement<K, V>> elementsFromAuxiliary =
874 new HashMap<>();
875
876 final CacheType cacheType = aux.getCacheType();
877
878 if (!localOnly || cacheType == CacheType.DISK_CACHE)
879 {
880 log.debug("Attempting to get from aux [{0}] which is of type: {1}",
881 aux::getCacheName, () -> cacheType);
882
883 try
884 {
885 elementsFromAuxiliary.putAll(aux.getMatching(pattern));
886 }
887 catch (final IOException e)
888 {
889 log.error("Error getting from aux", e);
890 }
891
892 log.debug("Got CacheElements: {0}", elementsFromAuxiliary);
893
894 processRetrievedElements(aux, elementsFromAuxiliary);
895 elements.putAll(elementsFromAuxiliary);
896 }
897 }
898
899 return elements;
900 }
901
902
903
904
905
906
907
908
909 private void processRetrievedElements(final AuxiliaryCache<K, V> aux, final Map<K, ICacheElement<K, V>> elementsFromAuxiliary)
910 throws IOException
911 {
912 elementsFromAuxiliary.entrySet().removeIf(entry -> {
913 final ICacheElement<K, V> element = entry.getValue();
914
915
916 if (element != null)
917 {
918 if (isExpired(element))
919 {
920 log.debug("{0} - Aux cache[{1}] hit, but element expired.",
921 () -> cacheAttr.getCacheName(), aux::getCacheName);
922
923
924
925
926
927 doExpires(element);
928 return true;
929 }
930 log.debug("{0} - Aux cache[{1}] hit.",
931 () -> cacheAttr.getCacheName(), aux::getCacheName);
932
933
934 hitCountAux.incrementAndGet();
935 try
936 {
937 copyAuxiliaryRetrievedItemToMemory(element);
938 }
939 catch (final IOException e)
940 {
941 log.error("{0} failed to copy element to memory {1}",
942 cacheAttr.getCacheName(), element, e);
943 }
944 }
945
946 return false;
947 });
948 }
949
950
951
952
953
954
955
956
957 private void copyAuxiliaryRetrievedItemToMemory(final ICacheElement<K, V> element)
958 throws IOException
959 {
960 if (memCache.getCacheAttributes().getMaxObjects() > 0)
961 {
962 memCache.update(element);
963 }
964 else
965 {
966 log.debug("Skipping memory update since no items are allowed in memory");
967 }
968 }
969
970
971
972
973
974
975
976
977
978 private Set<K> pruneKeysFound(final Set<K> keys, final Map<K, ICacheElement<K, V>> foundElements)
979 {
980 final Set<K> remainingKeys = new HashSet<>(keys);
981 remainingKeys.removeAll(foundElements.keySet());
982
983 return remainingKeys;
984 }
985
986
987
988
989
990
991 public Set<K> getKeySet()
992 {
993 return getKeySet(false);
994 }
995
996
997
998
999
1000
1001
1002
1003 public Set<K> getKeySet(final boolean localOnly)
1004 {
1005 return Stream.concat(memCache.getKeySet().stream(), auxCaches.stream()
1006 .filter(aux -> !localOnly || aux.getCacheType() == CacheType.DISK_CACHE)
1007 .flatMap(aux -> {
1008 try
1009 {
1010 return aux.getKeySet().stream();
1011 }
1012 catch (final IOException e)
1013 {
1014 return Stream.of();
1015 }
1016 }))
1017 .collect(Collectors.toSet());
1018 }
1019
1020
1021
1022
1023
1024
1025
1026
1027 @Override
1028 public boolean remove(final K key)
1029 {
1030 return remove(key, false);
1031 }
1032
1033
1034
1035
1036
1037
1038
1039 public boolean localRemove(final K key)
1040 {
1041 return remove(key, true);
1042 }
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061 protected boolean remove(final K key, final boolean localOnly)
1062 {
1063 removeCount.incrementAndGet();
1064
1065 boolean removed = false;
1066
1067 try
1068 {
1069 removed = memCache.remove(key);
1070 }
1071 catch (final IOException e)
1072 {
1073 log.error(e);
1074 }
1075
1076
1077 for (final ICache<K, V> aux : auxCaches)
1078 {
1079 if (aux == null)
1080 {
1081 continue;
1082 }
1083
1084 final CacheType cacheType = aux.getCacheType();
1085
1086
1087 if (localOnly && (cacheType == CacheType.REMOTE_CACHE || cacheType == CacheType.LATERAL_CACHE))
1088 {
1089 continue;
1090 }
1091 try
1092 {
1093 log.debug("Removing {0} from cacheType {1}", key, cacheType);
1094
1095 final boolean b = aux.remove(key);
1096
1097
1098 if (!removed && cacheType != CacheType.REMOTE_CACHE)
1099 {
1100 removed = b;
1101 }
1102 }
1103 catch (final IOException ex)
1104 {
1105 log.error("Failure removing from aux", ex);
1106 }
1107 }
1108
1109 return removed;
1110 }
1111
1112
1113
1114
1115
1116
1117
1118 @Override
1119 public void removeAll()
1120 throws IOException
1121 {
1122 removeAll(false);
1123 }
1124
1125
1126
1127
1128
1129
1130 public void localRemoveAll()
1131 throws IOException
1132 {
1133 removeAll(true);
1134 }
1135
1136
1137
1138
1139
1140
1141
1142
1143 protected void removeAll(final boolean localOnly)
1144 throws IOException
1145 {
1146 try
1147 {
1148 memCache.removeAll();
1149
1150 log.debug("Removed All keys from the memory cache.");
1151 }
1152 catch (final IOException ex)
1153 {
1154 log.error("Trouble updating memory cache.", ex);
1155 }
1156
1157
1158 auxCaches.stream()
1159 .filter(aux -> aux.getCacheType() == CacheType.DISK_CACHE || !localOnly)
1160 .forEach(aux -> {
1161 try
1162 {
1163 log.debug("Removing All keys from cacheType {0}",
1164 aux::getCacheType);
1165
1166 aux.removeAll();
1167 }
1168 catch (final IOException ex)
1169 {
1170 log.error("Failure removing all from aux " + aux, ex);
1171 }
1172 });
1173 }
1174
1175
1176
1177
1178 @Override
1179 public void dispose()
1180 {
1181 dispose(false);
1182 }
1183
1184
1185
1186
1187
1188
1189
1190
1191 public void dispose(final boolean fromRemote)
1192 {
1193
1194 if (!alive.compareAndSet(true, false))
1195 {
1196 return;
1197 }
1198
1199 log.info("In DISPOSE, [{0}] fromRemote [{1}]",
1200 this.cacheAttr::getCacheName, () -> fromRemote);
1201
1202
1203
1204 if (cacheManager != null)
1205 {
1206 cacheManager.freeCache(getCacheName(), fromRemote);
1207 }
1208
1209
1210 if (future != null)
1211 {
1212 future.cancel(true);
1213 }
1214
1215
1216 if (elementEventQ != null)
1217 {
1218 elementEventQ.dispose();
1219 elementEventQ = null;
1220 }
1221
1222
1223
1224 for (final ICache<K, V> aux : auxCaches)
1225 {
1226 try
1227 {
1228
1229
1230
1231
1232 if (aux == null || aux.getStatus() != CacheStatus.ALIVE
1233 || fromRemote && aux.getCacheType() == CacheType.REMOTE_CACHE)
1234 {
1235 log.info("In DISPOSE, [{0}] SKIPPING auxiliary [{1}] fromRemote [{2}]",
1236 this.cacheAttr::getCacheName,
1237 () -> aux == null ? "null" : aux.getCacheName(),
1238 () -> fromRemote);
1239 continue;
1240 }
1241
1242 log.info("In DISPOSE, [{0}] auxiliary [{1}]",
1243 this.cacheAttr::getCacheName, aux::getCacheName);
1244
1245
1246
1247
1248
1249
1250
1251 if (aux.getCacheType() == CacheType.DISK_CACHE)
1252 {
1253 final int numToFree = memCache.getSize();
1254 memCache.freeElements(numToFree);
1255
1256 log.info("In DISPOSE, [{0}] put {1} into auxiliary [{2}]",
1257 this.cacheAttr::getCacheName, () -> numToFree,
1258 aux::getCacheName);
1259 }
1260
1261
1262 aux.dispose();
1263 }
1264 catch (final IOException ex)
1265 {
1266 log.error("Failure disposing of aux.", ex);
1267 }
1268 }
1269
1270 log.info("In DISPOSE, [{0}] disposing of memory cache.",
1271 this.cacheAttr::getCacheName);
1272 try
1273 {
1274 memCache.dispose();
1275 }
1276 catch (final IOException ex)
1277 {
1278 log.error("Failure disposing of memCache", ex);
1279 }
1280 }
1281
1282
1283
1284
1285
1286
1287 public void save()
1288 {
1289 if (!alive.get())
1290 {
1291 return;
1292 }
1293
1294 auxCaches.stream()
1295 .filter(aux -> aux.getStatus() == CacheStatus.ALIVE)
1296 .forEach(aux -> {
1297 memCache.getKeySet().stream()
1298 .map(this::localGet)
1299 .filter(Objects::nonNull)
1300 .forEach(ce -> {
1301 try
1302 {
1303 aux.update(ce);
1304 }
1305 catch (IOException e)
1306 {
1307 log.warn("Failure saving element {0} to aux {1}.", ce, aux, e);
1308 }
1309 });
1310 });
1311
1312 log.debug("Called save for [{0}]", cacheAttr::getCacheName);
1313 }
1314
1315
1316
1317
1318
1319
1320
1321 @Override
1322 public int getSize()
1323 {
1324 return memCache.getSize();
1325 }
1326
1327
1328
1329
1330
1331
1332 @Override
1333 public CacheType getCacheType()
1334 {
1335 return CacheType.CACHE_HUB;
1336 }
1337
1338
1339
1340
1341
1342
1343 @Override
1344 public CacheStatus getStatus()
1345 {
1346 return alive.get() ? CacheStatus.ALIVE : CacheStatus.DISPOSED;
1347 }
1348
1349
1350
1351
1352
1353
1354 @Override
1355 public String getStats()
1356 {
1357 return getStatistics().toString();
1358 }
1359
1360
1361
1362
1363
1364
1365 public ICacheStats getStatistics()
1366 {
1367 final ICacheStats stats = new CacheStats();
1368 stats.setRegionName(this.getCacheName());
1369
1370
1371 stats.setStatElements(Arrays.asList(
1372 new StatElement<>("HitCountRam", Long.valueOf(getHitCountRam())),
1373 new StatElement<>("HitCountAux", Long.valueOf(getHitCountAux()))));
1374
1375
1376 final ArrayList<IStats> auxStats = new ArrayList<>(auxCaches.size() + 1);
1377
1378 auxStats.add(getMemoryCache().getStatistics());
1379 auxStats.addAll(auxCaches.stream()
1380 .map(AuxiliaryCache::getStatistics)
1381 .collect(Collectors.toList()));
1382
1383
1384 stats.setAuxiliaryCacheStats(auxStats);
1385
1386 return stats;
1387 }
1388
1389
1390
1391
1392
1393
1394 @Override
1395 public String getCacheName()
1396 {
1397 return cacheAttr.getCacheName();
1398 }
1399
1400
1401
1402
1403
1404
1405
1406 public IElementAttributes getElementAttributes()
1407 {
1408 if (attr != null)
1409 {
1410 return attr.clone();
1411 }
1412 return null;
1413 }
1414
1415
1416
1417
1418
1419
1420 public void setElementAttributes(final IElementAttributes attr)
1421 {
1422 this.attr = attr;
1423 }
1424
1425
1426
1427
1428
1429
1430 public ICompositeCacheAttributes getCacheAttributes()
1431 {
1432 return this.cacheAttr;
1433 }
1434
1435
1436
1437
1438
1439
1440 public void setCacheAttributes(final ICompositeCacheAttributes cattr)
1441 {
1442 this.cacheAttr = cattr;
1443
1444 this.memCache.initialize(this);
1445 }
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455 public IElementAttributes getElementAttributes(final K key)
1456 throws CacheException, IOException
1457 {
1458 final ICacheElement<K, V> ce = get(key);
1459 if (ce == null)
1460 {
1461 throw new ObjectNotFoundException("key " + key + " is not found");
1462 }
1463 return ce.getElementAttributes();
1464 }
1465
1466
1467
1468
1469
1470
1471
1472
1473 public boolean isExpired(final ICacheElement<K, V> element)
1474 {
1475 return isExpired(element, System.currentTimeMillis(),
1476 ElementEventType.EXCEEDED_MAXLIFE_ONREQUEST,
1477 ElementEventType.EXCEEDED_IDLETIME_ONREQUEST);
1478 }
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490 public boolean isExpired(final ICacheElement<K, V> element, final long timestamp,
1491 final ElementEventType eventMaxlife, final ElementEventType eventIdle)
1492 {
1493 try
1494 {
1495 final IElementAttributes attributes = element.getElementAttributes();
1496
1497 if (!attributes.getIsEternal())
1498 {
1499
1500 final long maxLifeSeconds = attributes.getMaxLife();
1501 final long createTime = attributes.getCreateTime();
1502
1503 final long timeFactorForMilliseconds = attributes.getTimeFactorForMilliseconds();
1504
1505 if (maxLifeSeconds != -1 && (timestamp - createTime) > (maxLifeSeconds * timeFactorForMilliseconds))
1506 {
1507 log.debug("Exceeded maxLife: {0}", element::getKey);
1508
1509 handleElementEvent(element, eventMaxlife);
1510 return true;
1511 }
1512 final long idleTime = attributes.getIdleTime();
1513 final long lastAccessTime = attributes.getLastAccessTime();
1514
1515
1516
1517
1518
1519 if (idleTime != -1 && timestamp - lastAccessTime > idleTime * timeFactorForMilliseconds)
1520 {
1521 log.debug("Exceeded maxIdle: {0}", element::getKey);
1522
1523 handleElementEvent(element, eventIdle);
1524 return true;
1525 }
1526 }
1527 }
1528 catch (final Exception e)
1529 {
1530 log.error("Error determining expiration period, expiring", e);
1531 return true;
1532 }
1533
1534 return false;
1535 }
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546 public void handleElementEvent(final ICacheElement<K, V> element, final ElementEventType eventType)
1547 {
1548 final ArrayList<IElementEventHandler> eventHandlers = element.getElementAttributes().getElementEventHandlers();
1549 if (eventHandlers != null)
1550 {
1551 log.debug("Element Handlers are registered. Create event type {0}", eventType);
1552 if (elementEventQ == null)
1553 {
1554 log.warn("No element event queue available for cache {0}", this::getCacheName);
1555 return;
1556 }
1557 final IElementEvent<ICacheElement<K, V>> event = new ElementEvent<>(element, eventType);
1558 for (final IElementEventHandler hand : eventHandlers)
1559 {
1560 try
1561 {
1562 elementEventQ.addElementEvent(hand, event);
1563 }
1564 catch (final IOException e)
1565 {
1566 log.error("Trouble adding element event to queue", e);
1567 }
1568 }
1569 }
1570 }
1571
1572
1573
1574
1575
1576
1577
1578
1579 private void createMemoryCache(final ICompositeCacheAttributes cattr)
1580 {
1581 if (memCache == null)
1582 {
1583 try
1584 {
1585 final Class<?> c = Class.forName(cattr.getMemoryCacheName());
1586 @SuppressWarnings("unchecked")
1587 final
1588 IMemoryCache<K, V> newInstance =
1589 (IMemoryCache<K, V>) c.getDeclaredConstructor().newInstance();
1590 memCache = newInstance;
1591 memCache.initialize(this);
1592 }
1593 catch (final Exception e)
1594 {
1595 log.warn("Failed to init mem cache, using: LRUMemoryCache", e);
1596
1597 this.memCache = new LRUMemoryCache<>();
1598 this.memCache.initialize(this);
1599 }
1600 }
1601 else
1602 {
1603 log.warn("Refusing to create memory cache -- already exists.");
1604 }
1605 }
1606
1607
1608
1609
1610
1611
1612 public IMemoryCache<K, V> getMemoryCache()
1613 {
1614 return memCache;
1615 }
1616
1617
1618
1619
1620
1621
1622 public long getHitCountRam()
1623 {
1624 return hitCountRam.get();
1625 }
1626
1627
1628
1629
1630
1631 public long getHitCountAux()
1632 {
1633 return hitCountAux.get();
1634 }
1635
1636
1637
1638
1639
1640 public long getMissCountNotFound()
1641 {
1642 return missCountNotFound.get();
1643 }
1644
1645
1646
1647
1648
1649 public long getMissCountExpired()
1650 {
1651 return missCountExpired.get();
1652 }
1653
1654
1655
1656
1657 public long getUpdateCount()
1658 {
1659 return updateCount.get();
1660 }
1661
1662
1663
1664
1665
1666
1667 @Override
1668 public void setKeyMatcher(final IKeyMatcher<K> keyMatcher)
1669 {
1670 if (keyMatcher != null)
1671 {
1672 this.keyMatcher = keyMatcher;
1673 }
1674 }
1675
1676
1677
1678
1679
1680
1681 public IKeyMatcher<K> getKeyMatcher()
1682 {
1683 return this.keyMatcher;
1684 }
1685
1686
1687
1688
1689
1690
1691 @Override
1692 public String toString()
1693 {
1694 return getStats();
1695 }
1696 }