View Javadoc

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