View Javadoc

1   /*
2    * $Id: WorkerContext.java 22217 2011-06-16 20:13:11Z pablo.lagreca $
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.RequestContext;
31  import org.mule.util.concurrent.Latch;
32  
33  import javax.resource.spi.work.ExecutionContext;
34  import javax.resource.spi.work.Work;
35  import javax.resource.spi.work.WorkAdapter;
36  import javax.resource.spi.work.WorkCompletedException;
37  import javax.resource.spi.work.WorkEvent;
38  import javax.resource.spi.work.WorkException;
39  import javax.resource.spi.work.WorkListener;
40  import javax.resource.spi.work.WorkRejectedException;
41  
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  
45  /**
46   * <code>WorkerContext</code> TODO
47   */
48  public class WorkerContext implements Work
49  {
50  
51      /**
52       * logger used by this class
53       */
54      protected static final Log logger = LogFactory.getLog(WorkerContext.class);
55  
56      /**
57       * Null WorkListener used as the default WorkListener.
58       */
59      private static final WorkListener NULL_WORK_LISTENER = new WorkAdapter()
60      {
61          @Override
62          public void workRejected(WorkEvent event)
63          {
64              if (event.getException() != null)
65              {
66                  if (event.getException() instanceof WorkCompletedException
67                      && event.getException().getCause() != null)
68                  {
69                      logger.error(event.getWork().toString(), event.getException().getCause());
70                  }
71                  else
72                  {
73                      logger.error(event.getWork().toString(), event.getException());
74                  }
75              }
76          }
77      };
78  
79      protected ClassLoader executionClassLoader;
80  
81      /**
82       * Priority of the thread, which will execute this work.
83       */
84      private int threadPriority;
85  
86      /**
87       * Actual work to be executed.
88       */
89      private Work worker;
90  
91      /**
92       * System.currentTimeMillis() when the wrapped Work has been accepted.
93       */
94      private long acceptedTime;
95  
96      /**
97       * Number of times that the execution of this work has been tried.
98       */
99      private int retryCount;
100 
101     /**
102      * Time duration (in milliseconds) within which the execution of the Work
103      * instance must start.
104      */
105     private long startTimeOut;
106 
107     /**
108      * Execution context of the actual work to be executed.
109      */
110     private final ExecutionContext executionContext;
111 
112     /**
113      * Listener to be notified during the life-cycle of the work treatment.
114      */
115     private WorkListener workListener = NULL_WORK_LISTENER;
116 
117     /**
118      * Work exception, if any.
119      */
120     private WorkException workException;
121 
122     /**
123      * A latch, which is released when the work is started.
124      */
125     private final Latch startLatch = new Latch();
126 
127     /**
128      * A latch, which is released when the work is completed.
129      */
130     private final Latch endLatch = new Latch();
131 
132     {
133         this.executionClassLoader = Thread.currentThread().getContextClassLoader();
134     }
135 
136     /**
137      * Create a WorkWrapper.
138      * 
139      * @param work Work to be wrapped.
140      */
141     public WorkerContext(Work work)
142     {
143         worker = work;
144         executionContext = null;
145     }
146 
147     /**
148      * Create a WorkWrapper with the specified execution context.
149      * 
150      * @param aWork Work to be wrapped.
151      * @param aStartTimeout a time duration (in milliseconds) within which the
152      *            execution of the Work instance must start.
153      * @param execContext an object containing the execution context with which the
154      *            submitted Work instance must be executed.
155      * @param workListener an object which would be notified when the various Work
156      *            processing events (work accepted, work rejected, work started,
157      */
158     public WorkerContext(Work aWork,
159                          long aStartTimeout,
160                          ExecutionContext execContext,
161                          WorkListener workListener)
162     {
163         worker = aWork;
164         startTimeOut = aStartTimeout;
165         executionContext = execContext;
166         if (null != workListener)
167         {
168             this.workListener = workListener;
169         }
170     }
171 
172     public void release()
173     {
174         worker.release();
175     }
176 
177     /**
178      * Defines the thread priority level of the thread, which will be dispatched to
179      * process this work. This priority level must be the same one for a given
180      * resource adapter.
181      * 
182      * @param aPriority Priority of the thread to be used to process the wrapped Work
183      *            instance.
184      */
185     public void setThreadPriority(int aPriority)
186     {
187         threadPriority = aPriority;
188     }
189 
190     /**
191      * Gets the priority level of the thread, which will be dispatched to process
192      * this work. This priority level must be the same one for a given resource
193      * adapter.
194      * 
195      * @return The priority level of the thread to be dispatched to process the
196      *         wrapped Work instance.
197      */
198     public int getThreadPriority()
199     {
200         return threadPriority;
201     }
202 
203     /**
204      * Call-back method used by a Work executor in order to notify this instance that
205      * the wrapped Work instance has been accepted.
206      * 
207      * @param anObject Object on which the event initially occurred. It should be the
208      *            work executor.
209      */
210     public synchronized void workAccepted(Object anObject)
211     {
212         acceptedTime = System.currentTimeMillis();
213         workListener.workAccepted(new WorkEvent(anObject, WorkEvent.WORK_ACCEPTED, worker, null));
214     }
215 
216     /**
217      * System.currentTimeMillis() when the Work has been accepted. This method can be
218      * used to compute the duration of a work.
219      * 
220      * @return When the work has been accepted.
221      */
222     public synchronized long getAcceptedTime()
223     {
224         return acceptedTime;
225     }
226 
227     /**
228      * Gets the time duration (in milliseconds) within which the execution of the
229      * Work instance must start.
230      * 
231      * @return Time out duration.
232      */
233     public long getStartTimeout()
234     {
235         return startTimeOut;
236     }
237 
238     /**
239      * Used by a Work executor in order to know if this work, which should be
240      * accepted but not started has timed out. This method MUST be called prior to
241      * retry the execution of a Work.
242      * 
243      * @return true if the Work has timed out and false otherwise.
244      */
245     public synchronized boolean isTimedOut()
246     {
247         // A value of 0 means that the work never times out.
248         // ??? really?
249         if (0 == startTimeOut || startTimeOut == MuleWorkManager.INDEFINITE)
250         {
251             return false;
252         }
253         boolean isTimeout = acceptedTime + startTimeOut > 0
254                             && System.currentTimeMillis() > acceptedTime + startTimeOut;
255         if (logger.isDebugEnabled())
256         {
257             logger.debug(this + " accepted at " + acceptedTime
258                          + (isTimeout ? " has timed out." : " has not timed out. ") + retryCount
259                          + " retries have been performed.");
260         }
261         if (isTimeout)
262         {
263             workException = new WorkRejectedException(this + " has timed out.", WorkException.START_TIMED_OUT);
264             workListener.workRejected(new WorkEvent(this, WorkEvent.WORK_REJECTED, worker, workException));
265             return true;
266         }
267         retryCount++;
268         return isTimeout;
269     }
270 
271     /**
272      * Gets the WorkException, if any, thrown during the execution.
273      * 
274      * @return WorkException, if any.
275      */
276     public synchronized WorkException getWorkException()
277     {
278         return workException;
279     }
280 
281     public void run()
282     {
283         if (isTimedOut())
284         {
285             // In case of a time out, one releases the start and end latches
286             // to prevent a dead-lock.
287             startLatch.countDown();
288             endLatch.countDown();
289             return;
290         }
291         // Implementation note: the work listener is notified prior to release
292         // the start lock. This behavior is intentional and seems to be the
293         // more conservative.
294         workListener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED, worker, null));
295         startLatch.countDown();
296         // Implementation note: we assume this is being called without an
297         // interesting TransactionContext,
298         // and ignore/replace whatever is associated with the current thread.
299         try
300         {
301             if (executionContext == null || executionContext.getXid() == null)
302             {
303                 // TODO currently unused, see below
304                 // ExecutionContext context = new ExecutionContext();
305                 final ClassLoader originalCl = Thread.currentThread().getContextClassLoader();
306                 try
307                 {
308                     // execute with the application-specific classloader in the context
309                     Thread.currentThread().setContextClassLoader(executionClassLoader);
310                     worker.run();
311                 }
312                 finally
313                 {
314                     Thread.currentThread().setContextClassLoader(originalCl);
315                     
316                     // ExecutionContext returningContext = new
317                     // ExecutionContext();
318                     // if (context != returningContext) {
319                     // throw new WorkCompletedException("Wrong
320                     // TransactionContext on return from work done");
321                     // }
322                 }
323                 // TODO should we commit the txContext to flush any leftover
324                 // state???
325             }
326             else
327             {
328                 // try {
329                 // long transactionTimeout =
330                 // executionContext.getDefaultTransactionTimeout();
331                 // //translate -1 value to 0 to indicate default transaction
332                 // timeout.
333                 // transactionContextManager.begin(executionContext.getXid(),
334                 // transactionTimeout == -1 ? 0 : transactionTimeout);
335                 // } catch (XAException e) {
336                 // throw new WorkCompletedException("Transaction import failed
337                 // for xid " + executionContext.getXid(),
338                 // WorkCompletedException.TX_RECREATE_FAILED).initCause(e);
339                 // } catch (InvalidTransactionException e) {
340                 // throw new WorkCompletedException("Transaction import failed
341                 // for xid " + executionContext.getXid(),
342                 // WorkCompletedException.TX_RECREATE_FAILED).initCause(e);
343                 // } catch (SystemException e) {
344                 // throw new WorkCompletedException("Transaction import failed
345                 // for xid " + executionContext.getXid(),
346                 // WorkCompletedException.TX_RECREATE_FAILED).initCause(e);
347                 // } catch (ImportedTransactionActiveException e) {
348                 // throw new WorkCompletedException("Transaction already active
349                 // for xid " + executionContext.getXid(),
350                 // WorkCompletedException.TX_CONCURRENT_WORK_DISALLOWED);
351                 // }
352                 try
353                 {
354                     worker.run();
355                 }
356                 finally
357                 {
358                     // transactionContextManager.end(executionContext.getXid());
359                 }
360 
361             }
362             workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, worker, null));
363         }
364         catch (Throwable e)
365         {
366             workException = (WorkException)(e instanceof WorkCompletedException
367                             ? e : new WorkCompletedException("Unknown error",
368                                 WorkCompletedException.UNDEFINED).initCause(e));
369             workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_REJECTED, worker, workException));
370         }
371         finally
372         {
373             RequestContext.clear();
374             endLatch.countDown();
375         }
376     }
377 
378     /**
379      * Provides a latch, which can be used to wait the start of a work execution.
380      * 
381      * @return Latch that a caller can acquire to wait for the start of a work
382      *         execution.
383      */
384     public Latch provideStartLatch()
385     {
386         return startLatch;
387     }
388 
389     /**
390      * Provides a latch, which can be used to wait the end of a work execution.
391      * 
392      * @return Latch that a caller can acquire to wait for the end of a work
393      *         execution.
394      */
395     public Latch provideEndLatch()
396     {
397         return endLatch;
398     }
399 
400     @Override
401     public String toString()
402     {
403         return "Work: " + worker;
404     }
405 }