Package org.apache.geronimo.connector.work

Source Code of org.apache.geronimo.connector.work.WorkerContext

/**
*
* Copyright 2003-2004 The Apache Software Foundation
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

package org.apache.geronimo.connector.work;

import javax.resource.spi.work.ExecutionContext;
import javax.resource.spi.work.Work;
import javax.resource.spi.work.WorkAdapter;
import javax.resource.spi.work.WorkCompletedException;
import javax.resource.spi.work.WorkEvent;
import javax.resource.spi.work.WorkException;
import javax.resource.spi.work.WorkListener;
import javax.resource.spi.work.WorkManager;
import javax.resource.spi.work.WorkRejectedException;
import javax.transaction.xa.XAException;
import javax.transaction.InvalidTransactionException;
import javax.transaction.SystemException;

import EDU.oswego.cs.dl.util.concurrent.Latch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.transaction.context.TransactionContextManager;
import org.apache.geronimo.transaction.context.TransactionContext;
import org.apache.geronimo.transaction.ImportedTransactionActiveException;

/**
* Work wrapper providing an execution context to a Work instance.
*
* @version $Rev: 355877 $ $Date: 2005-12-11 03:48:27 +0100 (Sun, 11 Dec 2005) $
*/
public class WorkerContext implements Work {

    private static final Log log = LogFactory.getLog(WorkerContext.class);

    /**
     * Null WorkListener used as the default WorkListener.
     */
    private static final WorkListener NULL_WORK_LISTENER = new WorkAdapter() {
        public void workRejected(WorkEvent event) {
            if (event.getException() != null) {
                if (event.getException() instanceof WorkCompletedException && event.getException().getCause() != null) {
                    log.error(event.getWork().toString(), event.getException().getCause());
                } else {
                    log.error(event.getWork().toString(), event.getException());
                }
            }
        }
    };

    /**
     * Priority of the thread, which will execute this work.
     */
    private int threadPriority;

    /**
     * Actual work to be executed.
     */
    private Work adaptee;

    /**
     * Indicates if this work has been accepted.
     */
    private boolean isAccepted;

    /**
     * System.currentTimeMillis() when the wrapped Work has been accepted.
     */
    private long acceptedTime;

    /**
     * Number of times that the execution of this work has been tried.
     */
    private int nbRetry;

    /**
     * Time duration (in milliseconds) within which the execution of the Work
     * instance must start.
     */
    private long startTimeOut;

    /**
     * Execution context of the actual work to be executed.
     */
    private final ExecutionContext executionContext;

    private final TransactionContextManager transactionContextManager;

    /**
     * Listener to be notified during the life-cycle of the work treatment.
     */
    private WorkListener workListener = NULL_WORK_LISTENER;

    /**
     * Work exception, if any.
     */
    private WorkException workException;

    /**
     * A latch, which is released when the work is started.
     */
    private Latch startLatch = new Latch();

    /**
     * A latch, which is released when the work is completed.
     */
    private Latch endLatch = new Latch();

    /**
     * Create a WorkWrapper.
     *
     * @param work                      Work to be wrapped.
     * @param transactionContextManager
     */
    public WorkerContext(Work work, TransactionContextManager transactionContextManager) {
        adaptee = work;
        executionContext = null;
        this.transactionContextManager = transactionContextManager;
    }

    /**
     * Create a WorkWrapper with the specified execution context.
     *
     * @param aWork         Work to be wrapped.
     * @param aStartTimeout a time duration (in milliseconds) within which the
     *                      execution of the Work instance must start.
     * @param execContext   an object containing the execution context with which
     *                      the submitted Work instance must be executed.
     * @param workListener  an object which would be notified when the various
     *                      Work processing events (work accepted, work rejected, work started,
     */
    public WorkerContext(Work aWork, long aStartTimeout,
                         ExecutionContext execContext,
                         TransactionContextManager transactionContextManager,
                         WorkListener workListener) {
        adaptee = aWork;
        startTimeOut = aStartTimeout;
        executionContext = execContext;
        this.transactionContextManager = transactionContextManager;
        if (null != workListener) {
            this.workListener = workListener;
        }
    }

    /* (non-Javadoc)
     * @see javax.resource.spi.work.Work#release()
     */
    public void release() {
        adaptee.release();
    }

    /**
     * Defines the thread priority level of the thread, which will be dispatched
     * to process this work. This priority level must be the same one for a
     * given resource adapter.
     *
     * @param aPriority Priority of the thread to be used to process the wrapped
     *                  Work instance.
     */
    public void setThreadPriority(int aPriority) {
        threadPriority = aPriority;
    }

    /**
     * Gets the priority level of the thread, which will be dispatched
     * to process this work. This priority level must be the same one for a
     * given resource adapter.
     *
     * @return The priority level of the thread to be dispatched to
     *         process the wrapped Work instance.
     */
    public int getThreadPriority() {
        return threadPriority;
    }

    /**
     * Call-back method used by a Work executor in order to notify this
     * instance that the wrapped Work instance has been accepted.
     *
     * @param anObject Object on which the event initially occurred. It should
     *                 be the work executor.
     */
    public synchronized void workAccepted(Object anObject) {
        isAccepted = true;
        acceptedTime = System.currentTimeMillis();
        workListener.workAccepted(new WorkEvent(anObject,
                WorkEvent.WORK_ACCEPTED, adaptee, null));
    }

    /**
     * System.currentTimeMillis() when the Work has been accepted. This method
     * can be used to compute the duration of a work.
     *
     * @return When the work has been accepted.
     */
    public synchronized long getAcceptedTime() {
        return acceptedTime;
    }

    /**
     * Gets the time duration (in milliseconds) within which the execution of
     * the Work instance must start.
     *
     * @return Time out duration.
     */
    public long getStartTimeout() {
        return startTimeOut;
    }

    /**
     * Used by a Work executor in order to know if this work, which should be
     * accepted but not started has timed out. This method MUST be called prior
     * to retry the execution of a Work.
     *
     * @return true if the Work has timed out and false otherwise.
     */
    public synchronized boolean isTimedOut() {
        assert isAccepted: "The work is not accepted.";
        // A value of 0 means that the work never times out.
        //??? really?
        if (0 == startTimeOut || startTimeOut == WorkManager.INDEFINITE) {
            return false;
        }
        boolean isTimeout = acceptedTime + startTimeOut > 0 &&
                System.currentTimeMillis() > acceptedTime + startTimeOut;
        if (log.isDebugEnabled()) {
            log.debug(this
                    + " accepted at "
                    + acceptedTime
                    + (isTimeout ? " has timed out." : " has not timed out. ")
                    + nbRetry
                    + " retries have been performed.");
        }
        if (isTimeout) {
            workException = new WorkRejectedException(this + " has timed out.",
                    WorkException.START_TIMED_OUT);
            workListener.workRejected(new WorkEvent(this,
                    WorkEvent.WORK_REJECTED,
                    adaptee,
                    workException));
            return true;
        }
        nbRetry++;
        return isTimeout;
    }

    /**
     * Gets the WorkException, if any, thrown during the execution.
     *
     * @return WorkException, if any.
     */
    public synchronized WorkException getWorkException() {
        return workException;
    }

    /* (non-Javadoc)
     * @see java.lang.Runnable#run()
     */
    public void run() {
        if (isTimedOut()) {
            // In case of a time out, one releases the start and end latches
            // to prevent a dead-lock.
            startLatch.release();
            endLatch.release();
            return;
        }
        // Implementation note: the work listener is notified prior to release
        // the start lock. This behavior is intentional and seems to be the
        // more conservative.
        workListener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED, adaptee, null));
        startLatch.release();
        //Implementation note: we assume this is being called without an interesting TransactionContext,
        //and ignore/replace whatever is associated with the current thread.
        try {
            if (executionContext == null || executionContext.getXid() == null) {
                TransactionContext context = transactionContextManager.newUnspecifiedTransactionContext();
                try {
                    adaptee.run();
                } finally {
                    TransactionContext returningContext = transactionContextManager.getContext();
                    transactionContextManager.setContext(null);
                    if (context != returningContext) {
                        throw new WorkCompletedException("Wrong TransactionContext on return from work done");
                    }
                }
                //TODO should we commit the txContext to flush any leftover state???
            } else {
                try {
                    long transactionTimeout = executionContext.getTransactionTimeout();
                    //translate -1 value to 0 to indicate default transaction timeout.
                    transactionContextManager.begin(executionContext.getXid(), transactionTimeout == -1 ? 0 : transactionTimeout);
                } catch (XAException e) {
                    throw new WorkCompletedException("Transaction import failed for xid " + executionContext.getXid(), WorkCompletedException.TX_RECREATE_FAILED).initCause(e);
                } catch (InvalidTransactionException e) {
                    throw new WorkCompletedException("Transaction import failed for xid " + executionContext.getXid(), WorkCompletedException.TX_RECREATE_FAILED).initCause(e);
                } catch (SystemException e) {
                    throw new WorkCompletedException("Transaction import failed for xid " + executionContext.getXid(), WorkCompletedException.TX_RECREATE_FAILED).initCause(e);
                } catch (ImportedTransactionActiveException e) {
                    throw new WorkCompletedException("Transaction already active for xid " + executionContext.getXid(), WorkCompletedException.TX_CONCURRENT_WORK_DISALLOWED);
                }
                try {
                    adaptee.run();
                } finally {
                    transactionContextManager.end(executionContext.getXid());
                }

            }
            workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, adaptee, null));
        } catch (Throwable e) {
            workException = (WorkException) (e instanceof WorkCompletedException ? e : new WorkCompletedException("Unknown error", WorkCompletedException.UNDEFINED).initCause(e));
            workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_REJECTED, adaptee,
                    workException));
        } finally {
            endLatch.release();
        }
    }

    /**
     * Provides a latch, which can be used to wait the start of a work
     * execution.
     *
     * @return Latch that a caller can acquire to wait for the start of a
     *         work execution.
     */
    public synchronized Latch provideStartLatch() {
        return startLatch;
    }

    /**
     * Provides a latch, which can be used to wait the end of a work
     * execution.
     *
     * @return Latch that a caller can acquire to wait for the end of a
     *         work execution.
     */
    public synchronized Latch provideEndLatch() {
        return endLatch;
    }

    public String toString() {
        return "Work :" + adaptee;
    }

}
TOP

Related Classes of org.apache.geronimo.connector.work.WorkerContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.