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