2.4. Seam and jBPM: the todo list example
jBPM provides sophisticated functionality for workflow and task management. To get a small taste of how jBPM integrates with Seam, we'll show you a simple "todo list" application. Since managing lists of tasks is such core functionality for jBPM, there is hardly any Java code at all in this example.
2.4.1. Understanding the code
The center of this example is the jBPM process definition. There are also two JSPs and two trivial JavaBeans (There was no reason to use session beans, since they do not access the database, or have any other transactional behavior). Let's start with the process definition:
<process-definition name="todo">
<start-state name="start">
<transition to="todo"/>
</start-state>
<task-node name="todo">
<task name="todo" description="#{todoList.description}">
<assignment actor-id="#{actor.id}"/>
</task>
<transition to="done"/>
</task-node>
<end-state name="done"/>
</process-definition>
-
The <start-state>
node represents the logical start of the process. When the process starts, it immediately transitions to the todo
node.
-
The <task-node>
node represents a
wait state
, where business process execution pauses, waiting for one or more tasks to be performed.
-
The <task>
element defines a task to be performed by a user. Since there is only one task defined on this node, when it is complete, execution resumes, and we transition to the end state. The task gets its description from a Seam component named todoList
(one of the JavaBeans).
-
Tasks need to be assigned to a user or group of users when they are created. In this case, the task is assigned to the current user, which we get from a built-in Seam component named actor
. Any Seam component may be used to perform task assignment.
-
The <end-state>
node defines the logical end of the business process. When execution reaches this node, the process instance is destroyed.
Example 2.13.
If we view this process definition using the process definition editor provided by JBossIDE, this is what it looks like:
This document defines our
business process
as a graph of nodes. This is the most trivial possible business process: there is one
task
to be performed, and when that task is complete, the business process ends.
The first JavaBean handles the login screen login.jsp
. Its job is just to initialize the jBPM actor id using the actor
component. (In a real application, it would also need to authenticate the user.)
@Name("login")
public class Login {
@In
private Actor actor;
private String user;
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String login()
{
actor.setId(user);
return "/todo.jsp";
}
}
Example 2.14.
Here we see the use of @In
to inject the built-in Actor
component.
The JSP itself is trivial:
<%@ taglib uri="https://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="https://java.sun.com/jsf/core" prefix="f"%>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<f:view>
<h:form>
<div>
<h:inputText value="#{login.user}"/>
<h:commandButton value="Login" action="#{login.login}"/>
</div>
</h:form>
</f:view>
</body>
</html>
Example 2.15.
The second JavaBean is responsible for starting business process instances, and ending tasks.
@Name("todoList")
public class TodoList {
private String description;
public String getDescription()
{
return description;
}
public void setDescription(String description) {
this.description = description;
}
@CreateProcess(definition="todo")
public void createTodo() {}
@StartTask @EndTask
public void done() {}
}
-
The description property accepts user input form the JSP page, and exposes it to the process definition, allowing the task description to be set.
-
The Seam @CreateProcess
annotation creates a new jBPM process instance for the named process definition.
-
The Seam @StartTask
annotation starts work on a task. The @EndTask
ends the task, and allows the business process execution to resume.
Example 2.16.
In a more realistic example, @StartTask
and @EndTask
would not appear on the same method, because there is usually work to be done using the application in order to complete the task.
Finally, the meat of the application is in todo.jsp
:
<%@ taglib uri="https://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="https://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="https://jboss.com/products/seam/taglib" prefix="s" %>
<html>
<head>
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<f:view>
<h:form id="list">
<div>
<h:outputText value="There are no todo items." rendered="#{empty
taskInstanceList}"/>
<h:dataTable value="#{taskInstanceList}" var="task" rendered="#{not empty
taskInstanceList}">
<h:column>
<f:facet name="header">
<h:outputText value="Description"/>
</f:facet>
<h:inputText value="#{task.description}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Created"/>
</f:facet>
<h:outputText value="#{task.taskMgmtInstance.processInstance.start}">
<f:convertDateTime type="date"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Priority"/>
</f:facet>
<h:inputText value="#{task.priority}" style="width: 30"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Due Date"/>
</f:facet>
<h:inputText value="#{task.dueDate}" style="width: 100">
<f:convertDateTime type="date" dateStyle="short"/>
</h:inputText>
</h:column>
<h:column>
<s:button value="Done" action="#{todoList.done}" taskInstance="#{task}"/>
</h:column>
</h:dataTable>
</div>
<div>
<h:messages/>
</div>
<div>
<h:commandButton value="Update Items" action="update"/>
</div>
</h:form>
<h:form id="new">
<div>
<h:inputText value="#{todoList.description}"/>
<h:commandButton value="Create New Item" action="#{todoList.createTodo}"/>
</div>
</h:form>
</f:view>
</body>
</html>
Example 2.17.
Let's take this one piece at a time.
The page renders a list of tasks, which it gets from a built-in Seam component named taskInstanceList
. The list is defined inside a JSF form.
<h:form id="list">
<div>
<h:outputText value="There are no todo items." rendered="#{empty taskInstanceList}"/>
<h:dataTable value="#{taskInstanceList}" var="task" rendered="#{not empty taskInstanceList}">
...
</h:dataTable>
</div>
</h:form>
Each element of the list is an instance of the jBPM class TaskInstance
. The following code simply displays the interesting properties of each task in the list. For the description, priority and due date, we use input controls, to allow the user to update these values.
<h:column>
<f:facet name="header">
<h:outputText value="Description"/>
</f:facet>
<h:inputText value="#{task.description}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Created"/>
</f:facet>
<h:outputText value="#{task.taskMgmtInstance.processInstance.start}">
<f:convertDateTime type="date"/>
</h:outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Priority"/>
</f:facet>
<h:inputText value="#{task.priority}" style="width: 30"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Due Date"/>
</f:facet>
<h:inputText value="#{task.dueDate}" style="width: 100">
<f:convertDateTime type="date" dateStyle="short"/>
</h:inputText>
</h:column>
This button ends the task by calling the action method annotated @StartTask @EndTask
. It passes the task id to Seam as a request parameter:
<h:column>
<s:button value="Done" action="#{todoList.done}" taskInstance="#{task}"/>
</h:column>
(Note that this is using a Seam <s:button>
JSF control from the seam-ui.jar
package.)
This button is used to update the properties of the tasks. When the form is submitted, Seam and jBPM will make any changes to the tasks persistent. There is no need for any action listener method:
<h:commandButton value="Update Items" action="update"/>
A second form on the page is used to create new items, by calling the action method annotated @CreateProcess
.
<h:form id="new">
<div>
<h:inputText value="#{todoList.description}"/>
<h:commandButton value="Create New Item" action="#{todoList.createTodo}"/>
</div>
</h:form>
There are several other files needed for the example, but they are just standard jBPM and Seam configuration and not very interesting.