View Javadoc

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