1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.portal.service.cache;
35
36 import java.io.File;
37 import java.io.FileInputStream;
38 import java.io.FileNotFoundException;
39 import java.lang.management.ManagementFactory;
40 import java.util.ArrayList;
41 import java.util.List;
42 import java.util.Properties;
43
44 import javax.management.MBeanServer;
45
46 import fr.paris.lutece.portal.service.datastore.DatastoreService;
47 import fr.paris.lutece.portal.service.util.AppLogService;
48 import fr.paris.lutece.portal.service.util.AppPathService;
49 import fr.paris.lutece.portal.service.util.AppPropertiesService;
50 import net.sf.ehcache.Cache;
51 import net.sf.ehcache.CacheManager;
52 import net.sf.ehcache.config.CacheConfiguration;
53 import net.sf.ehcache.config.Configuration;
54 import net.sf.ehcache.config.ConfigurationFactory;
55 import net.sf.ehcache.management.ManagementService;
56
57
58
59
60 public final class CacheService
61 {
62 private static final String ERROR_NUMERIC_PROP = "Invalid numeric property : {} {} = {}";
63 private static final String PROPERTY_PATH_CONF = "path.conf";
64 private static final String PROPERTY_IS_ENABLED = ".enabled";
65 private static final String FILE_CACHES_STATUS = "caches.dat";
66
67
68 private static final String PROPERTY_MAX_ELEMENTS = ".maxElementsInMemory";
69 private static final String PROPERTY_ETERNAL = ".eternal";
70 private static final String PROPERTY_TIME_TO_IDLE = ".timeToIdleSeconds";
71 private static final String PROPERTY_TIME_TO_LIVE = ".timeToLiveSeconds";
72 private static final String PROPERTY_OVERFLOW_TO_DISK = ".overflowToDisk";
73 private static final String PROPERTY_DISK_PERSISTENT = ".diskPersistent";
74 private static final String PROPERTY_DISK_EXPIRY = ".diskExpiryThreadIntervalSeconds";
75 private static final String PROPERTY_MAX_ELEMENTS_DISK = ".maxElementsOnDisk";
76 private static final String PROPERTY_STATISTICS = ".statistics";
77
78
79 private static final String KEY_PREFIX = "core.cache.status.";
80
81
82 private static final String PROPERTY_JMX_MONITORING = "lutece.cache.jmx.monitoring.enabled";
83 private static final String PROPERTY_MONITOR_CACHE_MANAGER = "lutece.cache.jmx.monitorCacheManager";
84 private static final String PROPERTY_MONITOR_CACHES = "lutece.cache.jmx.monitorCaches";
85 private static final String PROPERTY_MONITOR_CACHE_CONFIGURATIONS = "lutece.cache.jmx.monitorCacheConfiguration";
86 private static final String PROPERTY_MONITOR_CACHE_STATISTICS = "lutece.cache.jmx.monitorCacheStatistics";
87 private static final String FALSE = "false";
88 private static final String TRUE = "true";
89 private static final String ENABLED = "1";
90 private static final String DISABLED = "0";
91 private static final String NOT_FOUND = "NOT FOUND";
92 private static final String PREFIX_DEFAULT = "lutece.cache.default";
93 private static final String LUTECE_CACHEMANAGER_NAME = "LuteceCacheManager";
94 private static CacheService _singleton;
95 private static CacheManager _manager;
96
97 private static List<CacheableService> _listCacheableServicesRegistry = new ArrayList<>( );
98 private int _nDefaultMaxElementsInMemory;
99 private boolean _bDefaultEternal;
100 private long _lDefaultTimeToIdle;
101 private long _lDefaultTimeToLive;
102 private boolean _bDefaultOverflowToDisk;
103 private boolean _bDefaultDiskPersistent;
104 private long _lDefaultDiskExpiry;
105 private int _nDefaultMaxElementsOnDisk;
106 private boolean _bDefaultStatistics;
107
108
109
110
111 private CacheService( )
112 {
113 }
114
115
116
117
118
119
120 public static synchronized CacheService getInstance( )
121 {
122 if ( _singleton == null )
123 {
124 _singleton = new CacheService( );
125 _singleton.init( );
126 Configuration configuration = ConfigurationFactory.parseConfiguration( );
127 configuration.setName( LUTECE_CACHEMANAGER_NAME );
128 _manager = CacheManager.create( configuration );
129 }
130
131 return _singleton;
132 }
133
134
135
136
137 private void init( )
138 {
139 loadDefaults( );
140 loadCachesConfig( );
141
142 boolean bJmxMonitoring = AppPropertiesService.getProperty( PROPERTY_JMX_MONITORING, FALSE ).equals( TRUE );
143
144 if ( bJmxMonitoring )
145 {
146 initJmxMonitoring( );
147 }
148 }
149
150
151
152
153 private void initJmxMonitoring( )
154 {
155 MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer( );
156
157 boolean bRegisterCacheManager = AppPropertiesService.getProperty( PROPERTY_MONITOR_CACHE_MANAGER, FALSE ).equals( TRUE );
158 boolean bRegisterCaches = AppPropertiesService.getProperty( PROPERTY_MONITOR_CACHES, FALSE ).equals( TRUE );
159 boolean bRegisterCacheConfigurations = AppPropertiesService.getProperty( PROPERTY_MONITOR_CACHE_CONFIGURATIONS, FALSE ).equals( TRUE );
160 boolean bRegisterCacheStatistics = AppPropertiesService.getProperty( PROPERTY_MONITOR_CACHE_STATISTICS, FALSE ).equals( TRUE );
161 ManagementService.registerMBeans( _manager, mBeanServer, bRegisterCacheManager, bRegisterCaches, bRegisterCacheConfigurations,
162 bRegisterCacheStatistics );
163 }
164
165
166
167
168
169
170
171
172 public Cache createCache( String strCacheName )
173 {
174 Cache cache = new Cache( getCacheConfiguration( strCacheName ) );
175 _manager.addCache( cache );
176
177 return _manager.getCache( strCacheName );
178 }
179
180
181
182
183 public static void resetCaches( )
184 {
185
186 for ( CacheableService cs : _listCacheableServicesRegistry )
187 {
188 cs.resetCache( );
189 }
190 }
191
192
193
194
195 public void shutdown( )
196 {
197 CacheService.storeCachesStatus( );
198 _manager.shutdown( );
199 }
200
201
202
203
204
205
206
207 public static void registerCacheableService( CacheableService cs )
208 {
209 _listCacheableServicesRegistry.add( cs );
210
211
212 cs.enableCache( getStatus( cs ) );
213 }
214
215
216
217
218
219
220 public static List<CacheableService> getCacheableServicesList( )
221 {
222 return _listCacheableServicesRegistry;
223 }
224
225
226
227
228 public static void storeCachesStatus( )
229 {
230 for ( CacheableService cs : _listCacheableServicesRegistry )
231 {
232 String strKey = getDSKey( cs.getName( ), PROPERTY_IS_ENABLED );
233 DatastoreService.setInstanceDataValue( strKey, cs.isCacheEnable( ) ? ENABLED : DISABLED );
234 }
235 }
236
237
238
239
240
241
242
243
244 static String getInfos( Cache cache )
245 {
246 StringBuilder sbInfos = new StringBuilder( );
247 sbInfos.append( PROPERTY_MAX_ELEMENTS ).append( "=" ).append( cache.getCacheConfiguration( ).getMaxElementsInMemory( ) ).append( "\n" );
248 sbInfos.append( PROPERTY_ETERNAL ).append( "=" ).append( cache.getCacheConfiguration( ).isEternal( ) ).append( "\n" );
249 sbInfos.append( PROPERTY_TIME_TO_IDLE ).append( "=" ).append( cache.getCacheConfiguration( ).getTimeToIdleSeconds( ) ).append( "\n" );
250 sbInfos.append( PROPERTY_TIME_TO_LIVE ).append( "=" ).append( cache.getCacheConfiguration( ).getTimeToLiveSeconds( ) ).append( "\n" );
251 sbInfos.append( PROPERTY_OVERFLOW_TO_DISK ).append( "=" ).append( cache.getCacheConfiguration( ).isOverflowToDisk( ) ).append( "\n" );
252 sbInfos.append( PROPERTY_DISK_PERSISTENT ).append( "=" ).append( cache.getCacheConfiguration( ).isDiskPersistent( ) ).append( "\n" );
253 sbInfos.append( PROPERTY_DISK_EXPIRY ).append( "=" ).append( cache.getCacheConfiguration( ).getDiskExpiryThreadIntervalSeconds( ) ).append( "\n" );
254 sbInfos.append( PROPERTY_MAX_ELEMENTS_DISK ).append( "=" ).append( cache.getCacheConfiguration( ).getMaxElementsOnDisk( ) ).append( "\n" );
255 sbInfos.append( PROPERTY_STATISTICS ).append( '=' ).append( cache.getCacheConfiguration( ).getStatistics( ) ).append( "\n" );
256
257 return sbInfos.toString( );
258 }
259
260
261
262
263 private void loadDefaults( )
264 {
265 _nDefaultMaxElementsInMemory = AppPropertiesService.getPropertyInt( PREFIX_DEFAULT + PROPERTY_MAX_ELEMENTS, 10000 );
266 _bDefaultEternal = AppPropertiesService.getPropertyBoolean( PREFIX_DEFAULT + PROPERTY_ETERNAL, false );
267 _lDefaultTimeToIdle = AppPropertiesService.getPropertyLong( PREFIX_DEFAULT + PROPERTY_TIME_TO_IDLE, 10000L );
268 _lDefaultTimeToLive = AppPropertiesService.getPropertyLong( PREFIX_DEFAULT + PROPERTY_TIME_TO_LIVE, 10000L );
269 _bDefaultOverflowToDisk = AppPropertiesService.getPropertyBoolean( PREFIX_DEFAULT + PROPERTY_OVERFLOW_TO_DISK, true );
270 _bDefaultDiskPersistent = AppPropertiesService.getPropertyBoolean( PREFIX_DEFAULT + PROPERTY_DISK_PERSISTENT, true );
271 _lDefaultDiskExpiry = AppPropertiesService.getPropertyLong( PREFIX_DEFAULT + PROPERTY_DISK_EXPIRY, 120L );
272 _nDefaultMaxElementsOnDisk = AppPropertiesService.getPropertyInt( PREFIX_DEFAULT + PROPERTY_MAX_ELEMENTS_DISK, 10000 );
273 _bDefaultStatistics = AppPropertiesService.getPropertyBoolean( PREFIX_DEFAULT + PROPERTY_STATISTICS, false );
274 }
275
276
277
278
279 private void loadCachesConfig( )
280 {
281 String strCachesStatusFile = AppPathService.getPath( PROPERTY_PATH_CONF, FILE_CACHES_STATUS );
282 File file = new File( strCachesStatusFile );
283
284 try ( FileInputStream fis = new FileInputStream( file ) )
285 {
286 Properties properties = new Properties( );
287 properties.load( fis );
288
289
290 for ( String strKey : properties.stringPropertyNames( ) )
291 {
292 String strDSKey = KEY_PREFIX + strKey;
293
294 if ( !DatastoreService.existsInstanceKey( strDSKey ) )
295 {
296 String strValue = properties.getProperty( strKey );
297 DatastoreService.setInstanceDataValue( strDSKey, strValue );
298 }
299 }
300 }
301 catch( FileNotFoundException e )
302 {
303 AppLogService.error( "No cache.dat file. Should be created at shutdown." );
304 }
305 catch( Exception e )
306 {
307 AppLogService.error( "Error loading caches status defined in file : {}", file.getAbsolutePath( ), e );
308 }
309 }
310
311
312
313
314
315
316
317 public static void updateCacheStatus( CacheableService cs )
318 {
319 String strKey = getDSKey( cs.getName( ), PROPERTY_IS_ENABLED );
320 DatastoreService.setInstanceDataValue( strKey, ( cs.isCacheEnable( ) ? ENABLED : DISABLED ) );
321 }
322
323
324
325
326
327
328
329
330 private static boolean getStatus( CacheableService cs )
331 {
332 String strEnabled = DatastoreService.getInstanceDataValue( getDSKey( cs.getName( ), PROPERTY_IS_ENABLED ), DISABLED );
333
334 return strEnabled.equals( ENABLED );
335 }
336
337
338
339
340
341
342
343
344
345
346 private static String getDSKey( String strCacheName, String strProperty )
347 {
348 return KEY_PREFIX + normalizeName( strCacheName ) + strProperty;
349 }
350
351
352
353
354
355
356
357
358 private static String normalizeName( String strName )
359 {
360 return strName.replace( " ", "" );
361 }
362
363
364
365
366
367
368
369
370 private CacheConfiguration getCacheConfiguration( String strCacheName )
371 {
372 CacheConfiguration config = new CacheConfiguration( );
373 config.setName( strCacheName );
374 config.setMaxElementsInMemory( getIntProperty( strCacheName, PROPERTY_MAX_ELEMENTS, _nDefaultMaxElementsInMemory ) );
375 config.setEternal( getBooleanProperty( strCacheName, PROPERTY_ETERNAL, _bDefaultEternal ) );
376 config.setTimeToIdleSeconds( getLongProperty( strCacheName, PROPERTY_TIME_TO_IDLE, _lDefaultTimeToIdle ) );
377 config.setTimeToLiveSeconds( getLongProperty( strCacheName, PROPERTY_TIME_TO_LIVE, _lDefaultTimeToLive ) );
378 config.setOverflowToDisk( getBooleanProperty( strCacheName, PROPERTY_OVERFLOW_TO_DISK, _bDefaultOverflowToDisk ) );
379 config.setDiskPersistent( getBooleanProperty( strCacheName, PROPERTY_DISK_PERSISTENT, _bDefaultDiskPersistent ) );
380 config.setDiskExpiryThreadIntervalSeconds( getLongProperty( strCacheName, PROPERTY_DISK_EXPIRY, _lDefaultDiskExpiry ) );
381 config.setMaxElementsOnDisk( getIntProperty( strCacheName, PROPERTY_MAX_ELEMENTS_DISK, _nDefaultMaxElementsOnDisk ) );
382 config.setStatistics( getBooleanProperty( strCacheName, PROPERTY_STATISTICS, _bDefaultStatistics ) );
383
384 return config;
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398 private int getIntProperty( String strCacheName, String strProperty, int nDefault )
399 {
400 String strKey = getDSKey( strCacheName, strProperty );
401
402 if ( DatastoreService.existsInstanceKey( strKey ) )
403 {
404 String strValue = NOT_FOUND;
405
406 try
407 {
408 strValue = DatastoreService.getInstanceDataValue( strKey, strValue );
409
410 return Integer.parseInt( strValue );
411 }
412 catch( NumberFormatException e )
413 {
414 AppLogService.error( ERROR_NUMERIC_PROP, strCacheName, strProperty, strValue, e );
415 }
416 }
417
418 return nDefault;
419 }
420
421
422
423
424
425
426
427
428
429
430
431
432 private long getLongProperty( String strCacheName, String strProperty, long lDefault )
433 {
434 String strKey = getDSKey( strCacheName, strProperty );
435
436 if ( DatastoreService.existsInstanceKey( strKey ) )
437 {
438 String strValue = NOT_FOUND;
439
440 try
441 {
442 strValue = DatastoreService.getInstanceDataValue( strKey, strValue );
443
444 return Integer.parseInt( strValue );
445 }
446 catch( NumberFormatException e )
447 {
448 AppLogService.error( ERROR_NUMERIC_PROP, strCacheName, strProperty, strValue, e );
449 }
450 }
451
452 return lDefault;
453 }
454
455
456
457
458
459
460
461
462
463
464
465
466 private boolean getBooleanProperty( String strCacheName, String strProperty, boolean bDefault )
467 {
468 String strKey = getDSKey( strCacheName, strProperty );
469
470 if ( DatastoreService.existsInstanceKey( strKey ) )
471 {
472 String strValue = DatastoreService.getInstanceDataValue( strKey, NOT_FOUND );
473
474 return ( strValue.equalsIgnoreCase( TRUE ) );
475 }
476
477 return bDefault;
478 }
479 }