Build your own activiti task explorer with CDI and JSF 2

September 17 2011 by Daniel Meyer · 18 Comments

Outdated!

Please note that this blog post is outdated, since we forked Activiti in March 2013 and started a new Open Source BPM project called camunda BPM (www.camunda.org). You will find this topic and loads of more best practices, blueprints etc. there, so you should just have a look :-)


A couple of days ago, I promised that I would unveil “first class activiti support in Java EE 6”. Today I will give you a first taste. But keep in mind: what I am showing below is cool, but: it is by far not the coolest thing we have currently lined up. So stay tuned.

Many of our customers embed Activiti in their own applications, in many instances to support task or case management. In such a situation we do not use activiti explorer but implement task management ourselves, tightly integrated in our application. Today I want to show how we can use the activiti-cdi module to implement human task management. The activiti-cdi module is an activiti contribution by camunda (I am the module lead) and we have made huge progress over the last months. In this article I showcase some of the features of the current trunk, which will be released with activiti 5.8 (released on October 1st). We will see that in order to create JSF task forms, in simple cases we do not even have to write Java code. At the same time, we have the full power of Java EE 6 at our fingertips, if we need more. So today, we are going to build JSF based task management in under an hour!

Let’s start with an example

So without further introduction, let’s just jump right into it. We start by creating a simple BPMN 2.0 process:

So a very basic twitter quality assurance process, I submit a tweet (which starts the process), my manager has a look at it, if he decides that the tweet is in compliance with company policy, it is published, if not, it is rejected.

Project setup and activiti configuration

Now let’s start implementing this with activiti and Java EE 6. First step: create a maven project. I need activiti-cdi and a couple of other dependencies:

<dependencies>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-cdi</artifactId>
		<version>5.8-SNAPSHOT</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context</artifactId>
		<version>3.0.3.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>javax</groupId>
		<artifactId>javaee-api</artifactId>
		<version>6.0</version>
		<scope>provided</scope>
	</dependency>
</dependencies>

Activiti-cdi transitively pulls in activiti-engine and spring-beans. spring-context is required because we want to configure the process engine using spring and want to look up container (Jboss AS 7) managed resources in JNDI:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- activiti configuration for jboss AS 7 -->

	<!-- lookup the JTA-Transaction manager -->
	<bean id="transactionManager" class="org.springframework.jndi.JndiObjectFactoryBean">
		<property name="jndiName" value="java:jboss/TransactionManager"></property>
		<property name="resourceRef" value="true" />
	</bean>

	<!-- process engine configuration -->
	<bean id="processEngineConfiguration" class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
		<property name="dataSourceJndiName" value="java:jboss/datasources/ExampleDS" />
		<property name="databaseType" value="h2" />
		<property name="transactionManager" ref="transactionManager" />
		<property name="transactionsExternallyManaged" value="true" />
		<property name="databaseSchemaUpdate" value="true" />
	</bean>

</beans>

We could also configure activiti differently, for example programmatically or using seam3-config. In that case we would not need spring. But hey, why not use spring for configuration – we have to give them something, right?

At startup activiti-cdi will try to boot the process engine. To accomplish this it will lookup a bean implementing the “ProcessEngineLookup” interface. We provide one, extending LocalProcessEngine lookup, which will create a spring-configured process engine:

public class ProcessEngineConfiguration extends LocalProcessEngineLookup {
}

So if I do not want to use Spring I could initialize a process engine programmatically here. Finally, we create a file called processes.xml where we list the processes we want to deploy:

<?xml version="1.0" encoding="utf-8" ?>
<!-- list the processes to be deployed -->
<processes>
	<process resource="diagrams/twitterProcess.bpmn20.xml" />
</processes>

Ok, now setup is complete and we are good to go!

Displaying a list of deployed processes:

To start off, we want to display a list of processes which are deployed, which could look like this:

Ok, so admittedly we could improve the layout :) but that can be delegated to any UI expert who is familiar with HTML and CSS.
Activiti cdi makes the repository service available for injection, so implementing this is easy:

public class ProcessList {

  @Inject
  private RepositoryService repositoryService;

  @Produces
  @Named("processDefinitionList")
  public List getProcessDefinitionList() {
    return repositoryService.createProcessDefinitionQuery()
            .list();
  }
}

To display the list, we create a simple jsf page:

<h:dataTable value="#{processDefinitionList}" var="v_process">
	<h:column>
		<f:facet name="header">Key</f:facet>
		#{v_process.key}
	</h:column>
	<h:column>
		<f:facet name="header">Name</f:facet>
		#{v_process.name}
	</h:column>
	<h:column>
		<f:facet name="header">Version</f:facet>
		#{v_process.version}
	</h:column>
	<h:column>
		<f:facet name="header">Action</f:facet>
		<h:outputLink value="#{formService.getStartFormData(v_process.id).formKey}">
			Start
			<f:param name="processDefinitionKey" value="#{v_process.key}"></f:param>
		</h:outputLink>
	</h:column>
</h:dataTable>

The last column is the most interesting; let’s look at it in more detail. We are generating a link. We retrieve the activiti:formKey specified on the start event:

<startEvent id="startevent1" name="Start"
                           activiti:formKey="taskForm_newTweet.jsf” />

And add a GET parameter, in this case the key of the process definition. So this will generate the following link:

taskForm_newTweet.jsf?processDefinitionKey=tweetProcess

Starting the process

Ok, now we have to create the form which starts the process. The code looks like this:

<f:metadata>
<!-- bind the key of the process to be started -->
	<f:viewParam name="processDefinitionKey" />
</f:metadata>

<h1>New Tweet</h1>
<h:form>
	<table>
		<tr>
			<td>Your twitter account:</td>
			<td><h:inputText value="#{processVariables['twitterAccount']}" />					</td>
		</tr>
		<tr>
			<td>Tweet content:</td>
			<td><h:inputText value="#{processVariables['tweetContent']}" />
			</td>
		</tr>
		<tr>
			<td></td>
			<td><h:commandButton value="Submit"
							action="#{businessProcess.startProcessByKey(processDefinitionKey)}" />
			</td>
		</tr>
	</table>
</h:form>

In the f:metadata section, we „bind“ the GET parameter, we added to the link. This allows us to reference the value provided in the URL. We do this when starting the process using the commandButton in the last line:

#{businessProcess.startProcessByKey(processDefinitionKey)}

I am referencing a bean named businessProcess here. This bean is the “heart piece” of activiti-cdi and is built for simplifying the implementation of user tasks (for a deeper discussion, see the last section of this post). For now, it is enough to understand that the difference between businessProcess.startProcessByKey(String) and  runtimeService.startProcessByKey(String) is that in addition to starting the referenced process, the business process bean flushes any process variables I have created in the current unit of work, either by setting them explicitly through businessProcess.setVariable(‘key’, value) or using the processVariables bean which is a java.util.Map<String,Object>. This is exactly what we do in the above case, using #{processVariables['twitterAccount']}.

What is cool here is that

  • we are not required to write a backing bean which collects the process variables and starts the process,
  • it is very simple to access additional (domain) data,
  • we could use a sequence of forms as a Java EE 6 conversation (e.g. a Wizard), in that case activiti cdi would cache the process variables in the conversation,
  • the f:metadata and submit part of the form are completely generic, so it is easy to write a facelets template which makes this reusable!

Creating a tasklist

Creating a tasklist is very similar to displaying a list of process instances. Again we start with the backing beans.

public class TaskList {

  @Inject
  private CurrentUser currentUser;

  @Inject
  private TaskService taskService;

  @Produces
  @Named("personalTaskList")
  public List getPersonalTaskList() {
     return taskService.createTaskQuery()
           .taskAssignee(currentUser.getUsername())
           .list();
  }

}

Where the current user is another simple bean:

@Named
@SessionScoped
public class CurrentUser implements Serializable {

  private String username = "kermit";

  public void setUsername(String username) { this.username = username; }
  public String getUsername() { return username; }

}

The current user could be populated at login, in this case, we add an input field above the tasklist to be able to display tasks for arbitrary users:

<h1>List of assigned Tasks</h1>
<h:form>
	<p>
		Tasks for user:
		<h:inputText value="#{currentUser.username}">
			<f:ajax render="taskList" event="keyup" />
		</h:inputText>
	</p>
	<h:dataTable value="#{personalTaskList}" var="v_task" id="taskList">
		<h:column>
			<f:facet name="header">Task Name</f:facet>
			#{v_task.name}
		</h:column>
		<h:column>
			<f:facet name="header">Description</f:facet>
			#{v_task.description}
		</h:column>
		<h:column>
			<f:facet name="header">Action</f:facet>
			<h:outputLink
				value="#{formService.getTaskFormData(v_task.id).formKey}">
				Complete
				<f:param name="taskId" value="#{v_task.id}"></f:param>
			</h:outputLink>
		</h:column>
	</h:dataTable>
</h:form>

The “complete” link is implemented in the same fashion as the “start” link in the start process list. This brings us to the last step, implementing task forms.

Implement task forms with JSF 2


Again, we reference the jsf form in the process:

<userTask id="reviewTweet" name="Review Tweet"
                         activiti:assignee="kermit" activiti:formKey="taskForm_reviewTweet.jsf"></userTask>

The following is a jsf task form. Notice again how everything but the form fields themselves is completely generic and could be made reusable using templates:

<f:metadata>
	<f:viewParam name="taskId" />
	<!-- start working on the usertask and starting a conversation -->
	<f:event type="preRenderView" listener="#{businessProcess.startTask(taskId, true)}" />
</f:metadata>

<h1>#{task.name}</h1>
<p><em>#{task.description}</em></p>

<h:form>
<table>
		<tr>
			<td>Twitter Account:</td>
			<td><h:outputText value="#{processVariables['twitterAccount']}" /></td>
		</tr>
		<tr>
			<td>Tweet content:</td>
			<td><h:outputText value="#{processVariables['tweetContent']}" />
			</td>
		</tr>
		<tr>
			<td>Approve?</td>
			<td><h:selectBooleanCheckbox value="#{processVariables['approved']}" /></td>			</tr>
		<tr>
			<td>Comment:</td>
			<td><h:inputTextarea value="#{processVariables['comment']}" /></td>
		</tr>
		<tr>
			<td></td>
			<td>
				<h:commandButton value="Submit" action=#{businessProcess.completeTask(true)}" />
			</td>
		</tr>
	</table>
</h:form>

Let’s look at the f:metadata section in more detail. We are calling businessProcess.startTask(taskId, true), passing in the id of the task retrieved from the view parameters. By calling this method we are basically saying to the businessProcess bean “We are now working on the task with id 10”. As we do not like to repeat ourselves, we say that we also want to start a conversation (if none is already active). So the businessProcess bean will “remember” that we are working on that task for the lifetime of the current conversation. That means that while this conversation lasts, we are able to retrieve process variables which are resolved in the execution of the current task. As I wrote earlier, a conversation spans multiple HTTP requests, so this is particularly useful if we use AJAX requests or have a sequence of multiple forms representing a single usertask.

In this case we are ending the task / conversation using the submit button: businessProcess.completeTask(true). (true=end conversation) This also flushes the process variables we have changed to the execution. So behind the scenes taskService.completeTask(id, variables) is called, with the difference that activiti-cdi “knows” which task to complete and which process variables to flush, so that we do not have to do that ourselves, for each task we implement.

And that’s it! Now we have activiti usertasks in JSF! You can check out the complete project from our camunda fox SVN:
https://svn.camunda.com/fox/trunk/demo/activiti5-cdi-jsf-task-management/

The Deal with the “businessProcess” Bean

Now that we have seen an example of how to use activiti-cdi to be implement usertasks, I want to add a few more details about the “businessProcess” bean we used. The general concept is outlined in the following graphic:

The business process bean can be in two states: “associated” or not “associated”. If it is not associated, we can use it to create transient process variables, which are cached. If we call startProcess() later the process variables are flushed to the new process instance. If the business process bean is associated with a task or an execution, we can use it to retrieve process variables located in that execution. If we change the value of the process variables, the changes are not immediately flushed to the execution; on the contrary, only if we successfully complete the current unit of work, either through completeTask() or signalExecution(), the variables are flushed. So in a sense the businessProcess bean behaves like a JPA entity manager.

This allows us to scope the business process bean to a logical unit of work in our application. By default it will associate with the CDI conversation context. If the conversation context is not available i.e. during the invocation of a MDB or during the processing of a JAX-RS request, it will associate with the CDI request context. If no such context is available i.e. when the activiti job executor makes the call, it associates with the current Thread.

So to sum this up, the business process bean allows us to manage two items of “state”, (1) we can  use it to remember which task / execution we are working on and (2) which process variables we have modified. Whenever we implement something like a usertask, we have to think about how to manage that state, so I hope that activiti-cdi provides a usable generic solution to this.

These are some of the concepts we treat in our new training Java Enterprise Applications & Activiti.

The next blog will be about how you can skip the part named "Project setup and activiti configuration" above. That is possible on camunda fox - our brand new enterprise bpm platform based on Activiti.

Daniel Meyer, camunda BPM Project Lead

Daniel Meyer is project lead for camunda BPM. He is passionate about BPM with a particular focus on process automation and process engines. His mission is to deliver the best possible experience to developers who want to use BPM technology in their applications. He is a Java EE 6 aficionado and believes in polyglot technology stacks based on open standards.

18 Kommentare zu Build your own activiti task explorer with CDI and JSF 2

  1. Thanks for this interesting article.
    I just downloaded the project from SVN and deployed it to Glassfish 3.1.1, a JEE6 server.

    During deployment i get the following messages:
    INFO: Initializing activiti-cdi.
    SCHWERWIEGEND: No valid EE environment for injection of com.camunda.fox.demo.twitter.jsf.configuration.ProcessEngineConfiguration

    INFO: ProcessEngine default created
    INFO: initialised process engine default
    SCHWERWIEGEND: No valid EE environment for injection of org.activiti.cdi.impl.util.ActivitiServices

    is at the moment only JBoss supported?

  2. Hi Chris,

    sorry for not getting back to you on this sooner: activiti-cdi should work with all Java EE 6 compatible servers (including glassfish). What I had to do was to edit the activiti configuration to use a glassfish provided transaction manager and datasource (I used mysql):

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans&quot;
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"&gt;

    <!– activiti configuration for glassfish application server –>

    <!– lookup the JTA-Transaction manager –>
    <bean id="transactionManager" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:appserver/TransactionManager"></property>
    <property name="resourceRef" value="true" />
    </bean>

    <!– process engine configuration –>
    <bean id="processEngineConfiguration" class="org.activiti.cdi.CdiJtaProcessEngineConfiguration">
    <property name="dataSourceJndiName" value="jdbc/activiti" />
    <property name="databaseType" value="mysql" />
    <property name="transactionManager" ref="transactionManager" />
    <property name="transactionsExternallyManaged" value="true" />
    <property name="databaseSchemaUpdate" value="true" />

    <property name="mailServerHost" value="localhost" />
    <property name="mailServerPort" value="2525" />

    </bean>

    </beans>

    If this does not help, please do not hesitate to ask more questions.

  3. Hi,

    I downloaded and ran the example on JBoss 7.0.2.

    I had to add JUnit 4.9 as a runtime dependency to the POM to prevent exceptions on deployment.

    Thanks,

  4. Hi Paul,

    thx for trying out the example! This is definitely an issue, I filed it in JIRA:
    http://jira.codehaus.org/browse/ACT-990

    Daniel

  5. Hey,

    I checkd the project out and importet as a maven-project in eclipse.
    After that i build it and deployed the .war on a tomcat server.

    I got an error, The request resource () in not avaliable.

    Is it possible to run it on an tomcat?
    I’m on the right path or i’m doing something wrong?

    Is there some example more for, creating UI

    Thanks for your Help.

    Regards

  6. Hi Chris,

    the project is designed to be deployed on a Java EE 6 web profile server like Jboss 7 or Glassfish 3, etc.

    if you want to run it on tomcat, you need to add and configure cdi (e.g. Weld or Open Web Beans) yourself and potentially also additional libraries.

    Regards,
    Daniel

  7. Hey Daniel,

    ok, i get it to run an a jBoss 7 Server.

    Now, I have an other Question. Is it possible to model a BPMN 2.0 process with the Eclipse Designer and use the .xml file for the webapp or must i use jpdl 4?

    Thank you very much for your Help.

    Best Regards

  8. Hi Daniel,

    i m new to java web development and wanna start this tutorial.

    there are no problems with other examples from JBOSS such as login, guessnumber, helloworld, etc.

    But everytime i try to deploy this tutorial with JBOSS 7.0.2 i get this

    10:49:27,974 ERROR [org.jboss.as] (MSC service thread 1-4) JBoss AS 7.0.2.Final “Arc” started (with errors) in 4415ms – Started 95 of 151 services (1 services failed or missing dependencies, 55 services are passive or on-demand)
    10:49:27,976 INFO [org.jboss.as.server.controller] (DeploymentScanner-threads – 2) Deployment of “jsf-task-management.war” was rolled back with failure message {“Failed services” => {“jboss.deployment.unit.\”jsf-task-management.war\”.STRUCTURE” => “org.jboss.msc.service.StartException in service jboss.deployment.unit.\”jsf-task-management.war\”.STRUCTURE: Failed to process phase STRUCTURE of deployment \”jsf-task-management.war\”"}}
    10:49:27,977 INFO [org.jboss.as.controller] (DeploymentScanner-threads – 2) Service status report
    Services which failed to start:
    service jboss.deployment.unit.”jsf-task-management.war”.STRUCTURE: org.jboss.msc.service.StartException in service jboss.deployment.unit.”jsf-task-management.war”.STRUCTURE: Failed to process phase STRUCTURE of deployment “jsf-task-management.war”

    10:49:27,978 INFO [org.jboss.as.server.deployment] (MSC service thread 1-4) Stopped deployment jsf-task-management.war in 0ms
    10:49:27,980 ERROR [org.jboss.as.deployment] (DeploymentScanner-threads – 1) {“Composite operation failed and was rolled back. Steps that failed:” => {“Operation step-2″ => {“Failed services” => {“jboss.deployment.unit.\”jsf-task-management.war\”.STRUCTURE” => “org.jboss.msc.service.StartException in service jboss.deployment.unit.\”jsf-task-management.war\”.STRUCTURE: Failed to process phase STRUCTURE of deployment \”jsf-task-management.war\”"}}}}

    i use eclipse indigo and installed activiti 5.8 within eclipse.

    Btw,
    my maven is apache maven 3.0.3.

    Can u pls take a look at it where the problem could be?

    thank you very much

    regards

  9. Hi Daniel,

    i still have not given up, but it still doesnt work also.

    Since there is new version of JBoss 7.1, i use it, but still it cannot deploy the project i downloaded via svn.

    I import the project as “existing maven project”.

    What is the typical cause of the error?

    regards

  10. Hi Daniel,

    sorry for not getting back to you on this sooner.

    I upgraded the activiti dependency to 5.9-SNAPSHOT.
    You can update and make an “mvn clean install”.

    I was able to deploy the project as is to Jboss 7.0.2.Final without changes.

    Daniel

  11. Hi Daniel Meyer,

    thanks for the respond.

    It still doesnt work. So i used a virtual machine of a friend and it works. strange. We use same configuration in eclipse. All plugins are all the same. it works on his notebook but not on mine. the only difference is only the OS, xp and vista.

    btw,
    i tried the example, cool stuff.

    After modification to get a smaller process such as

    none startevent -> usertask new tweet (asignee : currentuser; formkey:taskform_newtweet.jsf) -> usertask review tweet (asignee : currentuser; formkey:taskform_rewiewtweet.jsf) -> service task publish -> endevent

    after clicking “start” in the processlist.jsf, it wont redirect to the taskForm_newTweet.jsf.
    it stays forever in the page processlist.jsf.

    why wont it start the process?
    i thought the none start even will start any process without any trigger’s specific condition.
    (maybe i understand it wrong from user guide)
    there is no error warning at all.

    thank s before

    regards

    Daniel Sim

  12. Hi Daniel,

    if in the example there is only 1 user “kermit”.

    Is it possible that there is no task list to complete a task?

    just like direct to change tweet if not approved and direct to publish tweet if approved?

    regards

    Daniel

  13. Hi Daniel Meyer,

    i tried the following and it works.

    Similar process like u have,
    but instead of ReviewTweet i have a service task.

    And then from ChangeTweet direct to the service task without exclusive gateway.

    Is there any reason why should we use the exclusive gateway in the case of your example?

    i havent tried it with your example.

    regards

  14. Hi

    Could someone please explain to me how exactly the project needs to be deployed to jboss?
    I’d also would be interested in how the acititi.cfg.xml should look like in order to run it with tomcat.

    regards

  15. hi,

    Do you have this sample project posted for download somewhere?

    Danke

  16. This is great stuff! I was wondering if this can work in a Tomcat web container only…I mean no full JEE application service. And, if so, is there a sample, please?

    Thank you

  17. Or even better, Tomcat 7 and Spring :) I just noticed one more person expressed a similar concern…

    Alles gute!

    Cristian

  18. application *server

Schreiben Sie einen Kommentar

Powered by WP Hashcash