[jBPM] - JUnit test support for jBPM5
by Maciej Swiderski
Maciej Swiderski [http://community.jboss.org/people/swiderski.maciej] modified the document:
"JUnit test support for jBPM5"
To view the document, visit: http://community.jboss.org/docs/DOC-17345
--------------------------------------------------------------
There are number of issues that people are facing while working with jBPM and to get help the most useful elements are:
* process definition (BPMN2 file)
* test case that illustrates mentioned issue
As process definition is concerned that is rather straight forward (since we already working with it when encountering the problem),
but with test case it can be bit more complex to give directly to others the driver we use for our case.
So, after struggling for some time with exactly the same issue, I decided to start with small but yet powerful support for test cases for jBPM.
There is already a test case class that we could extend and that already brings some useful elements but that is not all we need...
I tried to build up a small library that is dedicated to test cases based on JUnit and that aligns with latest releases of JUnit, meaning it is
based on annotations. So to move directly what I am referring to, lets take a look at very simple test case:
@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2"})
@KnowledgeSession()
public class JbpmJUnitTestCase {
protected org.drools.KnowledgeBase knowledgeBase;
protected StatefulKnowledgeSession session;
@Test
public void testScriptTask() {
ProcessInstance instance = session.startProcess("ScriptTask");
assertProcessInstanceComplete(instance);
}
}
What you can see here is the most basic test case that allows to execute and assert process instance completion. I think the code is self explanatory so
I jump directly to the annotations that were introduced with this library:
+*@RunWith*+ - this one is standard JUnit annotation that instructs what runner should be used to execute tests
+*@KnowledgeBase*+ - annotation that defines what artifacts should be loaded into knowledge base
Annotation attributes:
* source - list of resources to be loaded (currently only BPMN2 resources are supported)
* sharedKey - name of the key that will be used to cache knowledge based between test cases, so if you have set of resources that are used across various
test cases and you want to avoid building knowledge base for each of the test cases sharedKey attribute should be set to load knowledge base only one time
+*@KnowledgeSession*+ - annotation that defines how stateful sessions should be created and maintained
Annotation attributes:
* disposePerTest - defines if session should be disposed after each test within test case, default set to true
* logger - defines what working memory logger should be configured for the session, available options NONE (default), CONSOLE, JPA
* handlers - set of work item handlers that will be registered for each session (WorkItemHandler annotation)
* persistence - defines persistence for the session (Persistence annotation)
+*@Persistence*+ annotation attributes:
* persistenceUnit - name of the persistence unit that should be used while configuring JPA entity manager factory
* reloadSession - defines if session should be reloaded (with the same id) between test invocation within test case, default false
+*@WorkItemHandler*+ annotation attributes:
* taskName - name of the task the handler is registered for
* handles - class of the handler
+*@HumanTaskSupport*+ - annotation that instructs runner that test case requires Human Task interactions so it will initialize Task Server accessible via MINA transport
it will be started on test case scope, meaning that all tests will have access to the same instance of the server within test case.
Annotation attributes:
* users - list of user ids that should be added to the task server on startup
* groups - list of groups that should be added to the task server on startup
* persistanceUnit - name of the persistence unit that should be used while building entity manager factory
* host - interface on which task server should be started, defaults to localhost
* port - port on which task server should be started, defaults to 9123
+*@LifeCycle*+ - annotation that lets developer to configure what operation should be executed for particulat human task.
Annotation attributes:
* phases - list of human task life cycle phases (claim, start, complete, fail, skip, etc) defined as enumeration LifeCyclePhase
This annotation is intended to simplify work with human tasks while writing unit tests - very experimental!!!
In addition to annotation there are two property files required when configuring session persistence or human task server:
* datasource.properties - defines parameters of connection pool
* jndi.properties - defines JNDI provider for bitronix
NOTE: Connection pool will be created for entire test phase once datasource.properties file was discovered.
Obviously following files are required as well, since they are required by jbpm and task server artifacts:
* META-INF/persistence.xml that defines both (jbpm and human task persistence units)
* META-INF/JBPMorm.xml
* META-INF/Taskorm.xml
Main concept is around injecting dependencies into your test case class. As dependencies I mean following items:
* Knowledge base
* Stateful session
* Task client
Methods from JUnit life cycle are still available, such as these annotated with @Before, @After, @BeforeClass, @AfterClass. Instance methods (@Before @After) will
have access to session and knowledge base, for instance if some facts should be inserted before running the test.
There is one more item that is worth to mention, an assert class dedicated to jbpm - JbpmAssert, which defines (at the moment):
* assertProcessInstanceComplete
* assertProcessInstanceActive
* assertProcessInstanceAborted
* assertProcessInstancePending
* assertProcessInstanceSuspended
and most likely more to come...
*And now, its time to show some examples of it*
Simple test case with session persistence:
@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2"})
@KnowledgeSession(persistence=@Persistence(persistenceUnit="org.jbpm.persistence.jpa"))
public class JbpmJUnitTestCase {
protected org.drools.KnowledgeBase knowledgeBase;
protected StatefulKnowledgeSession session;
@Test
public void testScriptTask() {
ProcessInstance instance = session.startProcess("ScriptTask");
assertProcessInstanceComplete(instance);
}
}
If session reload is desired between tests test case should be defined as follows:
@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2"})
@KnowledgeSession(persistence=@Persistence(persistenceUnit="org.jbpm.persistence.jpa", reloadSession=true))
public class JbpmJUnitTestCase {
protected org.drools.KnowledgeBase knowledgeBase;
protected StatefulKnowledgeSession session;
@Test
public void testScriptTask() {
ProcessInstance instance = session.startProcess("ScriptTask");
assertProcessInstanceComplete(instance);
}
@Test
public void testScriptTask2() {
ProcessInstance instance = session.startProcess("ScriptTask");
assertProcessInstanceComplete(instance);
}
}
Both tests will use same session.
Last but not least, human interaction test case:
@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2","usertask.bpmn2"}, sharedKey="common")
@KnowledgeSession(handlers={@WorkItemHandler(taskName="Human Task", handler=WSHumanTaskHandler.class)}, logger=Logger.CONSOLE)
@HumanTaskSupport(persistenceUnit="org.jbpm.task", users={"john", "Administrator"})
public class JbpmJUnitRunnerTest {
protected org.drools.KnowledgeBase knowledgeBase;
protected StatefulKnowledgeSession session;
protected TaskClient taskClient;
@Test
public void testHumanTask() throws Exception {
ProcessInstance instance = session.startProcess("UserTask");
Thread.sleep(1000);
BlockingTaskSummaryResponseHandler taskSummaryHandler = new BlockingTaskSummaryResponseHandler();
taskClient.getTasksAssignedAsPotentialOwner("john", "en-UK", taskSummaryHandler);
assertEquals(1, taskSummaryHandler.getResults().size());
TaskSummary task1 = taskSummaryHandler.getResults().get(0);
BlockingTaskOperationResponseHandler taskOperationHandler = new BlockingTaskOperationResponseHandler();
taskClient.claim(task1.getId(), "john", taskOperationHandler);
taskOperationHandler = new BlockingTaskOperationResponseHandler();
taskClient.start(task1.getId(), "john", taskOperationHandler);
taskOperationHandler.waitTillDone(1000);
taskOperationHandler = new BlockingTaskOperationResponseHandler();
taskClient.complete(task1.getId(), "john", null, taskOperationHandler);
taskOperationHandler.waitTillDone(1000);
Thread.sleep(1000);
assertProcessInstanceComplete(instance);
}
}
In the example we can notice several configurations elements:
* knowledge base is configured to be shared between test cases under 'common' shared key
* knowledge session will have registered handler for 'Human Task' work items
* knowledge session will have a console logger configured
* human task server will be configured with 'org.jbpm.task' persistence unit and will add two users once it's started (john, Administrator)
As mentioned on the comments, working with human tasks is still quite complex so an alternative approach is introduced:
@RunWith(JbpmJUnitRunner.class)
@KnowledgeBase(source={"script.bpmn2","service.bpmn2","usertask.bpmn2"}, sharedKey="common")
@KnowledgeSession(handlers={@WorkItemHandler(taskName="Service Task", handler=ServiceTaskHandler.class),
@WorkItemHandler(taskName="Human Task", handler=WSHumanTaskHandler.class)}, logger=Logger.CONSOLE)
@HumanTaskSupport(persistenceUnit="org.jbpm.task", users={"john", "Administrator"})
public class JbpmJUnitRunnerTest {
protected org.drools.KnowledgeBase knowledgeBase;
protected StatefulKnowledgeSession session;
protected TestTaskClient taskClient;
@Test
(a)LifeCycle(phases={LifeCyclePhase.CLAIM, LifeCyclePhase.START, LifeCyclePhase.COMPLETE})
public void testHumanTask() throws Exception {
ProcessInstance instance = session.startProcess("UserTask");
taskClient.performLifeCycle("john", "en-UK", null);
assertProcessInstanceComplete(instance);
}
}
So, here you can see a quite substantial change in code lines to achieve exactly the same result as previous example. So at the method level you can define what life cycle operation of human task you would like to invoke, using @LifeCycle annotation. That of course is interesting if you would like to invoke exactly the same life cycle phases for all your human tasks within test case, for instance testing positive path for the entire process.
But if you would like to have it on task instance level you can specify the phases on execution of performLifeCycel method of testTaskClient.
An extension of regular TaskClient was introduced (TestTaskClient) that was equiped with two methods:
performLifeCycle(String user, String locale, HashMap input)
performLifeCycle(String user, String locale, HashMap input, LifeCyclePhase[] phases)
obviously it does not cover all possible use cases but as it was said it is experimental and will be enhanced hopefully soon (if you find it useful).
Ok, so that is it! I hope you can provide some feedback about it and hopefully we can get a valuable discussion around it and what's even more important
maybe we start using it on daily basis so everybody could benefit from it.
Attached draft version of the library.
Looking forward to your comments
P.S.
If you are interested in the complete module you can find it at: https://github.com/mswiderski/jbpm/tree/master/jbpm-test-support https://github.com/mswiderski/jbpm/tree/master/jbpm-test-support It is now merged with the jBPM code base as additional module
--------------------------------------------------------------
Comment by going to Community
[http://community.jboss.org/docs/DOC-17345]
Create a new document in jBPM at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&co...]
13 years
[jBPM] - Re: Does jBPM support clustering for load balancing?
by Maciej Swiderski
Maciej Swiderski [http://community.jboss.org/people/swiderski.maciej] created the discussion
"Re: Does jBPM support clustering for load balancing?"
To view the discussion, visit: http://community.jboss.org/message/634602#634602
--------------------------------------------------------------
Thanks, now I pretty much understood what you try to achieve.
Just one last question - are you going to use persistence of your sessions (StatefulKnowledgeSession)?
If so, you need to make sure that particular session will be loaded from db only by one server to avoid troubles when persisting sessions (potential overriding each other). You mentioned that:
> Morioka Ryoichi wrote:
>
> When web app recieve user's request, web app send the msg to BPM engine. And BPM engine execute "making new session of the flow" or "starting and completing the task".
so every request will create new session? One option would be to correlate userId with session so every user will have its own session that will be bound to session life cycle of the web application, and problem with same session loaded on both servers will be solved if your load balancer will use "sticky sessions".
Another thing, if you consider to have task service and jbpm engine running in the same JVM, you should take a look at latest implementation of local task service so you will not execute it over the network (for instance mina). It should be released with 5.2 version but it is already available as nightly build.
Hope this helps
--------------------------------------------------------------
Reply to this message by going to Community
[http://community.jboss.org/message/634602#634602]
Start a new discussion in jBPM at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=1&cont...]
13 years
[JBoss Cache] - JBoss cache. Concurrency issue
by Anton K
Anton K [http://community.jboss.org/people/aaauser] created the discussion
"JBoss cache. Concurrency issue"
To view the discussion, visit: http://community.jboss.org/message/636461#636461
--------------------------------------------------------------
I'm using +JBossCache 'Malagueta' 3.2.0.GA+
I faced with strange issue in production environment, sometimes writes to jboss are lost. I tried to reproduce this situation with simple java application
public static void testCache() {
Cache cache = new DefaultCacheFactory().createCache(false);
cache.create();
cache.start();
final Node node = cache.getRoot().addChild(Fqn.fromString("/child1"));
int threadsCount = 20;
final CyclicBarrier b = new CyclicBarrier(threadsCount);
for (int i = 0; i < threadsCount; i++) {
final long j = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
b.await();
String name = RandomGenerator.getRandomName(4);
node.put(j, name);
String nameFromCache = (String) node.get(j);
if (!name.equals(nameFromCache)) {
System.out.println("error");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
>From time to time, this test output "error", which i run from static void mai. 1 of 3 runs return "error" msg, node simply return "null". I can't reproduce it on every machine
--------------------------------------------------------------
Reply to this message by going to Community
[http://community.jboss.org/message/636461#636461]
Start a new discussion in JBoss Cache at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=1&cont...]
13 years
[Datasource Configuration] - Caused by: java.lang.RuntimeException: javax.naming.NameNotFoundException: comp not bound
by Srinivasan Purushothaman
Srinivasan Purushothaman [http://community.jboss.org/people/cyberjack] created the discussion
"Caused by: java.lang.RuntimeException: javax.naming.NameNotFoundException: comp not bound"
To view the discussion, visit: http://community.jboss.org/message/636289#636289
--------------------------------------------------------------
+*Hi Guys,*+
+*I get the below error when i try to deploy my Java Web Application application in Jboss 5.0.1 with Oracle Database 9i and JDK 1.6*+
*Caused by: java.lang.RuntimeException: javax.naming.NameNotFoundException: comp not bound*
+*I referred all over the internet and I coouldnt find a concrete solution, but wherever i saw they suggested there is some issue with my Oracle Datasource configuration.*+
+*I have pasted all the configuration changes that I made for Datasource below, please suggest me what is that I am doing wrong !!!!!!!!*+
+*1. oracle-ds.xml*+
<datasources>
<local-tx-datasource>
<jndi-name>my_ds</jndi-name>
<connection-url>jdbc:oracle:thin:@myserver:port:sid</connection-url>
<driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
<user-name>username</user-name>
<password>password</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>50</max-pool-size>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter</exception-sorter-class-name>
<metadata>
<type-mapping>Oracle9i</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>
+*2. jboss-web.xml*+
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<resource-ref>
<res-ref-name>my_ds</res-ref-name>
<res-type>oracle.jdbc.pool.OracleDataSource</res-type>
<jndi-name>java:/my_ds</jndi-name>
</resource-ref>
</jboss-web>
+*3. web.xml*+
<resource-ref>
<description>My Data Source</description>
<res-ref-name>my_ds</res-ref-name>
<res-type>oracle.jdbc.pool.OracleDataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
I have attached the server log file too.
--------------------------------------------------------------
Reply to this message by going to Community
[http://community.jboss.org/message/636289#636289]
Start a new discussion in Datasource Configuration at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=1&cont...]
13 years
[jBPM] - StatefulKnowledgeSession in custom work item handlers
by Amin Mohammed-Coleman
Amin Mohammed-Coleman [http://community.jboss.org/people/amin-mc] created the discussion
"StatefulKnowledgeSession in custom work item handlers"
To view the discussion, visit: http://community.jboss.org/message/636282#636282
--------------------------------------------------------------
Hello
My application provides the ability to register custom work item handlers. Users register an instance of the custom handlers and when i create a session i get these handlers and register them against the statefulknowledgesession. The problem is that some of these handlers might need access to the StatefulKnowledgesession however I can't find the best way to inject the session into these handlers. For example I decided to use StatefulKnowledgeSessionAware interface where i set the session if the handler implements the handler. The problem with this is that there is a single instance of the handler and i run the risk that the handler might have a session injected when it is currently already in use by another session.
What would be great is to have some kind of handler with a method signature
public void executeWorkItemWithSession(WorkItem workItem, WorkItemManager workItemManager, StatefulKnowledgeSession)
that way it doesn't matter that there is only one instance of the handler everytime the handler is invoked the session is injected into the method. Is this possible to achieve? Can i do some extension to support this?
Any help would be appreciated.
--------------------------------------------------------------
Reply to this message by going to Community
[http://community.jboss.org/message/636282#636282]
Start a new discussion in jBPM at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=1&cont...]
13 years
Re: [jboss-user] [jBPM] - JUnit test support for jBPM5
by Maciej Swiderski
Maciej Swiderski [http://community.jboss.org/people/swiderski.maciej] commented on the document
"JUnit test support for jBPM5"
To view all comments on this document, visit: http://community.jboss.org/docs/DOC-17345#comment-8186
--------------------------------------------------
Mauricio, Tihomir
first of all thanks for taking the time to look at it and for your comments. That was whole idea to post is as early draft to collect your thoughts since everybody has its own way of working with jBPM especially on the unit test phase.
Mauricio, I think both code bases can be merged to enhance the overall functionality of the test framework.
Tihomir, main idea behind using annotations rather than inheritance is that it is easer to get it when using custom JUnit runner (at least I find it easier). Of course I agree with you that it should be improved to reduce number of code lines required to write your tests (I admit I have not spent too much time on human tasks yet). But it has already some of these characteristics, for instance shared knowledge base so it will be built only once which in my eyes can be beneficial in quite some cases.
Regarding hiding setup code behind annotations - in my opinion writing a test case is more like you know what you are doing and you just want to make sure it is tested now and in the future. If you would like to get to know the framework you probably need to start with the getting started guide, api docs, etc rather than unit tests, that is my two cents ;)
By the way, if you have any ideas what would fit here I would be more than happy to include them.
P.S.
Currently I am away from my regular computer so cannot push it to the git repo, hopefully beginning of next week it will be available.
Cheers
--------------------------------------------------
13 years