View Javadoc
1   /*
2    * Copyright (c) 2002-2022, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.portal.service.daemon;
35  
36  import java.time.Duration;
37  import java.time.Instant;
38  import java.util.List;
39  import java.util.concurrent.AbstractExecutorService;
40  import java.util.concurrent.BlockingQueue;
41  import java.util.concurrent.BrokenBarrierException;
42  import java.util.concurrent.ExecutorService;
43  import java.util.concurrent.Executors;
44  import java.util.concurrent.ForkJoinPool;
45  import java.util.concurrent.LinkedBlockingQueue;
46  import java.util.concurrent.TimeUnit;
47  import java.util.concurrent.TimeoutException;
48  import java.util.function.Consumer;
49  
50  import fr.paris.lutece.test.LuteceTestCase;
51  
52  public class DaemonSchedulerTest extends LuteceTestCase
53  {
54      private static final class TestExecutorService extends AbstractExecutorService
55      {
56          private final Consumer<Runnable> _executor;
57  
58          public TestExecutorService( Consumer<Runnable> executor )
59          {
60              _executor = executor;
61          }
62  
63          @Override
64          public void execute( Runnable command )
65          {
66              _executor.accept( command );
67          }
68  
69          @Override
70          public List<Runnable> shutdownNow( )
71          {
72              return null;
73          }
74  
75          @Override
76          public void shutdown( )
77          {
78          }
79  
80          @Override
81          public boolean isTerminated( )
82          {
83              return false;
84          }
85  
86          @Override
87          public boolean isShutdown( )
88          {
89              return false;
90          }
91  
92          @Override
93          public boolean awaitTermination( long timeout, TimeUnit unit ) throws InterruptedException
94          {
95              return true;
96          }
97      }
98  
99      public void testEnqueue( )
100             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
101     {
102         testEnqueue( false );
103     }
104 
105     public void testEnqueueDaemonThrows( )
106             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
107     {
108         testEnqueue( true );
109     }
110 
111     /**
112      * test if the enqueue service does not fail when a daemon throws a RunTimeException
113      *
114      * @throws ClassNotFoundException
115      * @throws InstantiationException
116      * @throws IllegalAccessException
117      * @throws InterruptedException
118      * @throws BrokenBarrierException
119      * @throws TimeoutException
120      */
121     private void testEnqueue( boolean shouldThrow )
122             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
123     {
124         String strMethodName = new Object( )
125         {
126         }.getClass( ).getEnclosingMethod( ).getName( );
127         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
128         ExecutorService executor = Executors.newSingleThreadExecutor( );
129         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
130         try
131         {
132             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
133             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
134             testDaemon.setRunThrows( shouldThrow );
135             scheduler.enqueue( entry, 0L, TimeUnit.MILLISECONDS );
136             assertFalse( testDaemon.hasRun( ) );
137             testDaemon.go( 250L, TimeUnit.MILLISECONDS );
138             testDaemon.waitForCompletion( );
139             assertTrue( testDaemon.hasRun( ) );
140         }
141         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
142         {
143             fail( e.getMessage( ) );
144         }
145         finally
146         {
147             scheduler.shutdown( );
148         }
149     }
150 
151     public void testEnqueueDelay( )
152             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
153     {
154         testEnqueueDelay( false );
155     }
156 
157     public void testEnqueueDelayDaemonThrows( )
158             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
159     {
160         testEnqueueDelay( true );
161     }
162 
163     /**
164      * test if the enqueue delay service does not fail when a daemon throws a RunTimeException
165      *
166      * @throws ClassNotFoundException
167      * @throws InstantiationException
168      * @throws IllegalAccessException
169      * @throws InterruptedException
170      * @throws BrokenBarrierException
171      * @throws TimeoutException
172      */
173     private void testEnqueueDelay( boolean shouldThrow )
174             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
175     {
176         String strMethodName = new Object( )
177         {
178         }.getClass( ).getEnclosingMethod( ).getName( );
179         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
180         ExecutorService executor = Executors.newSingleThreadExecutor( );
181         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
182         try
183         {
184             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
185             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
186             testDaemon.setRunThrows( shouldThrow );
187             Instant start = Instant.now( );
188             assertTrue( scheduler.enqueue( entry, 500L, TimeUnit.MILLISECONDS ) );
189             assertFalse( testDaemon.hasRun( ) );
190             testDaemon.go( );
191             assertTrue( 500L <= Duration.between( start, Instant.now( ) ).toMillis( ) );
192             testDaemon.waitForCompletion( );
193             assertTrue( testDaemon.hasRun( ) );
194         }
195         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
196         {
197             fail( e.getMessage( ) );
198         }
199         finally
200         {
201             scheduler.shutdown( );
202         }
203     }
204 
205     /**
206      * test if the enqueue delay illegalstate exception service does not fail when a daemon throws a RunTimeException
207      *
208      * @throws ClassNotFoundException
209      * @throws InstantiationException
210      * @throws IllegalAccessException
211      * @throws InterruptedException
212      * @throws BrokenBarrierException
213      * @throws TimeoutException
214      */
215     public void testEnqueueDelayIllegalState( )
216             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
217     {
218         String strMethodName = new Object( )
219         {
220         }.getClass( ).getEnclosingMethod( ).getName( );
221         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
222         ExecutorService executor = Executors.newSingleThreadExecutor( );
223         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
224         scheduler.shutdown( );
225         try
226         {
227             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName );
228             scheduler.enqueue( entry, 500L, TimeUnit.MILLISECONDS );
229             fail( "Should not be able to enqueue after shutdown" );
230         }
231         catch( IllegalStateException e )
232         {
233             // ok
234         }
235         finally
236         {
237             scheduler.shutdown( );
238         }
239     }
240 
241     private DaemonEntry getDaemonEntry( String name ) throws ClassNotFoundException, InstantiationException, IllegalAccessException
242     {
243         DaemonEntry entry = new DaemonEntry( );
244         entry.setId( name );
245         entry.setIsRunning( true );
246         entry.setPluginName( "core" );
247         entry.setClassName( TestDaemon.class.getName( ) );
248         entry.loadDaemon( );
249         entry.setInterval( 1 );
250         TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
251         testDaemon.setPluginName( "core" );
252         return entry;
253     }
254 
255     public void testEnqueueFull( )
256             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
257     {
258         testEnqueueFull( false );
259     }
260 
261     public void testEnqueueFullDaemonThrows( )
262             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
263     {
264         testEnqueueFull( true );
265     }
266 
267     /**
268      * test if the enqueue full service does not fail when a daemon throws a RunTimeException
269      *
270      * @throws ClassNotFoundException
271      * @throws InstantiationException
272      * @throws IllegalAccessException
273      * @throws InterruptedException
274      * @throws BrokenBarrierException
275      * @throws TimeoutException
276      */
277     private void testEnqueueFull( boolean shouldThrow )
278             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
279     {
280         String strMethodName = new Object( )
281         {
282         }.getClass( ).getEnclosingMethod( ).getName( );
283         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( 1 );
284         ExecutorService executor = new TestExecutorService( runnable -> runnable.run( ) );
285         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
286         try
287         {
288             DaemonEntry executing = getDaemonEntry( "JUNIT-executing" + strMethodName + shouldThrow );
289             TestDaemon./../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon executingDaemon = (TestDaemon) executing.getDaemon( );
290             executingDaemon.setRunThrows( shouldThrow );
291             assertTrue( scheduler.enqueue( executing, 0L, TimeUnit.MILLISECONDS ) );
292             executingDaemon.go( );
293             DaemonEntry inqueue = getDaemonEntry( "JUNIT-inqueue" );
294             TestDaemon/../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon inqueueDaemon = (TestDaemon) inqueue.getDaemon( );
295             inqueueDaemon.setRunThrows( shouldThrow );
296             assertTrue( scheduler.enqueue( inqueue, 0L, TimeUnit.MILLISECONDS ) );
297             DaemonEntry refused = getDaemonEntry( "JUNIT-refused" );
298             assertFalse( scheduler.enqueue( refused, 0L, TimeUnit.MILLISECONDS ) );
299             executingDaemon.waitForCompletion( );
300             inqueueDaemon.go( );
301             inqueueDaemon.waitForCompletion( );
302         }
303         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
304         {
305             fail( e.getMessage( ) );
306         }
307         finally
308         {
309             scheduler.shutdown( );
310         }
311     }
312 
313     public void testSchedule( )
314             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
315     {
316         testSchedule( false );
317     }
318 
319     public void testSchedulDaemonThrows( )
320             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
321     {
322         testSchedule( true );
323     }
324 
325     /**
326      * test if the schedule service does not fail when a daemon throws a RunTimeException
327      *
328      * @throws ClassNotFoundException
329      * @throws InstantiationException
330      * @throws IllegalAccessException
331      * @throws InterruptedException
332      * @throws BrokenBarrierException
333      * @throws TimeoutException
334      */
335     private void testSchedule( boolean shouldThrow )
336             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
337     {
338         String strMethodName = new Object( )
339         {
340         }.getClass( ).getEnclosingMethod( ).getName( );
341         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
342         ExecutorService executor = Executors.newSingleThreadExecutor( );
343         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
344         try
345         {
346             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
347             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
348             testDaemon.setRunThrows( shouldThrow );
349             Instant start = Instant.now( );
350             scheduler.schedule( entry, 0L, TimeUnit.MILLISECONDS );
351             assertFalse( testDaemon.hasRun( ) );
352             testDaemon.go( );
353             System.out.println( "Daemon took " + Duration.between( start, Instant.now( ) ).toNanos( ) + "ns to execute" );
354             testDaemon.waitForCompletion( );
355             assertTrue( testDaemon.hasRun( ) );
356             testDaemon.go( );
357             assertTrue( 1000L <= Duration.between( start, Instant.now( ) ).toMillis( ) );
358             testDaemon.waitForCompletion( );
359             assertTrue( testDaemon.hasRun( ) );
360         }
361         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
362         {
363             fail( e.getMessage( ) );
364         }
365         finally
366         {
367             scheduler.shutdown( );
368         }
369     }
370 
371     public void testScheduleDelay( )
372             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
373     {
374         testScheduleDelay( false );
375     }
376 
377     public void testScheduleDelayDaemonThrows( )
378             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
379     {
380         testScheduleDelay( true );
381     }
382 
383     /**
384      * test if the schedule delay service does not fail when a daemon throws a RunTimeException
385      *
386      * @throws ClassNotFoundException
387      * @throws InstantiationException
388      * @throws IllegalAccessException
389      * @throws InterruptedException
390      * @throws BrokenBarrierException
391      * @throws TimeoutException
392      */
393     private void testScheduleDelay( boolean shouldThrow )
394             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
395     {
396         String strMethodName = new Object( )
397         {
398         }.getClass( ).getEnclosingMethod( ).getName( );
399         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
400         ExecutorService executor = Executors.newSingleThreadExecutor( );
401         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
402         try
403         {
404             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
405             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
406             testDaemon.setRunThrows( shouldThrow );
407             Instant start = Instant.now( );
408             scheduler.schedule( entry, 500L, TimeUnit.MILLISECONDS );
409             assertFalse( testDaemon.hasRun( ) );
410             testDaemon.go( );
411             assertTrue( 500L <= Duration.between( start, Instant.now( ) ).toMillis( ) );
412             testDaemon.waitForCompletion( );
413             assertTrue( testDaemon.hasRun( ) );
414         }
415         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
416         {
417             fail( e.getMessage( ) );
418         }
419         finally
420         {
421             scheduler.shutdown( );
422         }
423     }
424 
425     public void testScheduleTwice( )
426             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
427     {
428         testScheduleTwice( false );
429     }
430 
431     public void testScheduleTwiceDaemonThrows( )
432             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
433     {
434         testScheduleTwice( true );
435     }
436 
437     /**
438      * test if the schedule twice service does not fail when a daemon throws a RunTimeException
439      *
440      * @throws ClassNotFoundException
441      * @throws InstantiationException
442      * @throws IllegalAccessException
443      * @throws InterruptedException
444      * @throws BrokenBarrierException
445      * @throws TimeoutException
446      */
447     private void testScheduleTwice( boolean shouldThrow )
448             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
449     {
450         String strMethodName = new Object( )
451         {
452         }.getClass( ).getEnclosingMethod( ).getName( );
453         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
454         ExecutorService executor = Executors.newSingleThreadExecutor( );
455         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
456         try
457         {
458             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
459             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
460             testDaemon.setRunThrows( shouldThrow );
461             Instant start = Instant.now( );
462             scheduler.schedule( entry, 0L, TimeUnit.MICROSECONDS );
463             scheduler.schedule( entry, 500L, TimeUnit.MILLISECONDS );
464             assertFalse( testDaemon.hasRun( ) );
465             testDaemon.go( );
466             testDaemon.waitForCompletion( );
467             assertTrue( testDaemon.hasRun( ) );
468             testDaemon.go( );
469             long timeForSecondRun = Duration.between( start, Instant.now( ) ).toMillis( );
470             assertTrue( "Second run was " + timeForSecondRun + "ms after start", 1000L <= timeForSecondRun );
471             testDaemon.waitForCompletion( );
472             assertTrue( testDaemon.hasRun( ) );
473         }
474         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
475         {
476             fail( e.getMessage( ) );
477         }
478         finally
479         {
480             scheduler.shutdown( );
481         }
482     }
483 
484     /**
485      * test if the unschedule not scheduled service does not fail when a daemon throws a RunTimeException
486      *
487      * @throws ClassNotFoundException
488      * @throws InstantiationException
489      * @throws IllegalAccessException
490      * @throws InterruptedException
491      * @throws BrokenBarrierException
492      * @throws TimeoutException
493      */
494     public void testUnScheduleNotScheduled( ) throws ClassNotFoundException, InstantiationException, IllegalAccessException
495     {
496         String strMethodName = new Object( )
497         {
498         }.getClass( ).getEnclosingMethod( ).getName( );
499         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
500         ExecutorService executor = Executors.newSingleThreadExecutor( );
501         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
502         try
503         {
504             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName );
505             scheduler.unSchedule( entry );
506             // not sure how to assert something here
507         }
508         catch( Exception e )
509         {
510             fail( );
511         }
512         finally
513         {
514             scheduler.shutdown( );
515         }
516     }
517 
518     public void testUnSchedule( )
519             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
520     {
521         testUnSchedule( false );
522     }
523 
524     public void testUnScheduleDaemonThrows( )
525             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
526     {
527         testUnSchedule( true );
528     }
529 
530     /**
531      * test if the unschedule not scheduled service does not fail when a daemon throws a RunTimeException
532      *
533      * @throws ClassNotFoundException
534      * @throws InstantiationException
535      * @throws IllegalAccessException
536      * @throws InterruptedException
537      * @throws BrokenBarrierException
538      * @throws TimeoutException
539      */
540     private void testUnSchedule( boolean shouldThrow )
541             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
542     {
543         String strMethodName = new Object( )
544         {
545         }.getClass( ).getEnclosingMethod( ).getName( );
546         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
547         ExecutorService executor = Executors.newSingleThreadExecutor( );
548         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
549         try
550         {
551             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
552             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
553             testDaemon.setRunThrows( shouldThrow );
554             scheduler.schedule( entry, 0L, TimeUnit.MILLISECONDS );
555             assertFalse( testDaemon.hasRun( ) );
556             testDaemon.go( );
557             testDaemon.waitForCompletion( );
558             assertTrue( testDaemon.hasRun( ) );
559             assertEquals( 0, testDaemon.getStopCallNumber( ) );
560             Thread.sleep( 10L ); // leave some time to the daemon to exit executing daemons
561             scheduler.unSchedule( entry );
562             assertEquals( 1, testDaemon.getStopCallNumber( ) );
563             try
564             {
565                 testDaemon.go( 1100L, TimeUnit.MILLISECONDS );
566                 fail( "Daemon executed after unscheduling" );
567             }
568             catch( TimeoutException e )
569             {
570                 // OK
571             }
572         }
573         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
574         {
575             fail( e.getMessage( ) );
576         }
577         finally
578         {
579             scheduler.shutdown( );
580         }
581     }
582 
583     public void testUnScheduleWhileRunning( )
584             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
585     {
586         testUnScheduleWhileRunning( false );
587     }
588 
589     public void testUnScheduleWhileRunningDaemonThrows( )
590             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
591     {
592         testUnScheduleWhileRunning( true );
593     }
594 
595     /**
596      * test if the unschedule not scheduled service does not fail when a daemon throws a RunTimeException
597      *
598      * @throws ClassNotFoundException
599      * @throws InstantiationException
600      * @throws IllegalAccessException
601      * @throws InterruptedException
602      * @throws BrokenBarrierException
603      * @throws TimeoutException
604      */
605     private void testUnScheduleWhileRunning( boolean shouldThrow )
606             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
607     {
608         String strMethodName = new Object( )
609         {
610         }.getClass( ).getEnclosingMethod( ).getName( );
611         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
612         ExecutorService executor = Executors.newSingleThreadExecutor( );
613         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
614         try
615         {
616             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
617             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
618             testDaemon.setRunThrows( shouldThrow );
619             scheduler.schedule( entry, 0L, TimeUnit.MILLISECONDS );
620             assertFalse( testDaemon.hasRun( ) );
621             testDaemon.go( );
622             assertEquals( 0, testDaemon.getStopCallNumber( ) );
623             // unschedule while the daemon is executing
624             scheduler.unSchedule( entry );
625             Thread.sleep( 10L ); // leave some time to the daemon
626             assertEquals( "Stopping should wait for execution end", 0, testDaemon.getStopCallNumber( ) );
627             testDaemon.waitForCompletion( );
628             assertTrue( testDaemon.hasRun( ) );
629             Thread.sleep( 10L ); // leave some time to the daemon to exit executing daemons
630             assertEquals( 1, testDaemon.getStopCallNumber( ) );
631             try
632             {
633                 testDaemon.go( 1100L, TimeUnit.MILLISECONDS );
634                 fail( "Daemon executed after unscheduling" );
635             }
636             catch( TimeoutException e )
637             {
638                 // OK
639             }
640         }
641         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
642         {
643             fail( e.getMessage( ) );
644         }
645         finally
646         {
647             scheduler.shutdown( );
648         }
649     }
650 
651     public void testEnqueueWhileRunning( )
652             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
653     {
654         testEnqueueWhileRunning( false );
655     }
656 
657     public void testEnqueueWhileRunningDaemonThrows( )
658             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
659     {
660         testEnqueueWhileRunning( true );
661     }
662 
663     /**
664      * test if the enqueue while running service does not fail when a daemon throws a RunTimeException
665      *
666      * @throws ClassNotFoundException
667      * @throws InstantiationException
668      * @throws IllegalAccessException
669      * @throws InterruptedException
670      * @throws BrokenBarrierException
671      * @throws TimeoutException
672      */
673     private void testEnqueueWhileRunning( boolean shouldThrow )
674             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
675     {
676         String strMethodName = new Object( )
677         {
678         }.getClass( ).getEnclosingMethod( ).getName( );
679         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
680         ExecutorService executor = Executors.newCachedThreadPool( );
681         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
682         try
683         {
684             DaemonEntry executing = getDaemonEntry( "JUNIT-executing" + strMethodName + shouldThrow );
685             TestDaemon./../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon executingDaemon = (TestDaemon) executing.getDaemon( );
686             executingDaemon.setRunThrows( shouldThrow );
687             assertTrue( scheduler.enqueue( executing, 0L, TimeUnit.MILLISECONDS ) );
688             executingDaemon.go( );
689             assertTrue( scheduler.enqueue( executing, 0L, TimeUnit.MILLISECONDS ) );
690             try
691             {
692                 executingDaemon.go( 200, TimeUnit.MILLISECONDS );
693                 fail( "Daemon started execution while already running" );
694             }
695             catch( TimeoutException e )
696             {
697                 executingDaemon.resetGo( );
698             }
699             executingDaemon.waitForCompletion( );
700             executingDaemon.go( );
701             executingDaemon.waitForCompletion( );
702         }
703         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
704         {
705             fail( e.getMessage( ) );
706         }
707         finally
708         {
709             scheduler.shutdown( );
710         }
711     }
712 
713     public void testEnqueueCoalesce( )
714             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
715     {
716         testEnqueueCoalesce( false );
717     }
718 
719     public void testEnqueueCoalesceDaemonThrows( )
720             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
721     {
722         testEnqueueCoalesce( true );
723     }
724 
725     /**
726      * test if the enqueue coalesce service does not fail when a daemon throws a RunTimeException
727      *
728      * @throws ClassNotFoundException
729      * @throws InstantiationException
730      * @throws IllegalAccessException
731      * @throws InterruptedException
732      * @throws BrokenBarrierException
733      * @throws TimeoutException
734      */
735     private void testEnqueueCoalesce( boolean shouldThrow )
736             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
737     {
738         String strMethodName = new Object( )
739         {
740         }.getClass( ).getEnclosingMethod( ).getName( );
741         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
742         ExecutorService executor = new TestExecutorService( runnable -> runnable.run( ) );
743         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
744         try
745         {
746             DaemonEntry executing = getDaemonEntry( "JUNIT-executing" + strMethodName + shouldThrow );
747             TestDaemon./../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon executingDaemon = (TestDaemon) executing.getDaemon( );
748             executingDaemon.setRunThrows( shouldThrow );
749             assertTrue( scheduler.enqueue( executing, 0L, TimeUnit.MILLISECONDS ) );
750             executingDaemon.go( );
751             DaemonEntry inqueue = getDaemonEntry( "JUNIT-inqueue" );
752             TestDaemon/../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon inqueueDaemon = (TestDaemon) inqueue.getDaemon( );
753             inqueueDaemon.setRunThrows( shouldThrow );
754             assertTrue( scheduler.enqueue( inqueue, 0L, TimeUnit.MILLISECONDS ) );
755             assertTrue( scheduler.enqueue( inqueue, 0L, TimeUnit.MILLISECONDS ) );
756             executingDaemon.waitForCompletion( );
757             inqueueDaemon.go( );
758             inqueueDaemon.waitForCompletion( );
759             try
760             {
761                 inqueueDaemon.go( 200, TimeUnit.MILLISECONDS );
762                 fail( "Daemon started twice but should have been coalesced" );
763             }
764             catch( TimeoutException e )
765             {
766 
767             }
768         }
769         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
770         {
771             fail( e.getMessage( ) );
772         }
773         finally
774         {
775             scheduler.shutdown( );
776         }
777     }
778 
779     public void testShutdown( )
780             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
781     {
782         testShutdown( false );
783     }
784 
785     public void testShutdownThrows( )
786             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
787     {
788         testShutdown( true );
789     }
790 
791     /**
792      * test if the shutdown service does not fail when a daemon throws a RunTimeException
793      *
794      * @throws ClassNotFoundException
795      * @throws InstantiationException
796      * @throws IllegalAccessException
797      * @throws InterruptedException
798      * @throws BrokenBarrierException
799      * @throws TimeoutException
800      */
801     private void testShutdown( boolean shouldThrow )
802             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
803     {
804         String strMethodName = new Object( )
805         {
806         }.getClass( ).getEnclosingMethod( ).getName( );
807         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
808         ExecutorService executor = Executors.newCachedThreadPool( );
809         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
810         try
811         {
812             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
813             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
814             testDaemon.setStopThrows( shouldThrow );
815             scheduler.schedule( entry, 0L, TimeUnit.MILLISECONDS );
816             DaemonEntry entry2 = getDaemonEntry( "JUNIT2" );
817             TestDaemon../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon2 = (TestDaemon) entry2.getDaemon( );
818             testDaemon2.setStopThrows( shouldThrow );
819             scheduler.schedule( entry2, 0L, TimeUnit.MILLISECONDS );
820             assertFalse( testDaemon.hasRun( ) );
821             assertFalse( testDaemon2.hasRun( ) );
822             testDaemon.go( 250L, TimeUnit.MILLISECONDS );
823             testDaemon2.go( 250L, TimeUnit.MILLISECONDS );
824             testDaemon.waitForCompletion( );
825             testDaemon2.waitForCompletion( );
826             assertTrue( testDaemon.hasRun( ) );
827             assertTrue( testDaemon2.hasRun( ) );
828             Thread.sleep( 10L ); // leave some time to the daemons to exit
829                                  // executing daemons
830             scheduler.shutdown( );
831             assertEquals( 1, testDaemon.getStopCallNumber( ) );
832             assertEquals( 1, testDaemon2.getStopCallNumber( ) );
833         }
834         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
835         {
836             fail( e.getMessage( ) );
837         }
838         finally
839         {
840             scheduler.shutdown( );
841         }
842     }
843 
844     public void testShutdownWhileRunning( )
845             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
846     {
847         testShutdownWhileRunning( false );
848     }
849 
850     public void testShutdownWhileRunningThrows( )
851             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
852     {
853         testShutdownWhileRunning( true );
854     }
855 
856     /**
857      * test if the shutdown while running service does not fail when a daemon throws a RunTimeException
858      *
859      * @throws ClassNotFoundException
860      * @throws InstantiationException
861      * @throws IllegalAccessException
862      * @throws InterruptedException
863      * @throws BrokenBarrierException
864      * @throws TimeoutException
865      */
866     private void testShutdownWhileRunning( boolean shouldThrow )
867             throws ClassNotFoundException, InstantiationException, IllegalAccessException, InterruptedException, BrokenBarrierException, TimeoutException
868     {
869         String strMethodName = new Object( )
870         {
871         }.getClass( ).getEnclosingMethod( ).getName( );
872         BlockingQueue<DaemonEntry> queue = new LinkedBlockingQueue<>( );
873         ExecutorService executor = new TestExecutorService( runnable -> ForkJoinPool.commonPool( ).execute( runnable ) );
874         DaemonScheduler scheduler = new DaemonScheduler( queue, executor );
875         try
876         {
877             DaemonEntry entry = getDaemonEntry( "JUNIT" + strMethodName + shouldThrow );
878             TestDaemon/../../../fr/paris/lutece/portal/service/daemon/TestDaemon.html#TestDaemon">TestDaemon testDaemon = (TestDaemon) entry.getDaemon( );
879             testDaemon.setStopThrows( shouldThrow );
880             scheduler.schedule( entry, 0L, TimeUnit.MILLISECONDS );
881             assertFalse( testDaemon.hasRun( ) );
882             testDaemon.go( 250L, TimeUnit.MILLISECONDS );
883             scheduler.shutdown( );
884             assertEquals( 0, testDaemon.getStopCallNumber( ) );
885             testDaemon.waitForCompletion( );
886             assertTrue( testDaemon.hasRun( ) );
887             Thread.sleep( 10L ); // leave some time to the daemon to exit
888                                  // executing daemons
889             assertEquals( 1, testDaemon.getStopCallNumber( ) );
890         }
891         catch( InterruptedException | BrokenBarrierException | TimeoutException e )
892         {
893             fail( e.getMessage( ) );
894         }
895         finally
896         {
897             scheduler.shutdown( );
898         }
899     }
900 }