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 static final Logger log = LoggerFactory.getLogger(ThreadPoolAsyncTaskExecutor.class);
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      public void execute(Runnable task, long startTimeout)
31      {
32          // yes, we ignore the start timeout
33          executor.execute(task);
34      }
35  
36      /**
37       * Executes the runnable
38       * @param task The runnable task
39       */
40      public void execute(Runnable task)
41      {
42          this.execute(task, -1);
43      }
44  
45      /**
46       * Shuts down the internal {@code ExecutorService} to ensure that all threads are stopped in order to allow the JVM
47       * to terminate cleanly in a timely fashion.
48       */
49      public void shutdown()
50      {
51          log.debug("Attempting to shutdown ExecutorService");
52  
53          executor.shutdown();
54          try
55          {
56              if (executor.awaitTermination(5, TimeUnit.SECONDS))
57              {
58                  log.debug("ExecutorService has shutdown gracefully");
59              }
60              else
61              {
62                  //The executor did not shutdown within the timeout. We can't wait forever, though, so issue a
63                  //shutdownNow() and give it another 5 seconds
64                  log.warn("ExecutorService did not shutdown within the timeout; forcing shutdown");
65  
66                  executor.shutdownNow();
67                  if (executor.awaitTermination(5, TimeUnit.SECONDS))
68                  {
69                      //The forced shutdown has brought the executor down. Not ideal, but acceptable
70                      log.debug("ExecutorService has been forced to shutdown");
71                  }
72                  else
73                  {
74                      //We can't delay execution indefinitely waiting, so log a warning. The JVM may not shut down
75                      //if this service does not stop (because it uses non-daemon threads), so this may be helpful
76                      //in debugging should that happen.
77                      log.warn("ExecutorService did not shutdown; it will be abandoned");
78                  }
79              }
80          }
81          catch (InterruptedException e)
82          {
83              log.warn("Interrupted while waiting for the executor service to shutdown; some worker threads may " +
84                      "still be running");
85              Thread.currentThread().interrupt();
86          }
87      }
88  
89      /**
90       * Thread factory that names the threads for the executor
91       */
92      private static class NamedThreadFactory implements ThreadFactory
93      {
94          private final AtomicInteger counter = new AtomicInteger();
95          public Thread newThread(Runnable r)
96          {
97              Thread thread = new Thread(r);
98              thread.setDaemon(false);
99              thread.setName("ThreadPoolAsyncTaskExecutor::Thread " + counter.incrementAndGet());
100             return thread;
101         }
102     }
103 }