View Javadoc

1   package com.atlassian.plugin.osgi.spring;
2   
3   import org.slf4j.Logger;
4   import org.slf4j.LoggerFactory;
5   import org.springframework.core.task.AsyncTaskExecutor;
6   
7   import java.util.concurrent.ExecutorService;
8   import java.util.concurrent.Executors;
9   import java.util.concurrent.ThreadFactory;
10  import java.util.concurrent.TimeUnit;
11  import java.util.concurrent.atomic.AtomicInteger;
12  
13  /**
14   * Executes spring tasks using a cached thread pool that expands as necessary.  Overrides the default Spring executor
15   * that spawns a new thread for every application context creation.
16   *
17   * @since 2.5.0
18   */
19  public class ThreadPoolAsyncTaskExecutor implements AsyncTaskExecutor
20  {
21      private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory());
22  
23      /**
24       * Executes the runnable
25       * @param task The runnable task
26       * @param startTimeout The start timeout (ignored)
27       */
28      public void execute(Runnable task, long startTimeout)
29      {
30          // yes, we ignore the start timeout
31          executor.execute(task);
32      }
33  
34      /**
35       * Executes the runnable
36       * @param task The runnable task
37       */
38      public void execute(Runnable task)
39      {
40          this.execute(task, -1);
41      }
42  
43      /**
44       * Shuts down the internal {@code ExecutorService} to ensure that all threads are stopped in order to allow the JVM
45       * to terminate cleanly in a timely fashion.
46       */
47      public void shutdown()
48      {
49          Logger log = LoggerFactory.getLogger(getClass());
50          log.debug("Attempting to shutdown ExecutorService");
51  
52          executor.shutdown();
53          try
54          {
55              if (executor.awaitTermination(5, TimeUnit.SECONDS))
56              {
57                  log.debug("ExecutorService has shutdown gracefully");
58              }
59              else
60              {
61                  //The executor did not shutdown within the timeout. We can't wait forever, though, so issue a
62                  //shutdownNow() and give it another 5 seconds
63                  log.warn("ExecutorService did not shutdown within the timeout; forcing shutdown");
64  
65                  executor.shutdownNow();
66                  if (executor.awaitTermination(5, TimeUnit.SECONDS))
67                  {
68                      //The forced shutdown has brought the executor down. Not ideal, but acceptable
69                      log.debug("ExecutorService has been forced to shutdown");
70                  }
71                  else
72                  {
73                      //We can't delay execution indefinitely waiting, so log a warning. The JVM may not shut down
74                      //if this service does not stop (because it uses non-daemon threads), so this may be helpful
75                      //in debugging should that happen.
76                      log.warn("ExecutorService did not shutdown; it will be abandoned");
77                  }
78              }
79          }
80          catch (InterruptedException e)
81          {
82              log.warn("Interrupted while waiting for the executor service to shutdown; some worker threads may " +
83                      "still be running");
84              Thread.currentThread().interrupt();
85          }
86      }
87  
88      /**
89       * Thread factory that names the threads for the executor
90       */
91      private static class NamedThreadFactory implements ThreadFactory
92      {
93          private final AtomicInteger counter = new AtomicInteger();
94          public Thread newThread(Runnable r)
95          {
96              Thread thread = new Thread(r);
97              thread.setDaemon(false);
98              thread.setName("ThreadPoolAsyncTaskExecutor::Thread " + counter.incrementAndGet());
99              return thread;
100         }
101     }
102 }