View Javadoc

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