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