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