View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.work;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.config.MuleConfiguration;
12  import org.mule.api.config.ThreadingProfile;
13  import org.mule.api.context.MuleContextAware;
14  import org.mule.api.context.WorkManager;
15  import org.mule.api.work.WorkExecutor;
16  
17  import java.text.MessageFormat;
18  import java.util.List;
19  
20  import javax.resource.spi.XATerminator;
21  import javax.resource.spi.work.ExecutionContext;
22  import javax.resource.spi.work.Work;
23  import javax.resource.spi.work.WorkCompletedException;
24  import javax.resource.spi.work.WorkException;
25  import javax.resource.spi.work.WorkListener;
26  
27  import edu.emory.mathcs.backport.java.util.concurrent.Executor;
28  import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
29  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  
34  /**
35   * <code>MuleWorkManager</code> is a JCA Work manager implementation used to manage
36   * thread allocation for Mule components and connectors. This code has been adapted
37   * from the Geronimo implementation.
38   */
39  public class MuleWorkManager implements WorkManager, MuleContextAware
40  {
41      /**
42       * logger used by this class
43       */
44      protected static final Log logger = LogFactory.getLog(MuleWorkManager.class);
45  
46      /**
47       * Forced shutdown delay. The time the workManager will wait while threads are being
48       * interrupted. The graceful shutdown timeout which is the amount of time that
49       * the workManager will wait while the workManager completed pending and
50       * currently executing jobs is configured using {@link MuleConfiguration}.
51       */
52      private static final long FORCEFUL_SHUTDOWN_TIMEOUT = 5000L;
53  
54      /**
55       * The ThreadingProfile used for creation of the underlying ExecutorService
56       */
57      private final ThreadingProfile threadingProfile;
58  
59      /**
60       * The actual pool of threads used by this MuleWorkManager to process the Work
61       * instances submitted via the (do,start,schedule)Work methods.
62       */
63      private volatile ExecutorService workExecutorService;
64      private final String name;
65      private int gracefulShutdownTimeout;
66      private MuleContext muleContext;
67      
68      /**
69       * Various policies used for work execution
70       */
71      private final WorkExecutor scheduleWorkExecutor = new ScheduleWorkExecutor();
72      private final WorkExecutor startWorkExecutor = new StartWorkExecutor();
73      private final WorkExecutor syncWorkExecutor = new SyncWorkExecutor();
74  
75      public MuleWorkManager(ThreadingProfile profile, String name, int shutdownTimeout)
76      {
77          super();
78  
79          if (name == null)
80          {
81              name = "WorkManager#" + hashCode();
82          }
83  
84          this.threadingProfile = profile;
85          this.name = name;
86          gracefulShutdownTimeout = shutdownTimeout;
87      }
88  
89      public synchronized void start() throws MuleException
90      {
91          gracefulShutdownTimeout = getMuleContext().getConfiguration().getShutdownTimeout();
92          
93          if (workExecutorService == null)
94          {
95              workExecutorService = threadingProfile.createPool(name);
96          }
97      }
98  
99      public synchronized void dispose()
100     {
101         if (workExecutorService != null)
102         {
103             // Disable new tasks from being submitted
104             workExecutorService.shutdown();
105             try
106             {
107                 // Wait a while for existing tasks to terminate
108                 if (!workExecutorService.awaitTermination(gracefulShutdownTimeout, TimeUnit.MILLISECONDS))
109                 {
110                     // Cancel currently executing tasks and return list of pending
111                     // tasks
112                     List outstanding = workExecutorService.shutdownNow();
113                     // Wait a while for tasks to respond to being cancelled
114                     if (!workExecutorService.awaitTermination(FORCEFUL_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS))
115                     {
116                         logger.warn(MessageFormat.format(
117                             "Pool {0} did not terminate in time; {1} work items were cancelled.", name,
118                             outstanding.isEmpty() ? "No" : Integer.toString(outstanding.size())));
119                     }
120                     else
121                     {
122                         if (!outstanding.isEmpty())
123                         {
124                             logger.warn(MessageFormat.format(
125                                 "Pool {0} terminated; {1} work items were cancelled.", name,
126                                 Integer.toString(outstanding.size())));
127                         }
128                     }
129                 }
130             }
131             catch (InterruptedException ie)
132             {
133                 // (Re-)Cancel if current thread also interrupted
134                 workExecutorService.shutdownNow();
135                 // Preserve interrupt status
136                 Thread.currentThread().interrupt();
137             }
138             finally
139             {
140                 workExecutorService = null;
141             }
142         }
143     }
144 
145 
146     // TODO
147     public XATerminator getXATerminator()
148     {
149         return null;
150     }
151 
152     public void doWork(Work work) throws WorkException
153     {
154         executeWork(new WorkerContext(work), syncWorkExecutor);
155     }
156 
157     public void doWork(Work work, long startTimeout, ExecutionContext execContext, WorkListener workListener)
158         throws WorkException
159     {
160         WorkerContext workWrapper = new WorkerContext(work, startTimeout, execContext, workListener);
161         workWrapper.setThreadPriority(Thread.currentThread().getPriority());
162         executeWork(workWrapper, syncWorkExecutor);
163     }
164 
165     public long startWork(Work work) throws WorkException
166     {
167         WorkerContext workWrapper = new WorkerContext(work);
168         workWrapper.setThreadPriority(Thread.currentThread().getPriority());
169         executeWork(workWrapper, startWorkExecutor);
170         return System.currentTimeMillis() - workWrapper.getAcceptedTime();
171     }
172 
173     public long startWork(Work work,
174                           long startTimeout,
175                           ExecutionContext execContext,
176                           WorkListener workListener) throws WorkException
177     {
178         WorkerContext workWrapper = new WorkerContext(work, startTimeout, execContext, workListener);
179         workWrapper.setThreadPriority(Thread.currentThread().getPriority());
180         executeWork(workWrapper, startWorkExecutor);
181         return System.currentTimeMillis() - workWrapper.getAcceptedTime();
182     }
183 
184     public void scheduleWork(Work work) throws WorkException
185     {
186         WorkerContext workWrapper = new WorkerContext(work);
187         workWrapper.setThreadPriority(Thread.currentThread().getPriority());
188         executeWork(workWrapper, scheduleWorkExecutor);
189     }
190 
191     public void scheduleWork(Work work,
192                              long startTimeout,
193                              ExecutionContext execContext,
194                              WorkListener workListener) throws WorkException
195     {
196         WorkerContext workWrapper = new WorkerContext(work, startTimeout, execContext, workListener);
197         workWrapper.setThreadPriority(Thread.currentThread().getPriority());
198         executeWork(workWrapper, scheduleWorkExecutor);
199     }
200 
201     /**
202      * @see Executor#execute(Runnable)
203      */
204     public void execute(Runnable work)
205     {
206         if (!isStarted())
207         {
208             throw new IllegalStateException("This MuleWorkManager '" + name + "' is stopped");
209         }
210         workExecutorService.execute(work);
211     }
212 
213     /**
214      * Execute the specified Work.
215      *
216      * @param work Work to be executed.
217      * @exception WorkException Indicates that the Work execution has been
218      *                unsuccessful.
219      */
220     private void executeWork(WorkerContext work, WorkExecutor workExecutor) throws WorkException
221     {
222         if (!isStarted())
223         {
224             throw new IllegalStateException("This MuleWorkManager '" + name + "' is stopped");
225         }
226 
227         try
228         {
229             work.workAccepted(this);
230             workExecutor.doExecute(work, workExecutorService);
231             WorkException exception = work.getWorkException();
232             if (null != exception)
233             {
234                 throw exception;
235             }
236         }
237         catch (InterruptedException e)
238         {
239             WorkCompletedException wcj = new WorkCompletedException("The execution has been interrupted for WorkManager: " + name, e);
240             wcj.setErrorCode(WorkException.INTERNAL);
241             throw wcj;
242         }
243     }
244 
245     public boolean isStarted()
246     {
247         return (workExecutorService != null && !workExecutorService.isShutdown());
248     }
249 
250     public MuleContext getMuleContext()
251     {
252         return muleContext;
253     }
254 
255     public void setMuleContext(MuleContext muleContext)
256     {
257         this.muleContext = muleContext;
258         if (this.threadingProfile != null && muleContext != null)
259         {
260             threadingProfile.setMuleContext(muleContext);
261         }
262     }
263 }