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