« Building a Workflow... | Main | New Version 1.6 of... »

Building a Workflow Application - Part III

In part III of my tutorial I will explain how to build a EAR module with maven and develop a Web Frontend to test my workflow application.

If you followed the part I and part II of this tutorial you have setup an environment, created the workflow model and implemented a business loging in a EJB 3.0 session bean.

To deploy a hole JEE Web application we need now a Web Modul with the frontend and also EAR module bundeling all the components to an Enterprise Application Archive which is deployable on Glassfish.

 

Creating a WEB Module

First I create a new maven WEB module. This module will implement the web frontend and makes use of the business logic defined in the exsisting EJB Module.

As described before I add a new Module to my parent project. Choose "WAR" as the packaging Method:

 

pickture

To  get access to my business logic I modify the pom.xml and add the Workflow API and the predefined EJB module as a dependency to my pom.xml. The pom.xml file should look like the following example. In this example I added also support for MyFaces and Facelets which allows me to implement nice Web Forntend. But thise dependencies are not really necessary for a simple test:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>org.imixs.callcenter</artifactId>
<groupId>org.imixs</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.imixs.examples</groupId>
<artifactId>org.imixs.callcenter.web</artifactId>
<packaging>war</packaging>
<name>org.imixs.callcenter.web</name>
<version>0.0.1-SNAPSHOT</version>
<description/>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>

</plugins>
</build>

<dependencies>

<dependency>
<groupId>javaee</groupId>
<artifactId>javaee-api</artifactId>
<version>5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.myfaces.tomahawk</groupId>
<artifactId>tomahawk</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>com.sun.facelets</groupId>
<artifactId>jsf-facelets</artifactId>
<version>1.1.14</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.api</artifactId>
<version>1.5.3</version>
<type>jar</type>
</dependency>

<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.manik.orgunit</artifactId>
<version>0.0.1</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.manik.model</artifactId>
<version>0.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.imixs.examples</groupId>
<artifactId>
org.imixs.callcenter.ejb
</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>


</dependencies>
</project>

Create a Backing Bean

As I use JSF to implement my WebFrontend I need now a Backing Bean which provides the necessary properties and methods for my ticket application. So first I create a new package in my maven web module under /src/man/java  and create a TicketBan class.

This class should provide a simple access to the attributes for a Ticket to be used in JSP page. There for a added a getTicket method which returns a Map object with all the attributes. If no Ticket was created before the method creates a new Ticket using the TicketService of my EJB Module.

 package org.imxis.callcenter.web.workflow;

import java.util.Map;
import javax.ejb.EJB;
import javax.faces.event.ActionEvent;
import org.imixs.workflow.ItemCollection;

public class TicketBean {

/* Ticket Services */
@EJB
org.imixs.callcenter.business.TicketService ticketService;

/* Workflow Model Service */
@EJB
org.imixs.workflow.manik.model.ModelService modelService;

private ItemCollection workitemItemCollection;

public Map getTicket() throws Exception {
if (workitemItemCollection == null)
workitemItemCollection = ticketService.createTicket();

return workitemItemCollection.getAllItems();
}

public void doStartNewProcess(ActionEvent event) throws Exception {
workitemItemCollection = ticketService.createTicket();
workitemItemCollection.replaceItemValue("$processid", new Integer(10));

}

}

So also my Backing Bean is realy simple. No I can create a JSF Page to display a Form with the Ticket. To open the form I use the doStartNewTicket method as a actionListener. This method creates an empty ticket and started with the first ProcessID of my workflow modell ID=10.

Creating a Form

So the next stepp is to create a Form to display and edit a ticket.

As I use JSF I can access each property of a Ticket during my backing bean created before. So a simple Input Field Tag looks like this:

 <h:inputText value="#{ticketBean.ticket['subject'][0]}"
id="subject_id" required="true"></h:inputText>

 As you can see I use the expression language to access the entries of my hashMap. Each entry in a ItemCollection is stored in a Vector so I use [0] expression to access the first entry in the vector.

Also you can display each property from a ticket. The following snippet shows how to display the current workflow status managed by the Workflow Manager:

<h:outputLabel 
value="#{ticketBean.ticket['txtworkflowgroup'][0]}: #{workitemBean.workitem['txtworkflowstatus'][0]}" />

Computing the Command Actions from the Model

Now I came up to the most intresting part of the Workflow System. I want a command button bar computet out of the workflow model. For each Activity I defined in my Workflow Model I what to see a command action in my Form. So the user can decide which workflow activity should be processed. In me example the user can choose to "save" a ticket or to "accept" a ticket.

Therefor I need two new methods in my Backing Bean. One method to compute the available activities and one to process the workitem.

The following code shows how I compute a ArrayList which returns the model objects for the corresponding Process Entity. The process entity can be taken form the Ticket attriubte "$processid" which is initialized during doStartNewProcess method and managed by the WokflowManager.

 	public ArrayList<Map> getActivityList() {

int processId = workitemItemCollection
.getItemValueInteger("$processid");

activityList = new ArrayList<Map>();
// Workflow-Activities
List<ItemCollection> col = modelService.findPublicActivities(processId);
for (ItemCollection aworkitem : col) {
activityList.add(aworkitem.getAllItems());
}

return activityList;
}

So I can now implement a JSF code to display commandButtons for each Activity:

 <c:forEach var="activity" items="#{ticketBean.activityList}">
<h:commandButton action="show_worklist"
actionListener="#{ticketBean.doProcess}"
value="#{activity['txtname'][0]}">
<f:param name="id" value="#{activity['numactivityid'][0]}" />
</h:commandButton>
</c:forEach>

As you can see I have full access to the Activity Entities. So I can ask for the Name of an Activity and the activityID. If a ticket goes throgth the process in each Process step the right activity buttons will be displayed. And also I am able to change the model without changing the code of my application!

Each commandButton call the method doProcess of my Ticket Bean. This Method is for process a Workflow step. We implemented some code in our EJB so the backing bean code is again realy simple:

	public void doProcess(ActionEvent event) throws Exception {
// search Activity ID from command tree
List children = event.getComponent().getChildren();
int activityID = -1;

for (int i = 0; i < children.size(); i++) {
if (children.get(i) instanceof UIParameter) {
UIParameter currentParam = (UIParameter) children.get(i);
if (currentParam.getName().equals("id")
&& currentParam.getValue() != null) {
Object oid = currentParam.getValue();
Integer intobject = new Integer(oid.toString());
activityID = intobject;

}
}
}

// add team members programatical
Vector<String> vTeam=new Vector<String>();
vTeam.add("Manfred");
vTeam.add("Eddy");
vTeam.add("Anna");
workitemItemCollection.replaceItemValue("team",vTeam);

workitemItemCollection = ticketService.processTicket(
workitemItemCollection, activityID);


}

The code first identifies the activityid provided as a param in my commandButton. Next the method adds a member list of a team to the workitem programatical. This is typical code. So you can use this team attribute in the workflow model. For example you can grant write access to the team or you can send an email to the team to notify them about a new ticket. See the IX Modeler documentation for mor informations about modeling actors.

Finally the method call the processTicket method of the TicketService EJB created in the business facade. The new attribute values are updated into the ItemCollection used by the TicketBean. So you can display the Workflow status or the workflow History with simple JSP Tags like this:

Workflow History:<h:dataTable
value="#{ticketBean.ticket['txtworkflowhistorylog']}"
var="log">
<h:column>
<h:outputText value="#{log}" />
</h:column>
</h:dataTable>

Check out the hole example from the subversion repository to see the complete JSF pages.

Creating a EAR Module

Now finally I create a new EAR module using maven. The EAR module is the deployable unit for the glassfish server. As described before I add a new Module to my parent project. 


The maven packaging method now is "EAR".

After the module is created I change the pom.xml file to advice maven to include my ejb and web module. The pom.xml defines dependencies to the IX JEE Workflow API and defines a valid Enterpirse Archive directory structure:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>org.imixs.callcenter</artifactId>
<groupId>org.imixs</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.imixs.examples</groupId>
<artifactId>org.imixs.callcenter.ear</artifactId>
<packaging>ear</packaging>
<name>org.imixs.callcenter.ear</name>
<version>0.0.1-SNAPSHOT</version>
<description />
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<configuration>
<modules>
<webModule>
<groupId>org.imixs.examples</groupId>
<artifactId>org.imixs.callcenter.web</artifactId>
<contextRoot> /callcenter </contextRoot>
</webModule>
<ejbModule>
<groupId>org.imixs.examples</groupId>
<artifactId>org.imixs.callcenter.ejb</artifactId>
</ejbModule>
<JarModule>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.api</artifactId>
<bundleDir>lib</bundleDir>
</JarModule>
<JarModule>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.jee.api</artifactId>
<bundleDir>lib</bundleDir>
</JarModule>
<JarModule>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.manik.model</artifactId>
<bundleDir>lib</bundleDir>
</JarModule>
</modules>
</configuration>
</plugin>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<phase>pre-integration-test</phase>
<configuration>
<tasks>
<!--
the Property glassfish.install needs to be configured in the
settings.xml file the property should point to the autodeploy
folder from glassfish domain!
-->
<echo message="About to copy EAR to autodeploydirectory..." />
<echo> EAR:
${project.build.directory}/${project.build.finalName}.ear
autodeploy directory: ${glassfish.install} </echo>
<copy
file="${project.build.directory}/${project.build.finalName}.ear"
todir="${glassfish.install}" />
<echo message="Copied EAR to autodeploy directory." />
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.jee.api</artifactId>
<version>1.5.7</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.imixs.workflow</groupId>
<artifactId>org.imixs.workflow.api</artifactId>
<version>1.5.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.imixs.examples</groupId>
<artifactId>org.imixs.callcenter.web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<type>war</type>
</dependency>
<dependency>
<groupId>org.imixs.examples</groupId>
<artifactId>
org.imixs.callcenter.ejb
</artifactId>
<type>ejb</type>
<version>0.0.1-SNAPSHOT</version>
</dependency>

</dependencies>
</project>

The pom.xml also includes a helpfull ant script. This ant script automatical deploys the ear to the autodepoly folder of my glassfish server. You need to define an environment variable glassfish.install in your default profile of the settings.xml.

.....
<properties>
<!-- Property Glassfish Location for auto deplyment during install -->
<glassfish.install>/opt/glassfish/domains/domain1/autodeploy</glassfish.install>
</properties>

Ok finished! Now its time to build your parent project with the maven command "run-as -> maven install".

If everything works and the build process finished succesfull you can open the sample application with the followin url:

http://localhost:8080/callcenter

Synchronizing the Workflow Model

Before I can start creating and processing a new ticket I need to upload my Workflow model created in part I of this tutorial. Therefor open the model.ixm file with Eclipse and provide the new WebService URI of the new application:

http://localhost:8080/CallcenterWorkflowModelService?wsdl

 

On the first page of the editor you can now synchronize the model:

 

 

Now you can start creating new Tickets with the web application.

Notice that you have now workflow application where a lot of fuctionallity like security, process history or email notification is managed by the IX JEE Workflow engine without write a lot of code.

If you have questions or some feedback please comment to this blog. I will update the example and extend the sample application. So look out for updates.