package org.jbpm.job.executor;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import org.jbpm.db.AbstractDbTestCase;
import org.jbpm.graph.def.Action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.ProcessInstance;
public class JobExecutorDbTest extends AbstractDbTestCase {
private static final int INSTANCE_COUNT = 20;
static Set collectedResults = Collections.synchronizedSet(new TreeSet());
static List allocatedProcessIds = new Vector();
protected void setUp() throws Exception {
super.setUp();
// [JBPM-2115] multiple threads not supported on DB2 < 9.7
// multiple threads not supported on HSQL
String dialect = getHibernateDialect();
if (dialect.indexOf("DB2") == -1 && dialect.indexOf("HSQL") == -1) {
jbpmConfiguration.getJobExecutor().setNbrOfThreads(4);
}
}
protected void tearDown() throws Exception {
jbpmConfiguration.getJobExecutor().setNbrOfThreads(1);
super.tearDown();
}
public void testJobExecutor() {
deployProcessDefinition();
startProcessInstances();
processJobs();
assertEquals(createExpectedResults(), collectedResults);
}
void deployProcessDefinition() {
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition name='bulk messages'>"
+ " <start-state>"
+ " <transition to='a' />"
+ " </start-state>"
+ " <node name='a' async='true'>"
+ " <action class='"
+ AutomaticActivity.class.getName()
+ "' />"
+ " <transition to='b' />"
+ " </node>"
+ " <node name='b' async='true'>"
+ " <event type='node-enter'>"
+ " <action name='X' async='true' class='"
+ AsyncAction.class.getName()
+ "' />"
+ " </event>"
+ " <action class='"
+ AutomaticActivity.class.getName()
+ "' />"
+ " <transition to='c' />"
+ " </node>"
+ " <node name='c' async='true'>"
+ " <action class='"
+ AutomaticActivity.class.getName()
+ "' />"
+ " <transition to='d'>"
+ " <action name='Y' async='true' class='"
+ AsyncAction.class.getName()
+ "' />"
+ " </transition>"
+ " </node>"
+ " <node name='d' async='true'>"
+ " <action class='"
+ AutomaticActivity.class.getName()
+ "' />"
+ " <transition to='e' />"
+ " <event type='node-leave'>"
+ " <action name='Z' async='true' class='"
+ AsyncAction.class.getName()
+ "' />"
+ " </event>"
+ " </node>"
+ " <node name='e' async='true'>"
+ " <action class='"
+ AutomaticActivity.class.getName()
+ "' />"
+ " <transition to='end' />"
+ " </node>"
+ " <end-state name='end'/>"
+ "</process-definition>");
deployProcessDefinition(processDefinition);
}
void startProcessInstances() {
for (int i = 0; i < INSTANCE_COUNT; i++) {
ProcessInstance processInstance = jbpmContext.newProcessInstanceForUpdate("bulk messages");
processInstance.signal();
}
}
static final char[] nodeNames = { 'a', 'b', 'c', 'd', 'e', 'X', 'Y', 'Z' };
static Set createExpectedResults() {
Set expectedResults = new TreeSet();
NumberFormat formatter = NumberFormat.getIntegerInstance();
formatter.setMinimumIntegerDigits(Integer.toString(INSTANCE_COUNT).length());
StringBuffer text = new StringBuffer();
FieldPosition position = new FieldPosition(NumberFormat.INTEGER_FIELD);
for (int e = 0; e < INSTANCE_COUNT; e++) {
text.setLength(0);
formatter.format(e, text, position);
text.append('\0');
int lastIndex = text.length() - 1;
for (int c = 0; c < nodeNames.length; c++) {
text.setCharAt(lastIndex, nodeNames[c]);
expectedResults.add(text.toString());
}
}
return expectedResults;
}
public static class AutomaticActivity implements ActionHandler {
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext executionContext) throws Exception {
long id = executionContext.getProcessInstance().getId();
String procIndex = getProcessIndex(id);
String nodeName = executionContext.getNode().getName();
collectedResults.add(procIndex + nodeName);
executionContext.leaveNode();
}
}
public static class AsyncAction implements ActionHandler {
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext executionContext) throws Exception {
long id = executionContext.getProcessInstance().getId();
String procIndex = getProcessIndex(id);
Action action = executionContext.getAction();
String actionName = action.getName();
collectedResults.add(procIndex + actionName);
}
}
static synchronized String getProcessIndex(long id) {
Long identifier = new Long(id);
if (!allocatedProcessIds.contains(identifier)) allocatedProcessIds.add(identifier);
int procIndex = allocatedProcessIds.indexOf(identifier);
return procIndex < 10 ? "0" + procIndex : Integer.toString(procIndex);
}
}