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.daemon;
35
36 import fr.paris.lutece.portal.service.datastore.DatastoreService;
37 import fr.paris.lutece.portal.service.init.LuteceInitException;
38 import fr.paris.lutece.portal.service.util.AppLogService;
39 import fr.paris.lutece.portal.service.util.AppPropertiesService;
40
41 import java.util.Collection;
42 import java.util.HashMap;
43 import java.util.Map;
44 import java.util.Random;
45 import java.util.concurrent.ConcurrentHashMap;
46 import java.util.concurrent.Executors;
47 import java.util.concurrent.ScheduledFuture;
48 import java.util.concurrent.ScheduledThreadPoolExecutor;
49 import java.util.concurrent.TimeUnit;
50
51
52
53
54
55 public final class AppDaemonService
56 {
57 private static final String PROPERTY_MAX_INITIAL_START_DELAY = "daemon.maxInitialStartDelay";
58 private static final String PROPERTY_MAX_AWAIT_TERMINATION_DELAY = "daemon.maxAwaitTerminationDelay";
59 private static final String PROPERTY_SCHEDULED_THREAD_CORE_POOL_SIZE = "daemon.ScheduledThreadCorePoolSize";
60 private static final String PROPERTY_DAEMON_ON_STARTUP = ".onStartUp";
61 private static final String PROPERTY_DAEMON_INTERVAL = ".interval";
62 private static final String KEY_DAEMON_PREFIX = "core.daemon.";
63 private static final int MAX_INITIAL_START_DELAY = AppPropertiesService.getPropertyInt( PROPERTY_MAX_INITIAL_START_DELAY,
64 30 );
65 private static final int MAX_AWAIT_TERMINATION_DELAY = AppPropertiesService.getPropertyInt( PROPERTY_MAX_AWAIT_TERMINATION_DELAY,
66 15 );
67 private static final int DAEMON_CORE_POOL_SIZE = AppPropertiesService.getPropertyInt( PROPERTY_SCHEDULED_THREAD_CORE_POOL_SIZE,
68 30 );
69 private static final Map<String, DaemonEntry> _mapDaemonEntries = new HashMap<String, DaemonEntry>( );
70 private static final ScheduledThreadPoolExecutor _scheduler = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool( DAEMON_CORE_POOL_SIZE,
71 new DaemonThreadFactory( ) );
72 private static ConcurrentHashMap<String, ScheduledFuture<?>> _lRunningThread = new ConcurrentHashMap<String, ScheduledFuture<?>>( );
73 private static final Random _random = new Random( );
74 private static boolean _bInit;
75
76
77 private AppDaemonService( )
78 {
79 }
80
81
82
83
84
85
86
87 public static synchronized void init( ) throws LuteceInitException
88 {
89
90 if ( _bInit )
91 {
92 return;
93 }
94
95 if ( _mapDaemonEntries.size( ) > 0 )
96 {
97
98 int nInitialDaemon = 0;
99
100 for ( DaemonEntry entry : _mapDaemonEntries.values( ) )
101 {
102 if ( entry.onStartup( ) )
103 {
104 nInitialDaemon++;
105 }
106 }
107
108 int nDelay = MAX_INITIAL_START_DELAY;
109
110 if ( nInitialDaemon > 0 )
111 {
112 nDelay = MAX_INITIAL_START_DELAY / nInitialDaemon;
113 }
114
115 int nInitialDelay = 0;
116
117
118 for ( DaemonEntry entry : _mapDaemonEntries.values( ) )
119 {
120
121 if ( entry.onStartup( ) )
122 {
123 nInitialDelay += nDelay;
124
125 scheduleThread( entry, nInitialDelay );
126 }
127 }
128 }
129
130 _bInit = true;
131 }
132
133
134
135
136
137
138 public static void registerDaemon( DaemonEntry entry )
139 throws LuteceInitException
140 {
141 String strIntervalKey = getIntervalKey( entry.getId( ) );
142 String strIntervalKeyDefaultValue = null;
143
144
145 if ( !DatastoreService.existsInstanceKey( strIntervalKey ) )
146 {
147 strIntervalKeyDefaultValue = AppPropertiesService.getProperty( "daemon." + entry.getId( ) + ".interval",
148 "10" );
149 DatastoreService.setInstanceDataValue( strIntervalKey, strIntervalKeyDefaultValue );
150 }
151
152 String strIntervalKeyValue = DatastoreService.getInstanceDataValue( strIntervalKey, strIntervalKeyDefaultValue );
153
154 long lInterval = Long.valueOf( strIntervalKeyValue );
155
156 String strOnStartupKey = getOnStartupKey( entry.getId( ) );
157 String strOnStartupDefaultValue = null;
158
159
160 if ( !DatastoreService.existsInstanceKey( strOnStartupKey ) )
161 {
162 strOnStartupDefaultValue = AppPropertiesService.getProperty( "daemon." + entry.getId( ) + ".onstartup", "0" )
163 .equals( "1" ) ? DatastoreService.VALUE_TRUE
164 : DatastoreService.VALUE_FALSE;
165 DatastoreService.setInstanceDataValue( strOnStartupKey, strOnStartupDefaultValue );
166 }
167
168 String strOnStarupvalue = DatastoreService.getInstanceDataValue( strOnStartupKey, strOnStartupDefaultValue );
169 boolean bOnStartup = Boolean.valueOf( strOnStarupvalue );
170
171 entry.setInterval( lInterval );
172 entry.setOnStartUp( bOnStartup );
173
174 try
175 {
176 entry.loadDaemon( );
177 }
178 catch ( ClassNotFoundException e )
179 {
180 throw new LuteceInitException( "Couldn't instantiate daemon: " + entry.getId( ), e );
181 }
182 catch ( InstantiationException e )
183 {
184 throw new LuteceInitException( "Couldn't instantiate daemon: " + entry.getId( ), e );
185 }
186 catch ( IllegalAccessException e )
187 {
188 throw new LuteceInitException( "Couldn't instantiate daemon: " + entry.getId( ), e );
189 }
190
191
192 if ( entry.getPluginName( ) != null )
193 {
194 entry.getDaemon( ).setPluginName( entry.getPluginName( ) );
195 }
196
197 _mapDaemonEntries.put( entry.getId( ), entry );
198
199 AppLogService.info( "New Daemon registered : " + entry.getId( ) );
200 }
201
202
203
204
205
206 public static void unregisterDaemon( String strDaemonKey )
207 {
208 unScheduleThread( _mapDaemonEntries.get( strDaemonKey ) );
209 _mapDaemonEntries.remove( strDaemonKey );
210 }
211
212
213
214
215
216 public static void startDaemon( String strDaemonKey )
217 {
218 scheduleThread( _mapDaemonEntries.get( strDaemonKey ) );
219 }
220
221
222
223
224
225 public static void stopDaemon( String strDaemonKey )
226 {
227 unScheduleThread( _mapDaemonEntries.get( strDaemonKey ) );
228 }
229
230
231
232
233
234
235 public static void modifyDaemonInterval( String strDaemonKey, String strDaemonInterval )
236 {
237 DaemonEntry entry = _mapDaemonEntries.get( strDaemonKey );
238
239 if ( entry != null )
240 {
241 entry.setInterval( new Long( strDaemonInterval ) );
242 DatastoreService.setInstanceDataValue( getIntervalKey( entry.getId( ) ), strDaemonInterval );
243 }
244 }
245
246
247
248
249
250 private static void scheduleThread( DaemonEntry entry )
251 {
252 scheduleThread( entry, _random.nextInt( MAX_INITIAL_START_DELAY ) );
253 }
254
255
256
257
258
259
260 private static void scheduleThread( DaemonEntry entry, int nInitialDelay )
261 {
262 ScheduledFuture<?> result = _lRunningThread.get( entry.getId( ) );
263
264 if ( result == null )
265 {
266 ScheduledFuture<?> task = _scheduler.scheduleAtFixedRate( entry.getDaemonThread( ), nInitialDelay,
267 entry.getInterval( ), TimeUnit.SECONDS );
268
269 _lRunningThread.putIfAbsent( entry.getId( ), task );
270 AppLogService.info( "Starting daemon '" + entry.getId( ) + "'" );
271 }
272
273 entry.setIsRunning( true );
274
275 DatastoreService.setInstanceDataValue( getOnStartupKey( entry.getId( ) ), DatastoreService.VALUE_TRUE );
276 }
277
278
279
280
281
282 private static void unScheduleThread( DaemonEntry entry )
283 {
284 cancelScheduledThread( entry.getId( ) );
285 entry.setIsRunning( false );
286
287 DatastoreService.setInstanceDataValue( getOnStartupKey( entry.getId( ) ), DatastoreService.VALUE_FALSE );
288 AppLogService.info( "Stopping daemon '" + entry.getId( ) + "'" );
289 }
290
291
292
293
294
295 protected static void cancelScheduledThread( String strEntryId )
296 {
297 ScheduledFuture<?> task = _lRunningThread.get( strEntryId );
298
299 if ( task != null )
300 {
301 task.cancel( false );
302 _scheduler.remove( (Runnable) task );
303 _scheduler.purge( );
304 _lRunningThread.remove( strEntryId );
305 }
306 }
307
308
309
310
311
312
313 public static Collection<DaemonEntry> getDaemonEntries( )
314 {
315 return _mapDaemonEntries.values( );
316 }
317
318
319
320
321 public static void shutdown( )
322 {
323 AppLogService.info(
324 "Lutece daemons scheduler stop requested : trying to terminate gracefully daemons list (max wait " +
325 MAX_AWAIT_TERMINATION_DELAY + " s)." );
326 _scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy( false );
327 _scheduler.setContinueExistingPeriodicTasksAfterShutdownPolicy( false );
328 _scheduler.shutdown( );
329
330 try
331 {
332 if ( _scheduler.awaitTermination( MAX_AWAIT_TERMINATION_DELAY, TimeUnit.SECONDS ) )
333 {
334 AppLogService.info( "All daemons shutdown successfully." );
335 }
336 else
337 {
338 AppLogService.info( _scheduler.getActiveCount( ) +
339 " daemons still running, trying to interrupt them..." );
340 _scheduler.shutdownNow( );
341
342 if ( _scheduler.awaitTermination( 1, TimeUnit.SECONDS ) )
343 {
344 AppLogService.info( "All running daemons successfully interrupted." );
345 }
346 else
347 {
348 AppLogService.error( "Interrupt failed : " + _scheduler.getActiveCount( ) +
349 " daemons still running." );
350 }
351 }
352 }
353 catch ( InterruptedException e )
354 {
355 AppLogService.error( "Error during waiting for daemons termination", e );
356 }
357 finally
358 {
359 _scheduler.purge( );
360 }
361 }
362
363
364
365
366
367
368
369 public static Daemon getDaemon( String strDaemonKey )
370 {
371 DaemonEntry entry = _mapDaemonEntries.get( strDaemonKey );
372
373 return entry.getDaemon( );
374 }
375
376
377
378
379
380
381 private static String getOnStartupKey( String strDaemonKey )
382 {
383 return KEY_DAEMON_PREFIX + strDaemonKey + PROPERTY_DAEMON_ON_STARTUP;
384 }
385
386
387
388
389
390
391 private static String getIntervalKey( String strDaemonKey )
392 {
393 return KEY_DAEMON_PREFIX + strDaemonKey + PROPERTY_DAEMON_INTERVAL;
394 }
395 }