jBPM4 real-life example: The Train Ticket Demo (part 4: the end application)

The path from a business process model towards an automated business process implementation is often a long one. After the initial process modeling in the Signavio editor, the initial development and prototyping, it is now time to start the actual development of our Train Ticket handling platform.

In reality, development will be mixed with additional discussions between IT and business, enhancements and changes in the BPMN process model and definitely some addtional prototyping will be needed. Here it may look like an easy transition between our prototype and our eventual and application, but do keep in mind that this phase is the one were most often future failures are born. It is of extreme importance to keep in mind that a business process is The Holiest of Holy of a given company. Therefore, business process implementations must be as close as possible to what the business expects, and this often takes time to synchronise all stakeholders involved.

In this article, I’ll present the implementation of the business process as described in the previous articles. The end result will be a high-performant, scalable (by means of clustering) train ticket handling platform, using standard Java EE components. This article will also show how easy it is to integrate jBPM with any given Java tech. As described in my previous article, let jBPM enrich your architecture!

Of course, as a open source project we have a reputation to keep up. All of what we discussed in the previous articles and this article is contained in the download at the bottom of this article. Many people have asked me for the executable demo and so here it finally is! Like I said before, don’t hesitate to show this demo to all the people you know!

Architecture Overview

The following picture shows a high-level overview of the architecture.

Most of it is probably clear by just looking at it, but I’ll summarize what’s going on here:

  • The entrypoint to the application is a regular HTTP servlet. SMS gateways communicate between telecom operators using plain HTTP messages, so the choice for this technology is obvious.
  • We have a cellphone emulator (swing rich client), aka the jBPM-Phone, at the left side. This cellphone runs on my local machine and it sends text messages to our Ticket Handling Platform using the same HTTP messages used by the SMS gateway. This way, we can test our logic, without actually paying for the text message transfer.
  • The Servlet parses the incoming text messages and offers this to the TicketService, depending on the type of the message (a message that starts a new process, or one that triggers the task in a waiting process instance). See line 9 and 16.
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  ...
  if (isTicketRequestMessage(msgContent)) {

    TicketRequest ticketRequest = parse(msgContent, sendDate, senderNr);

    if (ticketRequest != null) {

      ticketService.handleTicketRequest(ticketRequest);

    } else if (isTicketAcceptanceMessage(msgContent)) {

      String acceptanceId = parseTicketAcceptanceMessage(msgContent);
        if (acceptanceId != null) {

          ticketService.handleTicketAcceptance(acceptanceId, senderNr);

        } else {
          LOG.warn("SMS message content invalid. Throwing it away.");
        }

    }
}
  • The TicketService is a stateless EJB3 service. The incoming message will be used to start a new process instance of our well-known ticket handling process (see previous articles if you haven’t the slighest clue). This means that the TicketService will actually call one of the jBPM API services, nothing more. Note how easy we have integrated jBPM with the EJB3. No magic involved: this is plain Java code, people. See line 16, 27 and 34 in particular.
@Remote(TicketService.class)
@Local(TicketService.class)
@Stateless
public class TicketServiceImpl implements TicketService {

  ... // field declarations

  public void handleTicketRequest(TicketRequest ticketRequest) {

    Map<String, Object> vars = new HashMap<String, Object>();

    vars.put(ProcessVariable.FROM, ticketRequest.getFrom());
    vars.put(ProcessVariable.TO, ticketRequest.getTo());
    vars.put(ProcessVariable.CELL_NR, ticketRequest.getCellPhoneNr());

    ProcessInstance processInstance = executionService.startProcessInstanceByKey(TICKET_PROCESS, vars);

    if (LOG.isInfoEnabled()) {
      LOG.info("Ticket Process with process instance id " + processInstance.getId() + " started");
    }
  }

  public void handleTicketAcceptance(String acceptId, String cellPhoneNr) {

    // Look up the task through the jBPM API

    Task task = taskService.createTaskQuery()
             .processInstanceId("ticketProcess." + acceptId)
             .assignee(cellPhoneNr)
             .uniqueResult();

    if (task!= null) {

      taskService.completeTask(task.getId());

      if (LOG.isInfoEnabled()) {
        LOG.info("Task " + task.getId() + " completed by " + cellPhoneNr);
      }
    }
  }

}

There are two important notes here:

  • The TicketService is a regular service, nothing special there. Here I’ve chosen for an EJB3 approach, but you can well imagine this to be a Spring service, where the jBPM services are injected into the service. The important part here is that the ticketService is a plain service, just like any other (CRUD) service in the application. See my article on “where does jBPM fit into my architecture”.
  • I’m using standard JEE components (servlet and EJB3), in a complete stateless way. This means that this architecture can scale up linearly with any given load (eg with a simple load balancer). Also note that the use of jBPM does not imply any limitation on scaling this architecture. jBPM is designed from scratch with concurrent executions in mind, and synchronisation is actually done at the database level. Every API call is self-contained, on which node in the cluster it is executed.

Notice that there is an additional component on the picture: the user registration app. This is a simple Seam application, that just allows for users to register their cellphone and add money to their account. For the moment, adding money is just filling in the form, but I’m sure you can imagine this to be done by MasterCard, or any other payment method.

Use The Source, Luke

If you want to just download the demo, then just scroll down to the next section. If you want to check out the source however, then these instructions are for you.

Check out the source for the Rich client emulator from our public svn repo:

svn co http://anonsvn.jboss.org/repos/jbpm/projects/demos/trainticket-demo/jbpm-phone/trunk/ jbpm-phone

Building the emulator is really easy, just do a standard maven build:

mvn clean install

The executable jar can be found in the target subfolder and is called jBPM-Phone-1.0.jar

Check out the source for the jbpm-on-rails platform from our svn repo:

svn co http://anonsvn.jboss.org/repos/jbpm/projects/demos/trainticket-demo/jbpm-on-rails/trunk/ jbpm-on-rails

The project is structured as a Maven-multiple-module with the following modules:

  • domain: contains the simple domain model of the ticket handling application: a TicketRequest, a User and a Quote
  • jboss-integration: contains some classes that are used to integrate easily with the JBoss AS, like binding the ProcessEngine to JNDI.
  • process-logic: this module has the most interesting classes. Here you’ll find the custom activities, Decision handler, event listeners, etc.
  • ejb: EJB module which contains the TicketService implementation as described above. Also contains the QuoteService EJB3, which emulates the external service shown in the architectural overview.
  • war: contains the described servlet that handles incoming text messages from customers
  • ear: wraps the ejb jar and the war in an ear that is deployable on the JBoss AS.

Building the project is done by just doing a standard maven build

mvn clean install

The resulting ear can be found under /ear/target. This ear is tested on a JBoss AS 4.2.3. I’m using some JBoss-specific extensions, so don’t expect it to run on other containers.

Download the demo

The complete application: a JBoss server (4.2.3) with the user registration app and the ticket handling application, together with the Rich client emulator can be downloaded here (+- 237 MB).

Follow these steps:

  • (this is the most intensive step) Go to $UNZIPPED_FOLDER/jboss-4.2.3.GA/server/default/deploy open up the jbpmeditor.war file with any zip utility and open the web.xml file. Look for the fileSystemRootDirectory and change the value to an existing foler on your own hard disk. Currently, the value is set to /home/jbarrez/Desktop/signavio-repository. Take care that the web.xml file is actually rewritten in the zip file (not all tools can do this).
  • go to $UNZIPPED_FOLDER/jboss-4.2.3.GA/bin and start up the JBoss server (./run.sh on linux)
  • go to $UNZIPPED_FOLDER/ and execute the jar: java -jar jBPM-Phone-1.0.jar
  • open your browser and go to http://localhost:8080/user_registration. You can log in with the emulator userid using 123456789/password and add some money to the emulator’s account.
  • Play around with the emulator. A ticket request is sent by typing ‘TICKET CITYA CITYB’.

Screencast

You can see how all this looks in reality by viewing the following screencast. In the movie, you’ll see how to use the emulator (aka the jBPM-phone), how to add some money to the emulator’s balance and how the ticket handling server communicates nicely with the jBPM-phone. All of what you see in this movie, can be done by yourself by downloading the demo!

Enjoy!

5 Comments

  1. Carlos Latugaye September 16, 2009

    Hi Joram,

    Thanks for this series of articles. They are a great introduction to jBPM.

    I’m interested in understanding how jBPM scales and behaves in a HA environment. Is it possible to have multiple JVMs running the same set of processes? If so, how get jobs synchronized across the JVMs? Is there any available documentation about this topic?

    Thanks in advance,

    Carlos

  2. Joram Barrez September 17, 2009

    @Carlos It is certainly possible to have multiple nodes with multiple JVM’s working on the same set f processes. jBPM relies heavily on the transactional capabilitites of the underlying database to achieve this.

    What do you mean by jobs? If you mean jBPM jobs, than it is quite easy. Just take a look at our developers guide, the section about the job executor (http://docs.jboss.org/jbpm/v4.0/devguide/html_single/#jobexecutor). It is built specifically to work on a cluster (but works great standalone,too).

    The architecture I describe in this article, is easy to scale over multiple nodes (with a simple loadbalancer like Pen on Linux). Since jBPM synchronizes on the database, it doesn’t matter on which node the actual request is processed.

    Hope this helps!

    Joram

  3. Carlos Latugaye September 18, 2009

    Thanks for the explanation! The documentation you provided was exactly what I was looking for.

    I was referring the the jBPM jobs. In particularly I was trying to understand how jBPM handles the case of a due transition of a process instance when running multiple engines.

  4. Eduardo June 17, 2010

    Thanks a lot for the example!!. Without any doubt is the more complex jbpm example than I have found in Internet. I only have to complain about the difficult to implement the all example because the svn direction doesn’t work and I had to spend a day until I could work it (although I didn’t try with complete download).

  5. José Luis Granda November 18, 2010

    Hi Joram, i try to run jbpm-on-rails at jboss-5.1.0.GA. I generate the ear pakage from sources.

    I get exception:

    [AbstractKernelController] Error installing to Real: name=vfszip:/usr/local/jboss-5.1.0.GA.JBPM/server/jbpm/deploy/jbpm-on-rails.ear/ state=PreReal mode=Manual requiredState=Real
    org.jboss.deployers.spi.DeploymentException: Error during deploy: vfszip:/usr/local/jboss-5.1.0.GA.JBPM/server/jbpm/deploy/jbpm-on-rails.ear/jbpm-on-rails-war-1.0.war/
    at org.jboss.deployers.spi.DeploymentException.rethrowAsDeploymentException(DeploymentException.java:49)

    Caused by: java.lang.NoClassDefFoundError: HttpServlet
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
    at org.jboss.classloader.spi.base.BaseClassLoader.access$200(BaseClassLoader.java:63)
    at org.jboss.classloader.spi.base.BaseClassLoader$2.run(BaseClassLoader.java:572)
    at org.jboss.classloader.spi.base.BaseClassLoader$2.run(BaseClassLoader.java:532)

    help me please

Leave a Reply

Your email address will not be published.