org.apache.servicemix.beanflow.Workflow Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of servicemix-beanflow Show documentation
Show all versions of servicemix-beanflow Show documentation
A library for orchestrating beans and asynchronous events using
regular POJOs rather than XML
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.servicemix.beanflow;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.servicemix.beanflow.support.EnumHelper;
import org.apache.servicemix.beanflow.support.Interpreter;
import org.apache.servicemix.beanflow.support.ReflectionInterpreter;
import java.util.Timer;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* An activity which implements a more traditional workflow model where each
* method represents a transition.
*
* @version $Revision: $
*/
public class Workflow extends JoinSupport {
private static final Log log = LogFactory.getLog(Workflow.class);
private Executor executor;
private Interpreter interpreter;
private Timer timer = new Timer();
private AtomicBoolean suspended = new AtomicBoolean();
private BlockingQueue queue = new LinkedBlockingQueue();
/**
* TODO is there a way to reference the parameter type of this class?
*
* public Workflow() { this(T); }
*/
@SuppressWarnings("unchecked")
public Workflow(Class enumType) {
this((T) getFirstStep(enumType));
}
public Workflow(T firstStep) {
this(Executors.newSingleThreadExecutor(), firstStep);
}
public Workflow(Executor executor, T firstStep) {
this(executor, new ReflectionInterpreter(), firstStep);
}
public Workflow(Executor executor, Interpreter interpreter, T firstStep) {
this.executor = executor;
this.interpreter = interpreter;
if (firstStep instanceof Enum) {
validateStepsExist(firstStep.getClass());
}
addStep(firstStep);
}
/**
* Returns the next step which will be executed asynchronously
*/
public T getNextStep() {
return queue.peek();
}
/**
* Adds a step to be executed after the current step completes processing
*/
public void addStep(T stepName) {
suspended.set(false);
queue.add(stepName);
executor.execute(this);
}
public void run() {
while (!isStopped()) {
try {
T stepToExecute = queue.poll();
if (stepToExecute != null) {
if (log.isDebugEnabled()) {
log.debug("About to execute step: " + stepToExecute);
}
interpreter.executeStep(stepToExecute, this);
}
else {
break;
}
}
catch (RuntimeException e) {
log.warn("Caught: " + e, e);
}
}
}
/**
* Forks one or more child activities
*/
public void fork(TimeoutActivity... activities) {
for (TimeoutActivity activity : activities) {
activity.start();
}
}
/**
* Forks one or more child activities
*/
public void fork(long timeout, TimeoutActivity... activities) {
for (TimeoutActivity activity : activities) {
activity.scheduleTimeout(timer, timeout);
activity.start();
}
}
/**
* Creates a join such that when all of the activities are completed the
* given step will be executed
*/
public void joinAll(T joinedStep, long timeout, Activity... activities) {
JoinAll joinFlow = new JoinAll(activities);
join(joinFlow, joinedStep, timeout);
}
/**
* Performs a join with the given join activity condition, advancing to the
* specified joinedStep when the join takes place using the given timeout to
* the join
*/
public void join(JoinSupport joinFlow, T joinedStep, long timeout) {
// when the join completes move to the next step
joinFlow.onStop(createGoToStepTask(joinedStep));
// start the join activity and register the timeout
fork(timeout, joinFlow);
}
/**
* Suspends the workflow processing. The workflow will then wait for an
* external event before restarting the activity
*/
public void suspend() {
suspended.set(true);
}
/**
* Returns true if the workflow is in a suspended state where it is waiting
* for an external event to cause the workflow to resume
*/
public boolean isSuspended() {
return suspended.get();
}
/**
* Returns true if this workflow has a next step to execute
*/
public boolean isNextStepAvailable() {
return !queue.isEmpty();
}
/**
* Creates a task which will move to the given step
*/
public Runnable createGoToStepTask(final T joinedStep) {
return new Runnable() {
public void run() {
addStep(joinedStep);
}
};
}
/**
* Called when a step fails to execute
*/
public void onStepException(String stepName, Exception e) {
log.warn("Step failed: " + stepName + ". Reason: " + e, e);
suspend();
fail("Failed to execute step: " + stepName + ". Reason: " + e, e);
}
@Override
protected void onChildStateChange(int childCount, int stoppedCount, int failedCount) {
}
/**
* Lets validate the steps exist on an enumerated type.
*
* Thanks to Sam Pullara for this idea :)
*/
protected void validateStepsExist(Class enumType) {
Object[] values = null;
try {
values = EnumHelper.getEnumValues(enumType);
}
catch (Exception e) {
fail("Cannot get the values of the enumeration: " + enumType.getName(), e);
}
if (values != null) {
interpreter.validateStepsExist(values, this);
}
}
protected static Object getFirstStep(Class enumType) {
try {
Object[] values = EnumHelper.getEnumValues(enumType);
return values[0];
}
catch (Exception e) {
throw new IllegalArgumentException("Could not find the values for the enumeration: " + enumType.getName() + ". Reason: " + e, e);
}
}
}