package org.jbpm.jpdl.par;
import org.jbpm.AbstractJbpmTestCase;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.instantiation.ConfigurableClassLoadersTest;
import org.jbpm.instantiation.ProcessClassLoader;
import org.jbpm.util.ClassLoaderUtil;
/**
* Test case for ProcessClassLoader hierarchy and setting the ContextClassLoader correctly.
* Relates to {@link ConfigurableClassLoadersTest}.
*
* @author Tom Baeyens
* @author bernd.ruecker@camunda.com
*/
public class ProcessClassLoaderTest extends AbstractJbpmTestCase {
public static class TestClassLoader extends ClassLoader {
public TestClassLoader(ClassLoader parent) {
super(parent);
}
}
static int contextLoadedActionInvocations;
static ClassLoader originalClassLoader;
protected void setUp() throws Exception {
super.setUp();
contextLoadedActionInvocations = 0;
originalClassLoader = Thread.currentThread().getContextClassLoader();
}
public static class DefaultLoadedAction implements ActionHandler {
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext executionContext) throws Exception {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
assertSame(ProcessClassLoader.class, contextClassLoader.getClass());
// verify that the default uses the jbpm-lib-classloader
assertSame(ClassLoaderUtil.class.getClassLoader(), contextClassLoader.getParent());
contextLoadedActionInvocations++;
}
}
/*
* DOES NOT configure usage of the context classloader. So this tests the default (backwards
* compatible) behaviour. so the classloading hierarchy of DefaultLoadedAction should be
* ProcessClassloader -> jbpm-lib classloader
*/
public void testDefaultClassLoader() {
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>"
+ " <start-state name='start'>"
+ " <transition to='state'>"
+ " <action class='"
+ DefaultLoadedAction.class.getName()
+ "' />"
+ " </transition>"
+ " </start-state>"
+ " <state name='state'>"
+ " <transition to='end'/>"
+ " </state>"
+ "</process-definition>");
// create the process instance
ProcessInstance processInstance = new ProcessInstance(processDefinition);
processInstance.signal();
assertEquals(1, contextLoadedActionInvocations);
}
public static class ContextLoadedAction implements ActionHandler {
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext executionContext) throws Exception {
ClassLoader processClassLoader = Thread.currentThread().getContextClassLoader();
assertSame(ProcessClassLoader.class, processClassLoader.getClass());
ClassLoader testClassLoader = processClassLoader.getParent();
assertSame(TestClassLoader.class, testClassLoader.getClass());
assertSame(originalClassLoader, testClassLoader.getParent());
contextLoadedActionInvocations++;
}
}
/**
* configures usage of the context class loader. the classloading hierarchy of
* ContextLoadedAction will be ProcessClassLoader -> TestClassLoader -> originalClassLoader
*/
public void testContextClassLoader() {
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.parseXmlString("<jbpm-configuration>"
+ " <jbpm-context />"
+ " <string name='jbpm.class.loader' value='context' />"
+ "</jbpm-configuration>");
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
ClassLoader testClassLoader = new TestClassLoader(originalClassLoader);
Thread.currentThread().setContextClassLoader(testClassLoader);
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>"
+ " <start-state name='start'>"
+ " <transition to='state'>"
+ " <action class='"
+ ContextLoadedAction.class.getName()
+ "' />"
+ " </transition>"
+ " </start-state>"
+ " <state name='state'>"
+ " <transition to='end'/>"
+ " </state>"
+ "</process-definition>");
// create the process instance
ProcessInstance processInstance = new ProcessInstance(processDefinition);
processInstance.signal();
assertEquals(1, contextLoadedActionInvocations);
assertSame(testClassLoader, Thread.currentThread().getContextClassLoader());
}
finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
jbpmContext.close();
}
}
/*
* a third test should set the testcontextClassLoader in the test and then let the action
* throw an exception. Then it should be verified that the original classloader is still
* restored correctly. Easiest is to start from a copy of the testContextClassLoader
*/
public static class ContextLoadedExceptionAction implements ActionHandler {
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext executionContext) throws Exception {
ClassLoader processClassLoader = Thread.currentThread().getContextClassLoader();
assertSame(ProcessClassLoader.class, processClassLoader.getClass());
ClassLoader testClassLoader = processClassLoader.getParent();
assertSame(TestClassLoader.class, testClassLoader.getClass());
assertSame(originalClassLoader, testClassLoader.getParent());
contextLoadedActionInvocations++;
throw new JbpmException("simulate exception");
}
}
public void testContextClassLoaderException() {
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.parseXmlString("<jbpm-configuration>"
+ " <jbpm-context />"
+ " <string name='jbpm.class.loader' value='context' />"
+ "</jbpm-configuration>");
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
ClassLoader testClassLoader = new TestClassLoader(originalClassLoader);
try {
Thread.currentThread().setContextClassLoader(testClassLoader);
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString("<process-definition>"
+ " <start-state name='start'>"
+ " <transition to='state'>"
+ " <action class='"
+ ContextLoadedExceptionAction.class.getName()
+ "' />"
+ " </transition>"
+ " </start-state>"
+ " <state name='state'>"
+ " <transition to='end'/>"
+ " </state>"
+ "</process-definition>");
// create the process instance
ProcessInstance processInstance = new ProcessInstance(processDefinition);
processInstance.signal();
}
catch (JbpmException ex) {
assertEquals(1, contextLoadedActionInvocations);
assertEquals("simulate exception", ex.getMessage());
assertSame(testClassLoader, Thread.currentThread().getContextClassLoader());
}
finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
jbpmContext.close();
}
}
}