Package org.ow2.easybeans.component.jca.workmanager

Source Code of org.ow2.easybeans.component.jca.workmanager.ResourceWorkManagerComponent

/**
* EasyBeans
* Copyright (C) 2006-2010 Bull S.A.S.
* Contact: easybeans@ow2.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
* USA
*
* --------------------------------------------------------------------------
* $Id: ResourceWorkManagerComponent.java 5479 2010-04-28 14:57:16Z benoitf $
* --------------------------------------------------------------------------
*/

package org.ow2.easybeans.component.jca.workmanager;

import java.util.LinkedList;

import javax.resource.spi.work.ExecutionContext;
import javax.resource.spi.work.Work;
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.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.xa.Xid;

import org.ow2.easybeans.component.api.EZBComponentException;
import org.ow2.easybeans.component.itf.EZBWorkManagerComponent;
import org.ow2.easybeans.component.itf.TMComponent;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
* Implementation of the Resource Work Manager API.
* @author Philippe Durieux (JOnAS)
* @author Florent Benoit (EasyBeans)
*/
public class ResourceWorkManagerComponent implements EZBWorkManagerComponent, WorkManager {

    /**
     * MilliSeconds value.
     */
    public static final long ONE_SECOND = 1000L;

    /**
     * Default waiting time.
     */
    private static final long DEFAULT_WAIT_TIME = 60 * ONE_SECOND;

    /**
     * Default min threads.
     */
    private static final int DEFAULT_MIN = 5;

    /**
     * Default max threads.
     */
    private static final int DEFAULT_MAX = 100;

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(ResourceWorkManagerComponent.class);

    /**
     * List of ResourceWork (which wrap Work object).
     */
    private LinkedList<ResourceWork> workList = new LinkedList<ResourceWork>();

    /**
     * Identifier of this pool.
     */
    private static int poolnumber = 0;

    /**
     * Thread number (when building ResourceWorkThread, it assigns a new thread
     * number).
     */
    private static int threadnumber = 0;

    /**
     * The maximum size of the pool.
     */
    private int maxpoolsz = DEFAULT_MAX;

    /**
     * The minimum size of the pool.
     */
    private int minpoolsz = DEFAULT_MIN;

    /**
     * The current size of thread pool.
     */
    private int poolsz;

    /**
     * Threads that are ready to work.
     */
    private int freeThreads = 0;

    /**
     * The time to wait (in millisec).
     */
    private long waitingTime = DEFAULT_WAIT_TIME;

    /**
     * Pool status : by default, it is not stopped.
     */
    private boolean stopped = false;

    /**
     * Wait few more seconds when waiting.
     */
    private static final long FEW_MORE_SECONDS = 3000;

    /**
     * Transaction component.
     */
    private TMComponent transactionComponent = null;

    /**
     * Default Constructor.
     */
    public ResourceWorkManagerComponent() {
        // new identifier
        poolnumber++;
    }

    /**
     * @return current pool size
     */
    public int getCurrentPoolSize() {
        return this.poolsz;
    }

    /**
     * @return min pool size
     */
    public int getMinPoolSize() {
        return this.minpoolsz;
    }

    /**
     * @return max pool size
     */
    public int getMaxPoolSize() {
        return this.maxpoolsz;
    }

    /**
     * Sets the min pool size.
     * @param minsz the min pool size.
     */
    public void setMinPoolSize(final int minsz) {
        this.minpoolsz = minsz;
    }

    /**
     * Sets the max pool size.
     * @param maxsz the max pool size.
     */
    public void setMaxPoolSize(final int maxsz) {
        this.maxpoolsz = maxsz;
    }

    /**
     * Accepts a Work instance for processing. This call blocks until the Work
     * instance completes execution. There is no guarantee on when the accepted
     * Work instance would start execution ie., there is no time constraint to
     * start execution.
     * @param work The unit of work to be done. Could be long or short-lived.
     * @throws WorkRejectedException a Work instance has been rejected from
     *         further processing.
     * @throws WorkCompletedException a Work instance has completed execution
     *         with an exception.
     * @throws WorkException if work is not done
     */
    public void doWork(final Work work) throws WorkRejectedException, WorkCompletedException, WorkException {
        doMyWork(work, INDEFINITE, null, null, 0);
    }

    /**
     * Accepts a Work instance for processing. This call blocks until the Work
     * instance completes execution.
     * @param work The unit of work to be done. Could be long or short-lived.
     * @param timeout a time duration (in milliseconds) within which the
     *        execution of the Work instance must start. Otherwise, the Work
     *        instance is rejected with a WorkRejectedException set to an
     *        appropriate error code (WorkRejectedException.TIMED_OUT).
     * @param executionContext 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, work completed) occur.
     * @throws WorkRejectedException a Work instance has been rejected from
     *         further processing.
     * @throws WorkCompletedException a Work instance has completed execution
     *         with an exception.
     * @throws WorkException if work is not done
     */
    public void doWork(final Work work, final long timeout, final ExecutionContext executionContext,
            final WorkListener workListener) throws WorkRejectedException, WorkCompletedException, WorkException {
        if (workListener != null) {
            workListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null));
        }
        doMyWork(work, timeout, executionContext, workListener, System.currentTimeMillis());
    }

    /**
     * Accepts a Work instance for processing. This call blocks until the Work
     * instance starts execution but not until its completion. There is no
     * guarantee on when the accepted Work instance would start execution ie.,
     * there is no time constraint to start execution.
     * @param work The unit of work to be done. Could be long or short-lived.
     * @return the time elapsed (in milliseconds) from Work acceptance until
     *         start of execution. Note, this does not offer real-time
     *         guarantees. It is valid to return -1, if the actual start delay
     *         duration is unknown.
     * @throws WorkRejectedException a Work instance has been rejected from
     *         further processing.
     * @throws WorkException if work is not started
     */
    public long startWork(final Work work) throws WorkRejectedException, WorkException {
        return startWork(work, INDEFINITE, null, null);
    }

    /**
     * Accepts a Work instance for processing. This call blocks until the Work
     * instance starts execution but not until its completion. There is no
     * guarantee on when the accepted Work instance would start execution ie.,
     * there is no time constraint to start execution.
     * @param work The unit of work to be done. Could be long or short-lived.
     * @param timeout a time duration (in milliseconds) within which the
     *        execution of the Work instance must start. Otherwise, the Work
     *        instance is rejected with a WorkRejectedException set to an
     *        appropriate error code (WorkRejectedException.TIMED_OUT).
     * @param executionContext 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, work completed) occur.
     * @return the time elapsed (in milliseconds) from Work acceptance until
     *         start of execution. Note, this does not offer real-time
     *         guarantees. It is valid to return -1, if the actual start delay
     *         duration is unknown.
     * @throws WorkRejectedException a Work instance has been rejected from
     *         further processing.
     * @throws WorkException if work is not started
     */
    public long startWork(final Work work, final long timeout, final ExecutionContext executionContext,
            final WorkListener workListener) throws WorkRejectedException, WorkException {

        ResourceWork resourceWork = new ResourceWork(work, timeout, executionContext, workListener);
        if (workListener != null) {
            workListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null));
        }
        long starttime = System.currentTimeMillis();
        long duration = 0;
        synchronized (this.workList) {
            this.workList.add(resourceWork);
            if (this.poolsz < this.maxpoolsz && this.workList.size() > this.freeThreads) {
                // We need one more thread.
                this.poolsz++;
                ResourceWorkThread resourceWorkThread = new ResourceWorkThread(this, threadnumber++, poolnumber);
                resourceWorkThread.start();
            } else {
                this.workList.notify();
            }
        }
        // Wait until my work is started.
        boolean started = false;
        synchronized (resourceWork) {
            if (!resourceWork.isStarted()) {
                try {
                    // No need to wait after timeout is elapsed
                    long waittime = this.waitingTime;
                    if (timeout < waittime) {
                        waittime = timeout + FEW_MORE_SECONDS;
                    }
                    resourceWork.wait(waittime);
                } catch (InterruptedException e) {
                    throw new WorkRejectedException("Interrupted");
                }
            }
            started = resourceWork.isStarted();
        }
        duration = System.currentTimeMillis() - starttime;
        if (!started) {
            synchronized (this.workList) {
                // Remove the work in the list
                if (!this.workList.remove(resourceWork)) {
                    logger.debug("Cannot remove the work");
                }
                throw new WorkRejectedException(WorkException.START_TIMED_OUT);
            }
        }
        return duration;
    }

    /**
     * Accepts a Work instance for processing. This call does not block and
     * returns immediately once a Work instance has been accepted for
     * processing. There is no guarantee on when the submitted Work instance
     * would start execution ie., there is no time constraint to start
     * execution.
     * @param work The unit of work to be done. Could be long or short-lived.
     * @throws WorkRejectedException - indicates that a Work instance has been
     *         rejected from further processing. This can occur due to internal
     *         factors.
     * @throws WorkException if work is not scheduled.
     */
    public void scheduleWork(final Work work) throws WorkRejectedException, WorkException {
        scheduleWork(work, INDEFINITE, null, null);
    }

    /**
     * Accepts a Work instance for processing. This call does not block and
     * returns immediately once a Work instance has been accepted for
     * processing. There is no guarantee on when the submitted Work instance
     * would start execution ie., there is no time constraint to start
     * execution.
     * @param work The unit of work to be done. Could be long or short-lived.
     * @param timeout a time duration (in milliseconds) within which the
     *        execution of the Work instance must start. Otherwise, the Work
     *        instance is rejected with a WorkRejectedException set to an
     *        appropriate error code (WorkRejectedException.TIMED_OUT).
     * @param executionContext 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, work completed) occur.
     * @throws WorkRejectedException a Work instance has been rejected from
     *         further processing.
     * @throws WorkException if work is not scheduled.
     */
    public void scheduleWork(final Work work, final long timeout, final ExecutionContext executionContext,
            final WorkListener workListener) throws WorkRejectedException, WorkException {

        ResourceWork resourceWork = new ResourceWork(work, timeout, executionContext, workListener);
        if (workListener != null) {
            workListener.workAccepted(new WorkEvent(this, WorkEvent.WORK_ACCEPTED, work, null));
        }
        synchronized (this.workList) {
            this.workList.add(resourceWork);
            if (this.poolsz < this.maxpoolsz && this.workList.size() > this.freeThreads) {
                // We need one more thread.
                this.poolsz++;
                ResourceWorkThread resourceWorkThread = new ResourceWorkThread(this, threadnumber++, poolnumber);
                resourceWorkThread.start();
            } else {
                // Just wake up a thread waiting for work.
                this.workList.notify();
            }
        }
    }

    /**
     * Internal method doing the work.
     * @param work The unit of work to be done. Could be long or short-lived.
     * @param timeout a time duration (in milliseconds) within which the
     *        execution of the Work instance must start. Otherwise, the Work
     *        instance is rejected with a WorkRejectedException set to an
     *        appropriate error code (WorkRejectedException.TIMED_OUT).
     * @param executionContext 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, work completed) occur.
     * @param creationTime the date of the creation of the work
     * @throws WorkException if work is not performed.
     */
    @SuppressWarnings("boxing")
    private void doMyWork(final Work work, final long timeout, final ExecutionContext executionContext,
            final WorkListener workListener, final long creationTime) throws WorkException {

        // Notify the listener that the work is started or rejected by timeout.
        if (workListener != null) {
            long duration = System.currentTimeMillis() - creationTime;
            if (duration > timeout) {
                // This can occur only in case of scheduleWork
                logger.warn("REJECTED: duration= {0}", duration);
                workListener.workRejected(new WorkEvent(this, WorkEvent.WORK_REJECTED, work, null));
                return;
            }
            workListener.workStarted(new WorkEvent(this, WorkEvent.WORK_STARTED, work, null));
        }

        // Setup ExecutionContext
        Xid xid = null;
        if (executionContext != null) {
            xid = executionContext.getXid();
            if (xid != null) {
                long txtimeout = executionContext.getTransactionTimeout();
                try {
                    if (txtimeout != WorkManager.UNKNOWN) {
                        this.transactionComponent.begin(xid, txtimeout);
                    } else {
                        this.transactionComponent.begin(xid);
                    }
                } catch (NotSupportedException e) {
                    throw new WorkException("Error starting a new transaction", e);
                } catch (SystemException e) {
                    throw new WorkException("Error starting a new transaction", e);
                }
            }
        }

        try {
            work.run();
            // Notify the listener that the work is completed.
            if (workListener != null) {
                workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, work, null));
            }
        } catch (Exception e) {
            if (workListener != null) {
                workListener.workCompleted(new WorkEvent(this, WorkEvent.WORK_COMPLETED, work, null));
            }
            throw new WorkCompletedException(e);
        } finally {
            if (xid != null) {
                this.transactionComponent.clearThreadTx();
            }
        }
    }

    /**
     * @return the waiting time.
     */
    public long getWaitingTime() {
        return this.waitingTime;
    }

    /**
     * Sets the waiting time.
     * @param waitingTime the time to wait
     */
    public void setWaitingTime(final long waitingTime) {
        this.waitingTime = waitingTime;
    }

    /**
     * Do the next JWork object to be run.
     * @throws WorkException if work is not done
     * @throws InterruptedException if one object can't wait.
     * @throws ResourceWorkManagerStoppedException if the manager is stopped.
     */
    public void nextWork() throws WorkException, InterruptedException, ResourceWorkManagerStoppedException {
        ResourceWork run = null;
        boolean haswait = false;
        synchronized (this.workList) {
            while (this.workList.isEmpty()) {
                if ((haswait && this.freeThreads > this.minpoolsz) || this.stopped) {
                    this.poolsz--;
                    throw new ResourceWorkManagerStoppedException("Manager is stopped");
                }
                try {
                    this.freeThreads++;
                    this.workList.wait(this.waitingTime);
                    this.freeThreads--;
                    haswait = true;
                } catch (InterruptedException e) {
                    this.freeThreads--;
                    this.poolsz--;
                    throw e;
                }
            }
            run = this.workList.removeFirst();
            // In case startWork() was called
            synchronized (run) {
                logger.debug("Starting a new work");
                run.setStarted();
                run.notify();
            }
        }
        doMyWork(run.getWork(), run.getTimeout(), run.getExecutionContext(), run.getWorkListener(), run
                .getCreationTime());
    }

    /**
     * Remove this WorkManager : Stop all threads.
     */
    public synchronized void stopThreads() {
        this.stopped = true;
        notifyAll();
        poolnumber--;
    }

    /**
     * Init method.<br/>
     * This method is called before the start method.
     * @throws EZBComponentException if the initialization has failed.
     */
    public void init() throws EZBComponentException {
        // init
    }

    /**
     * Start method.<br/>
     * This method is called after the init method.
     * @throws EZBComponentException if the start has failed.
     */
    public void start() throws EZBComponentException {

        if (this.transactionComponent == null) {
            logger.error("Component disabled as there is no transaction manager available");
            this.minpoolsz = 0;
            this.maxpoolsz = 0;
            return;
        }

        // Build threads for work.
        for (this.poolsz = 0; this.poolsz < this.minpoolsz; this.poolsz++) {
            ResourceWorkThread resourceWorkThread = new ResourceWorkThread(this, poolnumber, threadnumber++);
            resourceWorkThread.setDaemon(true);
            resourceWorkThread.start();
        }
        logger.info("Settings: minThreads={0},maxThreads={1},txTimeout={2}s", Integer.valueOf(this.minpoolsz), Integer
                .valueOf(this.maxpoolsz), Long.valueOf(this.waitingTime / ONE_SECOND));
    }

    /**
     * Stop method.<br/>
     * This method is called when component needs to be stopped.
     * @throws EZBComponentException if the stop is failing.
     */
    public void stop() throws EZBComponentException {
        // Stop threads of the workmanager
        stopThreads();
        logger.info("WorkManager Stopped");
    }

    /**
     * @return transaction component.
     */
    public TMComponent getTransactionComponent() {
        return this.transactionComponent;
    }

    /**
     * Sets the transaction component.
     * @param transactionComponent the given transaction component.
     */
    public void setTransactionComponent(final TMComponent transactionComponent) {
        this.transactionComponent = transactionComponent;
    }


}
TOP

Related Classes of org.ow2.easybeans.component.jca.workmanager.ResourceWorkManagerComponent

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.