BpelInstanceWorker.java :  » ESB » apache-ode-2.0 » org » apache » ode » bpel » engine » Java Open Source

Java Open Source » ESB » apache ode 2.0 
apache ode 2.0 » org » apache » ode » bpel » engine » BpelInstanceWorker.java
package org.apache.ode.bpel.engine;

import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.Semaphore;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ode.bpel.iapi.BpelEngineException;

/**
 * Objects used for synchronizing the execution of instance-level work. All work on behalf of an instance is funneled to one of
 * these objects. For all practical purposes these are singletons, with the caveat that they expire as soon as all work for an
 * instance is complete so they may be recreated on-demand. The effect is that all work for an instance occurs in a single thread.
 * 
 * @author Maciej Szefler <mszefler at gmail dot com>
 * 
 */
class BpelInstanceWorker implements Runnable {

    private static final Log __log = LogFactory.getLog(BpelInstanceWorker.class);
    
    final ODEProcess _process;

    final Long _iid;

    final Contexts _contexts;

    private boolean _running = false;

    private ArrayList<Runnable> _todoQueue = new ArrayList<Runnable>();

    private final ThreadLocal<Long> _activeInstance = new ThreadLocal<Long>();
    
    private Thread _workerThread;

    private CachedState _cachedState;

    BpelInstanceWorker(ODEProcess process, Long iid) {
        _process = process;
        _iid = iid;
        _contexts = _process._contexts;
    }

    Long getIID() {
        return _iid;
    }

    /**
     * Add a task for this instance.
     * 
     * @param runnable
     */
    synchronized void enqueue(Runnable runnable) {
        __log.debug("enqueue: for instance " +  _process.getPID() + "#" + _iid + ": " + runnable);
        _todoQueue.add(runnable);
        // We mayh need to reschedule this thread if we've dropped out of the end of the run() method.
        if (!_running) {
            _running = true;
            _process.enqueueRunnable(this);
        }
    }
 
    /**
     * Execute some work on behalf of the instance, but don't do it in the worker thread, instead do it in the calling thread. Why
     * bother? Well, sometimes we need to do some work in the current thread because it is associated with some transaction we'd
     * like to use, but we don't want to go through the suspend/resume BS. Ok, so why not just do the work directly? Well we want to
     * do the work as if it was occuring in our worker thread, so we have to block it for the duration of the action.
     * 
     * 
     * @param <T>
     *            parameterization of {@link Callable}
     * @param callable
     *            the thing to call
     * @return return value of the callble
     * @throws Exception
     *             forwarded from {@link Callable#call()}
     */
    <T> T execInCurrentThread(Callable<T> callable) throws Exception {

        // Allow recursive invocations. This allows us to nest P2P invocations to an arbitrary depth.
        if (isWorkerThread())
            return doInstanceWork(callable);
        
        final Semaphore ready = new Semaphore(0);
        final Semaphore finished = new Semaphore(0);
        enqueue(new Runnable() {
            public void run() {
                ready.release();
                try {
                    finished.acquire();
                } catch (InterruptedException ie) {
                    __log.error("Thread interrupted.", ie);
                    throw new BpelEngineException("Thread interrupted.", ie);
                }
            }
        });
        
        try {
            ready.acquire();
        } catch (InterruptedException ex) {
            __log.error("Thread interrupted.", ex);
            throw new BpelEngineException("Thread interrupted.", ex);
        }


        try {
            return doInstanceWork(callable);
        } finally {
            finished.release();
        }

    }

   

    /**
     * Implementation of the {@link Runnable} interface.
     */
    public void run() {
        __log.debug("Starting worker thread " + Thread.currentThread() + " for instance IID " + instanceId());
        _activeInstance.set(_iid);
        _workerThread = Thread.currentThread();
        try {

            do {
                final Runnable next;
                synchronized (this) {
                    if (_todoQueue.isEmpty()) {
                        // This is the only way to drop out of this method short of some disasterous error. This is
                        // important since we need to synchronize _running with _todoQueue state.
                        _running = false;
                        __log.debug("Worker thread " + Thread.currentThread() + " for instance IID " + _iid + " ran out of work. ");
                        return;
                    }

                    next = _todoQueue.remove(0);
                }

                try {
                    doInstanceWork(new Callable<Void>() {
                        public Void call() throws Exception {
                            next.run();
                            return null;
                        }
                    });
                } catch (Throwable t) {
                    // This is rather serious as it is too late to recover at this point. 
                    __log.fatal("Unexpected error in instance " + _iid + " thread " + Thread.currentThread() + 
                            "; the error was not handled, it is likely that this has corrupted the state of the" +
                            "instance!", t);
                    // TODO: use the recovery mechanism to mark the instance as corrupted. 
                }
            } while (true);
        } finally {
            _activeInstance.set(null);
            _workerThread = null;
        }
    }

    
    /**
     * Wrapper routine for all instance work. Set a break-point here if you'd like to follow the execution of an instance.
     * 
     * @param <T>
     * @param work
     * @return
     * @throws Exception
     */
    private <T> T doInstanceWork(Callable<T> work) throws Exception {
        __log.debug("Doing work for instance " + instanceId() +" in thread " + Thread.currentThread());
        _activeInstance.set(_iid);
        try {
            return work.call();
        } catch (Exception ex) {
            __log.error("Work for instance " + instanceId() + " in thread "  + Thread.currentThread() + " resulted in an exception." ,ex);
            throw ex;
        } finally {
            _activeInstance.set(null);
            __log.debug("Finished work for instance " + instanceId() + " in thread " + Thread.currentThread());
        }
    }

    public String toString() {
        return "{BpelInstanceWorker for " + instanceId() + "}";
    }

    boolean isWorkerThread() {
        return _activeInstance.get() != null;
    }

    Object getCachedState(Object uuid) {
        CachedState cs = _cachedState;
        if (cs != null && cs.uuid.equals(uuid))
            return cs.state;
        return null;
    }
    
    void setCachedState(Object uuid, Object state) {
        _cachedState = new CachedState(uuid, state);
    }
    
    private class CachedState {
        final Object uuid;
        final Object state;
        
        CachedState(Object uuid, Object state) {
            this.uuid = uuid;
            this.state = state;
        }
    }
    
    private String instanceId() {
        return _process.getPID() + "#" + _iid;
    }
    
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.