Nick S: Created a JBPMEngineUnitTest which uses much less of the Spring context and is much faster to run.

Also created a OneToManyMap and a OneToManyBiMap to help mock up the NamespaceService.

*** IMPORTANT!!! ***
The test requires a new database schema. The schema has the same name as the original schema but has _test after it, i.e. ${db.name}_test


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@15746 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
N Smith
2009-08-14 10:58:14 +00:00
parent abafb8f4e5
commit 1dfbefe4d5
15 changed files with 916 additions and 124 deletions

View File

@@ -2667,7 +2667,7 @@ public class JBPMEngine extends BPMEngine
}
catch (RuntimeException re)
{
throw new IllegalStateException("Invalid company home path: " + companyHomePath + ": " + re.getMessage());
throw new IllegalStateException("Invalid company home path: " + companyHomePath + ": " + re.getMessage(), re);
}
}
else

View File

@@ -73,7 +73,6 @@ public class JBPMEngineTest extends BaseSpringTest
WorkflowPackageComponent packageComponent;
WorkflowDefinition testWorkflowDef;
NodeRef testNodeRef;
@Override
protected void onSetUpInTransaction() throws Exception

View File

@@ -0,0 +1,225 @@
/*
* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.workflow.jbpm;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.transaction.TransactionDefinition.*;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import junit.framework.TestCase;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.i18n.MessageService;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.NamespaceServiceMemoryImpl;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.transaction.SpringAwareUserTransaction;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springmodules.workflow.jbpm31.JbpmTemplate;
/**
* JBPMEngine Unit Tests.
*
* @author Nick Smith
*/
public class JBPMEngineUnitTest extends TestCase
{
private static final NodeRef companyHome = new NodeRef("for://test/home");
private static final ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] { "classpath:test/alfresco/test-database-context.xml",
"classpath:test/alfresco/test-workflow-context.xml", });
private JBPMEngine engine = new JBPMEngine();
private WorkflowDefinition testWorkflowDef;
private SpringAwareUserTransaction transaction;
public void testStartWorkflowWithoutPackage() throws Exception
{
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
engine.startWorkflow(testWorkflowDef.getId(), params);
}
@Override
protected void setUp() throws Exception
{
super.setUp();
// Mock up various services.
NodeService nodeService = mock(NodeService.class);
TenantService tenantService = makeTenantService();
NamespaceService namespaceService = makeNamespaceService();
DictionaryService dictionaryService = makeDictionaryService();
// Add services to ServiceRegistry
ServiceRegistry serviceRegistry = mock(ServiceRegistry.class);
when(serviceRegistry.getNodeService()).thenReturn(nodeService);
when(serviceRegistry.getNamespaceService()).thenReturn(namespaceService);
when(serviceRegistry.getDictionaryService()).thenReturn(dictionaryService);
JbpmTemplate jbpmTemplate = (JbpmTemplate) ctx.getBean("test_jbpm_template");
// Set up the JBPMEngine.
engine.setJBPMTemplate(jbpmTemplate);
engine.setTenantService(tenantService);
engine.setNodeService(nodeService);
engine.setServiceRegistry(serviceRegistry);
engine.setNamespaceService(namespaceService);
engine.setMessageService(mock(MessageService.class));
engine.setDictionaryService(dictionaryService);
engine.setEngineId("jbpm_test");
// Need to register JBPMEngine with bean factory so WorflowTaskInstance
// can load it.
ctx.getBeanFactory().registerSingleton("test_jbpm_engine", engine);
// Deploy test workflow process definition to JBPM.
startTransaction();
deployTestDefinition();
}
@SuppressWarnings("unchecked")
private DictionaryService makeDictionaryService()
{
DictionaryService service = mock(DictionaryService.class);
// DictionaryService.getType(QName) always returns a mock
// TypeDefinition.
TypeDefinition typeDef = mock(TypeDefinition.class);
when(service.getType((QName) any())).thenReturn(typeDef);
// DictionaryService.getAnonymousType(QName, Collection<QName>)
// always returns a mock TypeDefinition
when(service.getAnonymousType((QName) any(),//
(Collection<QName>) any()))//
.thenReturn(typeDef);
return service;
}
private void startTransaction() throws NotSupportedException, SystemException
{
PlatformTransactionManager transactionManager = (PlatformTransactionManager) ctx
.getBean("testTransactionManager");
transaction = new SpringAwareUserTransaction(transactionManager, false, ISOLATION_DEFAULT,
PROPAGATION_REQUIRED, 1000);
transaction.begin();
}
// deploy test process definition
private void deployTestDefinition() throws IOException
{
ClassPathResource processDef = new ClassPathResource(
"jbpmresources/test_processdefinition.xml");
assertFalse(engine.isDefinitionDeployed(processDef.getInputStream(),
MimetypeMap.MIMETYPE_XML));
WorkflowDeployment deployment = engine.deployDefinition(processDef.getInputStream(),
MimetypeMap.MIMETYPE_XML);
testWorkflowDef = deployment.definition;
assertNotNull(testWorkflowDef);
assertEquals("jbpm_test$test", testWorkflowDef.name);
assertEquals("1", testWorkflowDef.version);
assertTrue(engine.isDefinitionDeployed(processDef.getInputStream(),
MimetypeMap.MIMETYPE_XML));
}
private NamespaceService makeNamespaceService()
{
NamespaceServiceMemoryImpl namespace = new NamespaceServiceMemoryImpl();
namespace.registerNamespace(NamespaceService.DEFAULT_PREFIX, NamespaceService.DEFAULT_URI);
namespace.registerNamespace("wf", "http://www.alfresco.org/model/bpm/1.0");
return namespace;
}
private TenantService makeTenantService()
{
TenantService tenantService = mock(TenantService.class);
// Tenant Service.isEnabled() returns true.
when(tenantService.isEnabled()).thenReturn(true);
// TenantService.getRootNode always returns companyHome.
when(tenantService.getRootNode((NodeService) any(),//
(SearchService) any(),//
(NamespaceService) any(),//
anyString(),//
(NodeRef) any()))//
.thenReturn(companyHome);
// Tenant Service.getName(String) will return the input param.
when(tenantService.getName(anyString())).thenAnswer(new Answer<String>()
{
public String answer(InvocationOnMock invocation) throws Throwable
{
return (String) invocation.getArguments()[0];
}
});
when(tenantService.getBaseName(anyString())).thenAnswer(new Answer<String>()
{
public String answer(InvocationOnMock invocation) throws Throwable
{
return (String) invocation.getArguments()[0];
}
});
return tenantService;
}
@Override
protected void tearDown() throws Exception
{
super.tearDown();
try
{
transaction.rollback();
}
// To prevent rollback exceptions hiding other exceptions int he unit
// test.
catch (Throwable t)
{
System.out.println(t.getStackTrace());
}
}
}

View File

@@ -6,8 +6,10 @@
<hibernate-mapping default-access="field">
<subclass name="org.alfresco.repo.workflow.jbpm.WorkflowTaskInstance"
extends="org.jbpm.taskmgmt.exe.TaskInstance"
discriminator-value="W"/>
<subclass name="org.alfresco.repo.workflow.jbpm.WorkflowTaskInstance"
extends="org.jbpm.taskmgmt.exe.TaskInstance" discriminator-value="W">
<property name="jbpmEngineName" type="string" length="50"
column="JBPM_ENGINE_NAME" />
</subclass>
</hibernate-mapping>

View File

@@ -22,6 +22,7 @@
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.workflow.jbpm;
import java.io.Serializable;
@@ -39,7 +40,6 @@ import org.springframework.beans.factory.access.BeanFactoryLocator;
import org.springframework.beans.factory.access.BeanFactoryReference;
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
/**
* Alfresco specific implementation of a jBPM task instance
*
@@ -49,29 +49,11 @@ public class WorkflowTaskInstance extends TaskInstance
{
private static final long serialVersionUID = 6824116036569411964L;
/** Alfresco JBPM Engine */
private String jbpmEngineName = null;
/** Alfresco JBPM Engine */
private static JBPMEngine jbpmEngine = null;
/**
* Gets the JBPM Engine instance
*
* @return JBPM Engine
*/
private JBPMEngine getJBPMEngine()
{
if (jbpmEngine == null)
{
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
jbpmEngine = (JBPMEngine)factory.getFactory().getBean("jbpm_engine");
if (jbpmEngine == null)
{
throw new WorkflowException("Failed to retrieve JBPMEngine component");
}
}
return jbpmEngine;
}
/**
* Construct
*/
@@ -92,13 +74,33 @@ public class WorkflowTaskInstance extends TaskInstance
}
/**
* Construct
* Sets jbpmEngineName which is used to get the JBPMEngine instance from a
* BeanFactory
*
* @param taskName
* @param jbpmEngineName the jbpmEngineName to set
*/
public WorkflowTaskInstance(String taskName)
public void setJbpmEngineName(String jbpmEngineName)
{
super(taskName);
this.jbpmEngineName = jbpmEngineName;
}
/**
* Gets the JBPM Engine instance
*
* @return JBPM Engine
*/
private JBPMEngine getJBPMEngine()
{
if (jbpmEngine == null)
{
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
if (jbpmEngineName == null) jbpmEngineName = "jbpm_engine";
jbpmEngine = (JBPMEngine) factory.getFactory().getBean(jbpmEngineName);
if (jbpmEngine == null) { throw new WorkflowException(
"Failed to retrieve JBPMEngine component"); }
}
return jbpmEngine;
}
@Override
@@ -111,30 +113,34 @@ public class WorkflowTaskInstance extends TaskInstance
@Override
public void end(Transition transition)
{
// Force assignment of task if transition is taken, but no owner has yet been assigned
// Force assignment of task if transition is taken, but no owner has yet
// been assigned
if (actorId == null)
{
actorId = AuthenticationUtil.getFullyAuthenticatedUser();
}
// Set task properties on completion of task
// NOTE: Set properties first, so they're available during the submission of
// task variables to the process context
// NOTE: Set properties first, so they're available during the
// submission of
// task variables to the process context
Map<QName, Serializable> taskProperties = new HashMap<QName, Serializable>();
Transition outcome = (transition == null) ? token.getNode().getDefaultLeavingTransition() : transition;
Transition outcome = (transition == null) ? token.getNode().getDefaultLeavingTransition()
: transition;
if (outcome != null)
{
taskProperties.put(WorkflowModel.PROP_OUTCOME, outcome.getName());
}
taskProperties.put(WorkflowModel.PROP_STATUS, "Completed");
getJBPMEngine().setTaskProperties(this, taskProperties);
// perform transition
super.end(transition);
if (getTask().getStartState() != null)
{
// if ending a start task, push start task properties to process context, if not
// if ending a start task, push start task properties to process
// context, if not
// already done
getJBPMEngine().setDefaultWorkflowProperties(this);

View File

@@ -22,13 +22,13 @@
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.workflow.jbpm;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.TaskInstanceFactory;
import org.jbpm.taskmgmt.exe.TaskInstance;
/**
* jBPM factory for creating Alfresco derived Task Instances
*
@@ -38,12 +38,26 @@ public class WorkflowTaskInstanceFactory implements TaskInstanceFactory
{
private static final long serialVersionUID = -8097108150047415711L;
private String jbpmEngineName;
/* (non-Javadoc)
* @see org.jbpm.taskmgmt.TaskInstanceFactory#createTaskInstance(org.jbpm.graph.exe.ExecutionContext)
/**
* @param jbpmEngine the jbpmEngine to set
*/
public void setJbpmEngine(String jbpmEngine)
{
this.jbpmEngineName = jbpmEngine;
}
/*
* (non-Javadoc)
* @see
* org.jbpm.taskmgmt.TaskInstanceFactory#createTaskInstance(org.jbpm.graph
* .exe.ExecutionContext)
*/
public TaskInstance createTaskInstance(ExecutionContext executionContext)
{
return new WorkflowTaskInstance();
WorkflowTaskInstance taskInstance = new WorkflowTaskInstance();
taskInstance.setJbpmEngineName(jbpmEngineName);
return taskInstance;
}
}

View File

@@ -1,47 +1,91 @@
<jbpm-configuration>
<jbpm-context>
<service name="persistence">
<factory>
<bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
<field name="isCurrentSessionEnabled"><true/></field>
<field name="isTransactionEnabled"><false/></field>
</bean>
</factory>
</service>
<service name="tx" factory="org.jbpm.tx.TxServiceFactory" />
<service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
<service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
<service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
<service name="authentication" factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
</jbpm-context>
<jbpm-configuration>
<!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
<string name="resource.business.calendar" value="org/jbpm/calendar/jbpm.business.calendar.properties" />
<string name="resource.default.modules" value="org/jbpm/graph/def/jbpm.default.modules.properties" />
<string name='resource.converter' value='org/alfresco/repo/workflow/jbpm/jbpm.converter.properties' />
<string name="resource.action.types" value="org/alfresco/repo/workflow/jbpm/jbpm.action.types.xml" />
<string name="resource.node.types" value="org/alfresco/repo/workflow/jbpm/jbpm.node.types.xml" />
<string name="resource.parsers" value="org/alfresco/repo/workflow/jbpm/jbpm.parsers.xml" />
<string name="resource.varmapping" value="org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml" />
<string name="resource.mail.templates" value="jbpm.mail.templates.xml" />
<jbpm-context>
<service name="persistence">
<factory>
<bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
<field name="isCurrentSessionEnabled">
<true />
</field>
<field name="isTransactionEnabled">
<false />
</field>
</bean>
</factory>
</service>
<service name="tx" factory="org.jbpm.tx.TxServiceFactory" />
<service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
<service name="scheduler"
factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
<service name="logging"
factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
<service name="authentication"
factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
</jbpm-context>
<!--
configuration resource files pointing to default configuration
files in jbpm-{version}.jar
-->
<string name="resource.business.calendar"
value="org/jbpm/calendar/jbpm.business.calendar.properties" />
<string name="resource.default.modules"
value="org/jbpm/graph/def/jbpm.default.modules.properties" />
<string name='resource.converter'
value='org/alfresco/repo/workflow/jbpm/jbpm.converter.properties' />
<string name="resource.action.types"
value="org/alfresco/repo/workflow/jbpm/jbpm.action.types.xml" />
<string name="resource.node.types"
value="org/alfresco/repo/workflow/jbpm/jbpm.node.types.xml" />
<string name="resource.parsers"
value="org/alfresco/repo/workflow/jbpm/jbpm.parsers.xml" />
<string name="resource.varmapping"
value="org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml" />
<string name="resource.mail.templates" value="jbpm.mail.templates.xml" />
<int name="jbpm.byte.block.size" value="1024" singleton="true" />
<string name="jbpm.mail.smtp.host" value="localhost" />
<bean name="jbpm.task.instance.factory"
class="org.alfresco.repo.workflow.jbpm.WorkflowTaskInstanceFactory"
singleton="true">
<field name="jbpmEngineName">
<string value="test_jbpm_engine" />
</field>
</bean>
<bean name="jbpm.variable.resolver" class="org.jbpm.jpdl.el.impl.JbpmVariableResolver"
singleton="true" />
<bean name="jbpm.mail.address.resolver" class="org.jbpm.identity.mail.IdentityAddressResolver"
singleton="true" />
<bean name="jbpm.job.executor" class="org.alfresco.repo.workflow.jbpm.AlfrescoJobExecutor">
<field name="jbpmConfiguration">
<ref bean="jbpmConfiguration" />
</field>
<field name="name">
<string value="AlfrescoJbpmJobExecutor" />
</field>
<field name="nbrOfThreads">
<int value="1" />
</field>
<field name="idleInterval">
<int value="90000" />
</field> <!-- 15 minutes -->
<field name="maxIdleInterval">
<int value="3600000" />
</field> <!-- 1 hour -->
<field name="historyMaxSize">
<int value="20" />
</field>
<field name="maxLockTime">
<int value="600000" />
</field> <!-- 10 minutes -->
<field name="lockMonitorInterval">
<int value="60000" />
</field> <!-- 1 minute -->
<field name="lockBufferTime">
<int value="5000" />
</field> <!-- 5 seconds -->
</bean>
<int name="jbpm.byte.block.size" value="1024" singleton="true" />
<string name="jbpm.mail.smtp.host" value="localhost" />
<bean name="jbpm.task.instance.factory" class="org.alfresco.repo.workflow.jbpm.WorkflowTaskInstanceFactory" singleton="true" />
<bean name="jbpm.variable.resolver" class="org.jbpm.jpdl.el.impl.JbpmVariableResolver" singleton="true" />
<bean name="jbpm.mail.address.resolver" class="org.jbpm.identity.mail.IdentityAddressResolver" singleton="true" />
<bean name="jbpm.job.executor" class="org.alfresco.repo.workflow.jbpm.AlfrescoJobExecutor">
<field name="jbpmConfiguration"><ref bean="jbpmConfiguration" /></field>
<field name="name"><string value="AlfrescoJbpmJobExecutor" /></field>
<field name="nbrOfThreads"><int value="1" /></field>
<field name="idleInterval"><int value="90000" /></field> <!-- 15 minutes -->
<field name="maxIdleInterval"><int value="3600000" /></field> <!-- 1 hour -->
<field name="historyMaxSize"><int value="20" /></field>
<field name="maxLockTime"><int value="600000" /></field> <!-- 10 minutes -->
<field name="lockMonitorInterval"><int value="60000" /></field> <!-- 1 minute -->
<field name="lockBufferTime"><int value="5000" /></field> <!-- 5 seconds -->
</bean>
</jbpm-configuration>

View File

@@ -0,0 +1,73 @@
/* Copyright (C) 2005-2009 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.namespace;
import java.util.Collection;
import org.alfresco.util.OneToManyHashBiMap;
/**
* A basic implementation of the NamespaceService interface intended for use in
* unit tests. This implementation does not persist any changes beyond the
* lifetime of the object.
*
* @author Nick Smith
*/
public class NamespaceServiceMemoryImpl implements NamespaceService
{
// URI to Prefix map.
private final OneToManyHashBiMap<String, String> map = new OneToManyHashBiMap<String, String>();
public void registerNamespace(String prefix, String uri)
{
map.putSingleValue(uri, prefix);
}
public void unregisterNamespace(String prefix)
{
map.removeValue(prefix);
}
public String getNamespaceURI(String prefix) throws NamespaceException
{
return map.getKey(prefix);
}
public Collection<String> getPrefixes(String namespaceURI) throws NamespaceException
{
return map.get(namespaceURI);
}
public Collection<String> getPrefixes()
{
return map.flatValues();
}
public Collection<String> getURIs()
{
return map.keySet();
}
}