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.io.InputStream;
24 import java.lang.management.ManagementFactory;
25 import java.security.AccessControlException;
26 import java.util.List;
27 import java.util.Objects;
28 import java.util.Properties;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentMap;
32 import java.util.concurrent.Executors;
33 import java.util.concurrent.LinkedBlockingDeque;
34 import java.util.concurrent.ScheduledExecutorService;
35 import java.util.concurrent.atomic.AtomicInteger;
36 import java.util.stream.Collectors;
37 import java.util.stream.Stream;
38
39 import javax.management.MBeanServer;
40 import javax.management.ObjectName;
41
42 import org.apache.commons.jcs3.access.exception.CacheException;
43 import org.apache.commons.jcs3.admin.JCSAdminBean;
44 import org.apache.commons.jcs3.auxiliary.AuxiliaryCache;
45 import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheAttributes;
46 import org.apache.commons.jcs3.auxiliary.AuxiliaryCacheFactory;
47 import org.apache.commons.jcs3.auxiliary.remote.behavior.IRemoteCacheConstants;
48 import org.apache.commons.jcs3.engine.CompositeCacheAttributes;
49 import org.apache.commons.jcs3.engine.ElementAttributes;
50 import org.apache.commons.jcs3.engine.behavior.ICache;
51 import org.apache.commons.jcs3.engine.behavior.ICacheType.CacheType;
52 import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes;
53 import org.apache.commons.jcs3.engine.behavior.ICompositeCacheManager;
54 import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
55 import org.apache.commons.jcs3.engine.behavior.IProvideScheduler;
56 import org.apache.commons.jcs3.engine.behavior.IShutdownObserver;
57 import org.apache.commons.jcs3.engine.control.event.ElementEventQueue;
58 import org.apache.commons.jcs3.engine.control.event.behavior.IElementEventQueue;
59 import org.apache.commons.jcs3.engine.stats.CacheStats;
60 import org.apache.commons.jcs3.engine.stats.behavior.ICacheStats;
61 import org.apache.commons.jcs3.log.Log;
62 import org.apache.commons.jcs3.log.LogManager;
63 import org.apache.commons.jcs3.utils.config.OptionConverter;
64 import org.apache.commons.jcs3.utils.threadpool.DaemonThreadFactory;
65 import org.apache.commons.jcs3.utils.threadpool.ThreadPoolManager;
66 import org.apache.commons.jcs3.utils.timing.ElapsedTimer;
67
68
69
70
71
72
73
74
75
76 public class CompositeCacheManager
77 implements IRemoteCacheConstants, ICompositeCacheManager, IProvideScheduler
78 {
79
80 private static final Log log = LogManager.getLog( CompositeCacheManager.class );
81
82
83 public static final String JMX_OBJECT_NAME = "org.apache.commons.jcs3:type=JCSAdminBean";
84
85
86 private static final String DEFAULT_CONFIG = "/cache.ccf";
87
88
89 private static final String DEFAULT_REGION = "jcs.default";
90
91
92 private static final boolean DEFAULT_USE_SYSTEM_PROPERTIES = true;
93
94
95 private static final boolean DEFAULT_FORCE_RECONFIGURATION = false;
96
97
98 private final ConcurrentMap<String, ICache<?, ?>> caches = new ConcurrentHashMap<>();
99
100
101 private final AtomicInteger clients = new AtomicInteger(0);
102
103
104 private ICompositeCacheAttributes defaultCacheAttr = new CompositeCacheAttributes();
105
106
107 private IElementAttributes defaultElementAttr = new ElementAttributes();
108
109
110 private final ConcurrentMap<String, AuxiliaryCacheFactory> auxiliaryFactoryRegistry =
111 new ConcurrentHashMap<>( );
112
113
114 private final ConcurrentMap<String, AuxiliaryCacheAttributes> auxiliaryAttributeRegistry =
115 new ConcurrentHashMap<>( );
116
117
118 private final ConcurrentMap<String, AuxiliaryCache<?, ?>> auxiliaryCaches =
119 new ConcurrentHashMap<>( );
120
121
122 private Properties configurationProperties;
123
124
125 private String defaultAuxValues;
126
127
128 private static CompositeCacheManager instance;
129
130
131 private final LinkedBlockingDeque<IShutdownObserver> shutdownObservers = new LinkedBlockingDeque<>();
132
133
134 private ScheduledExecutorService scheduledExecutor;
135
136
137 private IElementEventQueue elementEventQueue;
138
139
140 private Thread shutdownHook;
141
142
143 private boolean isInitialized;
144
145
146 private boolean isConfigured;
147
148
149 private boolean isJMXRegistered;
150
151 private String jmxName = JMX_OBJECT_NAME;
152
153
154
155
156
157
158
159
160
161 public static synchronized CompositeCacheManager getInstance() throws CacheException
162 {
163 return getInstance( DEFAULT_CONFIG );
164 }
165
166
167
168
169
170
171
172
173 public static synchronized CompositeCacheManager getInstance( final String propsFilename ) throws CacheException
174 {
175 if ( instance == null )
176 {
177 log.info( "Instance is null, creating with config [{0}]", propsFilename );
178 instance = createInstance();
179 }
180
181 if (!instance.isInitialized())
182 {
183 instance.initialize();
184 }
185
186 if (!instance.isConfigured())
187 {
188 instance.configure( propsFilename );
189 }
190
191 instance.clients.incrementAndGet();
192
193 return instance;
194 }
195
196
197
198
199
200
201
202 public static synchronized CompositeCacheManager getUnconfiguredInstance()
203 {
204 if ( instance == null )
205 {
206 log.info( "Instance is null, returning unconfigured instance" );
207 instance = createInstance();
208 }
209
210 if (!instance.isInitialized())
211 {
212 instance.initialize();
213 }
214
215 instance.clients.incrementAndGet();
216
217 return instance;
218 }
219
220
221
222
223
224
225
226 protected static CompositeCacheManager createInstance()
227 {
228 return new CompositeCacheManager();
229 }
230
231
232
233
234 protected CompositeCacheManager()
235 {
236
237 }
238
239
240 protected synchronized void initialize()
241 {
242 if (!isInitialized)
243 {
244 this.shutdownHook = new Thread(() -> {
245 if ( isInitialized() )
246 {
247 log.info("Shutdown hook activated. Shutdown was not called. Shutting down JCS.");
248 shutDown();
249 }
250 });
251 try
252 {
253 Runtime.getRuntime().addShutdownHook( shutdownHook );
254 }
255 catch ( final AccessControlException e )
256 {
257 log.error( "Could not register shutdown hook.", e );
258 }
259
260 this.scheduledExecutor = Executors.newScheduledThreadPool(4,
261 new DaemonThreadFactory("JCS-Scheduler-", Thread.MIN_PRIORITY));
262
263
264 if (!isJMXRegistered && jmxName != null)
265 {
266 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
267 final JCSAdminBean adminBean = new JCSAdminBean(this);
268 try
269 {
270 final ObjectName jmxObjectName = new ObjectName(jmxName);
271 mbs.registerMBean(adminBean, jmxObjectName);
272 isJMXRegistered = true;
273 }
274 catch (final Exception e)
275 {
276 log.warn( "Could not register JMX bean.", e );
277 }
278 }
279
280 isInitialized = true;
281 }
282 }
283
284
285
286
287
288
289 public IElementEventQueue getElementEventQueue()
290 {
291 return elementEventQueue;
292 }
293
294
295
296
297
298
299 @Override
300 public ScheduledExecutorService getScheduledExecutorService()
301 {
302 return scheduledExecutor;
303 }
304
305
306
307
308
309 public void configure() throws CacheException
310 {
311 configure( DEFAULT_CONFIG );
312 }
313
314
315
316
317
318
319
320 public void configure( final String propFile ) throws CacheException
321 {
322 log.info( "Creating cache manager from config file: {0}", propFile );
323
324 final Properties props = new Properties();
325
326 try (InputStream is = getClass().getResourceAsStream( propFile ))
327 {
328 props.load( is );
329 log.debug( "File [{0}] contained {1} properties", () -> propFile, props::size);
330 }
331 catch ( final IOException ex )
332 {
333 throw new CacheException("Failed to load properties for name [" + propFile + "]", ex);
334 }
335
336 configure( props );
337 }
338
339
340
341
342
343
344
345 public void configure( final Properties props )
346 {
347 configure( props, DEFAULT_USE_SYSTEM_PROPERTIES );
348 }
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363 public void configure( final Properties props, final boolean useSystemProperties )
364 {
365 configure( props, useSystemProperties, DEFAULT_FORCE_RECONFIGURATION );
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 public synchronized void configure( final Properties props, final boolean useSystemProperties, final boolean forceReconfiguration )
384 {
385 if ( props == null )
386 {
387 log.error( "No properties found. Please configure the cache correctly." );
388 return;
389 }
390
391 if ( isConfigured )
392 {
393 if ( !forceReconfiguration )
394 {
395 log.debug( "Configure called after the manager has been configured. "
396 + "Force reconfiguration is false. Doing nothing" );
397 return;
398 }
399 log.info( "Configure called after the manager has been configured. "
400 + "Force reconfiguration is true. Reconfiguring as best we can." );
401 }
402 if ( useSystemProperties )
403 {
404 CompositeCacheConfigurator.overrideWithSystemProperties( props );
405 }
406 doConfigure( props );
407 }
408
409
410
411
412
413
414 private synchronized void doConfigure( final Properties properties )
415 {
416
417 this.configurationProperties = properties;
418
419
420 ThreadPoolManager.setProps( properties );
421 final ThreadPoolManager poolMgr = ThreadPoolManager.getInstance();
422 log.debug( "ThreadPoolManager = {0}", poolMgr);
423
424
425 this.elementEventQueue = new ElementEventQueue();
426
427
428 final CompositeCacheConfigurator configurator = newConfigurator();
429
430 final ElapsedTimer timer = new ElapsedTimer();
431
432
433 this.defaultAuxValues = OptionConverter.findAndSubst( CompositeCacheManager.DEFAULT_REGION,
434 properties );
435
436 log.info( "Setting default auxiliaries to \"{0}\"", this.defaultAuxValues );
437
438
439 this.defaultCacheAttr = configurator.parseCompositeCacheAttributes( properties, "",
440 new CompositeCacheAttributes(), DEFAULT_REGION );
441
442 log.info( "setting defaultCompositeCacheAttributes to {0}", this.defaultCacheAttr );
443
444
445 this.defaultElementAttr = configurator.parseElementAttributes( properties, "",
446 new ElementAttributes(), DEFAULT_REGION );
447
448 log.info( "setting defaultElementAttributes to {0}", this.defaultElementAttr );
449
450
451
452 configurator.parseSystemRegions( properties, this );
453
454
455 configurator.parseRegions( properties, this );
456
457 log.info( "Finished configuration in {0} ms.", timer::getElapsedTime);
458
459 isConfigured = true;
460 }
461
462
463
464
465
466
467 public ICompositeCacheAttributes getDefaultCacheAttributes()
468 {
469 return this.defaultCacheAttr.clone();
470 }
471
472
473
474
475
476
477 public IElementAttributes getDefaultElementAttributes()
478 {
479 return this.defaultElementAttr.clone();
480 }
481
482
483
484
485
486
487
488 @Override
489 public <K, V> CompositeCache<K, V> getCache( final String cacheName )
490 {
491 return getCache( cacheName, getDefaultCacheAttributes() );
492 }
493
494
495
496
497
498
499
500
501 public <K, V> CompositeCache<K, V> getCache( final String cacheName, final ICompositeCacheAttributes cattr )
502 {
503 cattr.setCacheName( cacheName );
504 return getCache( cattr, getDefaultElementAttributes() );
505 }
506
507
508
509
510
511
512
513
514
515 public <K, V> CompositeCache<K, V> getCache( final String cacheName, final ICompositeCacheAttributes cattr, final IElementAttributes attr )
516 {
517 cattr.setCacheName( cacheName );
518 return getCache( cattr, attr );
519 }
520
521
522
523
524
525
526
527 public <K, V> CompositeCache<K, V> getCache( final ICompositeCacheAttributes cattr )
528 {
529 return getCache( cattr, getDefaultElementAttributes() );
530 }
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545 @SuppressWarnings("unchecked")
546 public <K, V> CompositeCache<K, V> getCache( final ICompositeCacheAttributes cattr, final IElementAttributes attr )
547 {
548 log.debug( "attr = {0}", attr );
549
550 return (CompositeCache<K, V>) caches.computeIfAbsent(cattr.getCacheName(),
551 cacheName -> {
552 final CompositeCacheConfigurator configurator = newConfigurator();
553 return configurator.parseRegion( this.getConfigurationProperties(), this, cacheName,
554 this.defaultAuxValues, cattr );
555 });
556 }
557
558 protected CompositeCacheConfigurator newConfigurator() {
559 return new CompositeCacheConfigurator();
560 }
561
562
563
564
565 public void freeCache( final String name )
566 {
567 freeCache( name, false );
568 }
569
570
571
572
573
574 public void freeCache( final String name, final boolean fromRemote )
575 {
576 final CompositeCache<?, ?> cache = (CompositeCache<?, ?>) caches.remove( name );
577
578 if ( cache != null )
579 {
580 cache.dispose( fromRemote );
581 }
582 }
583
584
585
586
587 public synchronized void shutDown()
588 {
589
590 if (this.elementEventQueue != null)
591 {
592 this.elementEventQueue.dispose();
593 }
594
595
596 IShutdownObserver observer = null;
597 while ((observer = shutdownObservers.poll()) != null)
598 {
599 observer.shutdown();
600 }
601
602
603 if (isJMXRegistered)
604 {
605 final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
606 try
607 {
608 final ObjectName jmxObjectName = new ObjectName(jmxName);
609 mbs.unregisterMBean(jmxObjectName);
610 }
611 catch (final Exception e)
612 {
613 log.warn( "Could not unregister JMX bean.", e );
614 }
615
616 isJMXRegistered = false;
617 }
618
619
620 getCacheNames().forEach(this::freeCache);
621
622
623 for (final String key : auxiliaryCaches.keySet())
624 {
625 try
626 {
627 freeAuxiliaryCache(key);
628 }
629 catch (final IOException e)
630 {
631 log.warn("Auxiliary cache {0} failed to shut down", key, e);
632 }
633 }
634
635
636 auxiliaryFactoryRegistry.values().forEach(AuxiliaryCacheFactory::dispose);
637
638 auxiliaryAttributeRegistry.clear();
639 auxiliaryFactoryRegistry.clear();
640
641
642 this.scheduledExecutor.shutdownNow();
643
644
645 ThreadPoolManager.dispose();
646
647 if (shutdownHook != null)
648 {
649 try
650 {
651 Runtime.getRuntime().removeShutdownHook(shutdownHook);
652 }
653 catch (final IllegalStateException e)
654 {
655
656 }
657
658 this.shutdownHook = null;
659 }
660
661 isConfigured = false;
662 isInitialized = false;
663 }
664
665
666 public void release()
667 {
668 release( false );
669 }
670
671
672
673
674 private void release( final boolean fromRemote )
675 {
676 synchronized ( CompositeCacheManager.class )
677 {
678
679 if ( clients.decrementAndGet() > 0 )
680 {
681 log.debug( "Release called, but {0} remain", clients);
682 return;
683 }
684
685 log.debug( "Last client called release. There are {0} caches which will be disposed",
686 caches::size);
687
688 caches.values().stream()
689 .filter(Objects::nonNull)
690 .forEach(cache -> ((CompositeCache<?, ?>)cache).dispose( fromRemote ));
691 }
692 }
693
694
695
696
697
698 public Set<String> getCacheNames()
699 {
700 return caches.keySet();
701 }
702
703
704
705
706 public CacheType getCacheType()
707 {
708 return CacheType.CACHE_HUB;
709 }
710
711
712
713
714 public void registryFacPut( final AuxiliaryCacheFactory auxFac )
715 {
716 auxiliaryFactoryRegistry.put( auxFac.getName(), auxFac );
717 }
718
719
720
721
722
723 public AuxiliaryCacheFactory registryFacGet( final String name )
724 {
725 return auxiliaryFactoryRegistry.get( name );
726 }
727
728
729
730
731 public void registryAttrPut( final AuxiliaryCacheAttributes auxAttr )
732 {
733 auxiliaryAttributeRegistry.put( auxAttr.getName(), auxAttr );
734 }
735
736
737
738
739
740 public AuxiliaryCacheAttributes registryAttrGet( final String name )
741 {
742 return auxiliaryAttributeRegistry.get( name );
743 }
744
745
746
747
748
749
750
751 public void addCache(final String cacheName, final ICache<?, ?> cache)
752 {
753 caches.put(cacheName, cache);
754 }
755
756
757
758
759
760
761
762
763 public void addAuxiliaryCache(final String auxName, final String cacheName, final AuxiliaryCache<?, ?> cache)
764 {
765 final String key = String.format("aux.%s.region.%s", auxName, cacheName);
766 auxiliaryCaches.put(key, cache);
767 }
768
769
770
771
772
773
774
775
776
777 @Override
778 @SuppressWarnings("unchecked")
779 public <K, V> AuxiliaryCache<K, V> getAuxiliaryCache(final String auxName, final String cacheName)
780 {
781 final String key = String.format("aux.%s.region.%s", auxName, cacheName);
782 return (AuxiliaryCache<K, V>) auxiliaryCaches.get(key);
783 }
784
785
786
787
788
789
790
791
792 public void freeAuxiliaryCache(final String auxName, final String cacheName) throws IOException
793 {
794 final String key = String.format("aux.%s.region.%s", auxName, cacheName);
795 freeAuxiliaryCache(key);
796 }
797
798
799
800
801
802
803
804 public void freeAuxiliaryCache(final String key) throws IOException
805 {
806 final AuxiliaryCache<?, ?> aux = auxiliaryCaches.remove( key );
807
808 if ( aux != null )
809 {
810 aux.dispose();
811 }
812 }
813
814
815
816
817
818
819
820 @Override
821 public String getStats()
822 {
823 final ICacheStats[] stats = getStatistics();
824 if ( stats == null )
825 {
826 return "NONE";
827 }
828
829
830 final StringBuilder buf = new StringBuilder();
831 Stream.of(stats).forEach(stat -> {
832 buf.append( "\n---------------------------\n" );
833 buf.append( stat );
834 });
835 return buf.toString();
836 }
837
838
839
840
841
842
843 public ICacheStats[] getStatistics()
844 {
845 final List<ICacheStats> cacheStats = caches.values().stream()
846 .filter(Objects::nonNull)
847 .map(cache -> ((CompositeCache<?, ?>)cache).getStatistics() )
848 .collect(Collectors.toList());
849
850 return cacheStats.toArray( new CacheStats[0] );
851 }
852
853
854
855
856
857
858
859
860
861 @Override
862 public void registerShutdownObserver( final IShutdownObserver observer )
863 {
864 if (!shutdownObservers.contains(observer))
865 {
866 shutdownObservers.push( observer );
867 }
868 else
869 {
870 log.warn("Shutdown observer added twice {0}", observer);
871 }
872 }
873
874
875
876
877 @Override
878 public void deregisterShutdownObserver( final IShutdownObserver observer )
879 {
880 shutdownObservers.remove( observer );
881 }
882
883
884
885
886
887
888 @Override
889 public Properties getConfigurationProperties()
890 {
891 return configurationProperties;
892 }
893
894
895
896
897 public boolean isInitialized()
898 {
899 return isInitialized;
900 }
901
902
903
904
905 public boolean isConfigured()
906 {
907 return isConfigured;
908 }
909
910 public void setJmxName(final String name)
911 {
912 if (isJMXRegistered)
913 {
914 throw new IllegalStateException("Too late, MBean registration is done");
915 }
916 jmxName = name;
917 }
918 }