Chapter 23. jBPM and Spring

A business is only as good as its processes. Often, businesses will thread together the contributions of multiple resources (people, automated computer processes, and so forth) to achieve a greater result. These individual contributions by people and automatic services are most efficient when single-focused and, ideally, reused. The simplest example of this might be a conveyor belt in a car factory, where work enters the line at the beginning of the conveyer belt and is worked on by any number of individuals or machines until finally the output of the work reaches the end of the line, at which point the job is done. One machine paints the chassis; another machine lowers the engine into the car. A person screws in and attaches the chairs and another person installs the radio. These people and machines do their work without worrying about what's going to happen to the car next.

A more complicated, interesting process—to take the car example even further—can be seen at a car dealership. There are many workers whose job depends on playing their role in selling you a car. It starts when you enter the car dealership and salesmen descend on you like wolves. Somebody walks with you, showing off models and features and answering questions. Finally, your eye catches the glimmer of a silver Porsche sitting an aisle over. You're sold. The next part of the process begins.

You're whisked away into the office where somebody starts prompting you for information to purchase the vehicle. You either have cash on hand, or you require a loan, or you've already got a loan from another bank. If you have cash on hand, you give it to them and wait an hour for them to count it. Perhaps you've got a check from the bank, in which case you give them that. Or, you begin the process of applying for a loan with the dealership. Eventually, the pecuniary details are sorted, credit scores checked, driver's license and insurance verified, and you begin signing paper work. If you've already paid for the car, the paper work to ensure proper title and registration is drawn up. If you're establishing a loan with the dealership, you fill out that paperwork, then work on registration, and so on.

Eventually, you're given the keys, the car, and the relevant paperwork, and you're done. Or so you think. You make a break for the door, just itching to see how fast you can get the car to 65, which is the maximum speed limit in your area freeway, conditions permitting. As you arrive at the door, you're all but assaulted with one last packet of brochures and business cards and a branded pen and the good wishes of the grinning salesmen.

Baffled, you shrug them off and break for the car, jumping into the sporty convertible's driver's seat. As you leave, you turn the music up and speed off into the horizon. You'll remember that you left your wife at the dealership eventually, but for now, the fruit of all that bureaucracy is too sweet to ignore.

The process to buy the car may seem like it takes forever, and indeed, it does take a long time. However, the process is efficient in that all things that can be done at the same time are done at the same time, by multiple workers. Further, because each actor knows the part, each individual step is as efficient as possible. Being able to orchestrate a process like this is crucial in the enterprise.

You can extrapolate here, too. These examples are relatively small, though perhaps the inefficiencies of the worst-case scenario for the process are tolerable. The inefficiencies are overwhelmingly untenable in even slightly larger business processes, though! For example, imagine the new-hire process at a large company. Beyond the initial process of interviewing and a background security check, there's the provisioning that's required to get the new employee installed. The IT department needs to repurpose a laptop, image it, and install an operating system. Somebody needs to create a user account for that employee and ensure that LDAP and e-mail are accessible. Somebody needs to ready a security card so that the employee can use the elevator or enter the building. Somebody needs to make sure the employee's desk station or office is cleaned and that remnants from the previous occupant are gone. Somebody needs to get forms for the health insurance or benefits, and somebody needs to give the new employee a walk around the office, introducing the employee to staff.

Imagine having only one person to do all of that for each employee! In a bigger company (such as a bank, for example) this process would soon become overwhelming! Indeed, many of the tasks mentioned themselves require several steps to achieve the goal. Thus, the main process—integrating a new employee in the company—has multiple subprocesses. If all the tasks are performed concurrently by many people however, the process becomes manageable. Additionally, not all people are suited to doing all of those tasks. A little specialization makes for a lot of efficiency here.

We see that processes, and the understanding of those processes, are crucial to a business. It is from this revelation that the study of business management emerged, called Business Process Management (BPM). BPM originally described how to best orchestrate technology and people to the betterment of the business, but it was a businessman's preoccupation, not a technologist's. As it became apparent that businesses were already leveraging technology, the next hurdle was to codify the notion of a business process. How could software systems know—and react to—what the immovable enterprises and unbending market forces demanded? BPM provides the answer. It describes, in higher-level diagrams, the flow a given process takes from start to finish. These diagrams are useful both to the business analyst and to the programmer, because they describe two sides of the same coin. Once a process is codified, it can be reused and reapplied in the same way a programmer reuses a class in Java.

Software Processes

Thus, the unit of work—that which is required to achieve a quantifiable goal—for a business is rarely a single request/response. Even the simplest of processes in a business requires at least a few steps. This is true not just in business but in your users' use cases. Short of simple read-only scenarios such as looking at a web page for the news, most meaningful processes require multiple steps. Think through the sign-up process of your typical web application. It begins with a user visiting a site and filling out a form. The user completes the form and submits the finalized data, after satisfying validation. If you think about it, however, this is just the beginning of the work for this very simple process. Typically, to avoid spam, a verification e-mail will be sent to the user. When the e-mail is read, the user clicks a link, which confirms the intentions of the registrant and that the registrant is not a robot. This tells the server that the user is a valid user, and that a welcome e-mail should be sent. A welcome e-mail is then sent. Here alone, we had four steps with two different roles! This involved process, when translated into an activity diagram, is shown in Figure 23-1.

The two roles (user and system) are shown as swimlanes. Rounded shapes inside the swimlanes are states. Process flows from one state to another, following the path of the connecting lines.

Figure 23.1. The two roles (user and system) are shown as swimlanes. Rounded shapes inside the swimlanes are states. Process flows from one state to another, following the path of the connecting lines.

For such a simple process, it might be tempting to keep track of the state of the process in the domain model. After all, some of the state, such as the sign-up date, can certainly be regarded as business data, belonging to model entities. Such a date is valuable for revenue recognition. The date when the welcome e-mail was sent is probably not very important, though. The situation will escalate if you send out more e-mail. If you build other kinds of processes involving the user, management of the user's state within those processes will become a burden on your system and will complicate the schema.

A workflow system extricates that process state from the domain and into a separate layer, called a business process. A workflow system also typically models which agents in the system do what work, providing work lists for different agents in the system.

A workflow engine lets you model the process in a higher-level form, roughly corresponding in code to what a UML activity diagram can describe. Because a workflow is high-level, specifying how a business process is leveraged as an executable component of a system is a dizzyingly vast task. In industry, there are standards for the language used to model a business process as well as the model of the engine that's used to run the business process. Additionally, there are standards specifying interoperability, how endpoints are mapped to the agents being orchestrated by the process, and much more. All this can quickly become overwhelming.

Let's look at some of these standards in Table 23-1.

Table 23.1. Some of the Myriad, Significant Standards Surrounding BPM

Standard Name

Standards Group

Description

WS-BPEL (BPEL)

OASIS

A language that, when deployed to a BPEL container, describes the execution of a process. It interfaces with the outside world via the invocation of external web services. This language describes the runtime behavior of a process. It has several flaws, not the least of which is the reliance on web service technology and the lack of work list support.

WS-BPEL (BPEL 2.0)

OASIS

This is largely an upgrade to its predecessor, clarifying the behavior at runtime of certain elements and adding more expressive elements to the language.

WS-BPEL for People (BPEL4People)

OASIS

The main feature common to traditional workflow systems is the ability to support work lists for actors in a process. BPEL had no such support, as it didn't support human tasks (that is, wait states for people). This specification addresses that exact shortcoming.

Business Process Modeling Notation (BPMN)

Originally BPMI, then OMG, as the two organizations merged

This provides a set of diagramming notations that describe a business process. This notation is akin to UML's activity diagram, though the specification also describes how the notations relate to runtime languages such as BPEL. The notation is sometimes ambiguous, however, and one of the formidable challenges facing BPM vendors is creating a drawing tool that can take a round-trip to BPEL and back, providing seamless authoring.

XML Process Definition Language

Workflow Management Coalition (WfMC)

This one describes the interchange of diagrams between modeling tools, especially how elements are displayed and the semantics of those elements to the target notation.

As you can see, there are some problems. Some of these standards are ill suited to real business needs, even once the busywork is surmounted. And some of them lack adequate support for work lists—essentially making the processes useless for anything that models human interaction, a fairly typical requirement.

While a lot of this is slowly getting better, there's no reason to wait. There are viable de facto standards that meet a lot of these problems and offer a compelling alternative. In this chapter, we will review jBPM, a popular open source environment. You might take a look at the alternative open source workflow engines (e.g., Enhydra Shark or OpenWFE) or, indeed, the proprietary engines from Tibco, IBM, Oracle, WebMethods, and so forth, before you decide on jBPM. In our opinion, it's powerful enough for easily 80 percent of the situations you're likely to encounter and can at least facilitate solutions for another 10 percent.

Ultimately, jBPM integrates well with Spring, and it provides a very powerful complement to the features of the things we've discussed in this book and to the core Spring framework itself. Just as a page-flow description language threads together multiple requests in a web application, workflows thread together many disparate actors (both people and automatic computer processes) into a process, keeping track of state. Workflow support becomes far more compelling in an architecture using even a few of the technologies we've covered in this book: messaging, distributed computing, ESB endpoints, web services, and long-lived processing infrastructure, for example! Workflow orchestrates these different, powerful tools and provides cohesion. Eventually, you'll even begin to reuse processes much like you might reuse a class or a Spring Integration endpoint.

Understanding Workflow Models

Problem

You understand the "why" behind business processes and have identified types of problems that might be well suited to this technology. Now, you want to understand how—after all, it all sounds so nebulous and abstract. How do you describe and speak about a workflow, exactly?

Solution

It turns out, happily, that you probably already know most of what you need to describe a workflow engine. This is part of why a workflow engine is so powerful—it formalizes and facilitates solutions you're already struggling to build in other technologies. In fact, when you draw an activity diagram, the result is likely directly translatable into a workflow. As you'll see, a lot of the constructs used to build a business process are familiar. We've discussed many of them in our discussions of Spring Integration and Spring Batch and of the map/reduce pattern with GridGain.

How It Works

One metric by which you might judge an engine is how well it lets you express workflow patterns. The workflow patterns describe different idioms that you might apply to a pattern. All patterns ultimately are built out of any mixture of a few key concepts (see Table 23-2), many of which we've discussed in other chapters of this book, as with Spring Batch and Spring Integration.

Table 23.2. Kinds of Constructs You Will Use When Working with a BPM or Workflow System

Concept

Description

State

"State" can mean many things, but simply, it's a pause or window in the action. It's a way of letting your process rest in a known condition indefinitely. Perhaps during this time, an external event might take place, or the system might wait for an input. Perhaps the state is entered and left as quickly as can be, serving only to provide record of the event. This is one of the simplest and most powerful functions of a workflow engine. Indeed, all discussions of long-lived state, of conversational state, and of continuations are centered on this concept. It allows your process to stop and wait for an event that tells it to proceed. This is powerful because it implies that no resources need to be wasted waiting for that event and that the process can effectively sleep, or passivate, until such an event, at which point the workflow engine will wake the process up and start it moving.

Activity

An activity is a pause in the action that can only move forward when a known actor or agent in the system moves it forward. You might imagine the moderator or group administrator who has to OK your subscription to a news group. In that case, only specified agents or roles may say the process may move forward.

Sequence

A sequence is simply an aggregation of states, activities, and other types of constructs that serializes them. You might have three states and an activity. You might imagine them as steps on a ladder, where the ladder is the sequence directing the process upwards to the ultimate goal.

Fork or concurrence or split

A sequence is important because it implies there's another way of threading things along. A fork is a concurrent execution of multiple threads of execution at the same time, originating from a common thread. Some parts of a business process are inherently sequential, and some are readily concurrent. In the new employee example explored previously, you might imagine the security clearance, laptop provisioning, and other tasks could be done at the same time, thus increasing the speed of the process.

Subprocess

In the new employee example, we discuss several tasks that need to be performed by representatives of different departments. Each department may have its own task list to complete in order to achieve the goals of the overarching process. These subtasks (basically a separate process unto their own) may be modeled as a subprocess. Subprocesses afford your workflows the same flexibility through reuse that composition through functions or classes affords your programs.

Decision

A decision describes a node that is conditional, based on some logic that you inject. You might use this to vary the execution based on some fact that you provide the process as a parameter.

Installing jBPM

Problem

You want to build a jBPM solution and need to know the simplest way to get the JARs. There are many supported workflows for jBPM, so it isn't clear where to begin for what role. For a business analyst, the path is different than for a programmer, whose task it will be to employ jBPM, not deploy it. You will need a few libraries first.

Solution

You can use jBPM as an API, rather than as a server or service. This integration is the most natural to a developer, but not, for example, to a business user. We'll use Maven to get the dependencies.

While we'll focus on embedding jBPM in this chapter, it's useful to see the other ways you can integrate jBPM into your architecture.

  • A developer may embed jBPM as services and Hibernate entities.

  • A developer may deploy jBPM into a stand-alone server and then use the administration console to deploy and test processes.

  • jBPM 4.3 ships with a web application in which a user can diagram and test processes.

How It Works

For this example, we're using a few libraries for AOP, transactions, the core Spring context, and, of course, jBPM itself.

If you're looking to find out more, read the documentation, and get the downloadable binaries for exploration, check out http://jboss.org/jbossjbpm/. There, you can find a lot of useful information.

Because we're looking to embed it, we'll simply use the libraries.

Note

If you're using Maven, you should add the following dependencies to your project.

<dependency>
  <groupId>org.jbpm.jbpm4</groupId>
  <artifactId>jbpm-jpdl</artifactId>
  <version>4.3</version>
 </dependency>
<dependency>
  <groupId>javax.annotation</groupId>
  <artifactId>jsr250-api</artifactId>
  <version>1.0</version>
 </dependency>

<dependency>
  <groupId>commons-dbcp</groupId>
  <artifactId>commons-dbcp</artifactId>
  <version>1.2.1</version>
  <exclusions>
    <exclusion>
      <groupId>xerces</groupId>
      <artifactId>xercesImpl</artifactId>
    </exclusion>
  </exclusions>
 </dependency>

 <dependency>
  <groupId>xerces</groupId>
  <artifactId>xercesImpl</artifactId>
  <version>2.7.1</version>
 </dependency>

 <dependency>
  <groupId>commons-lang</groupId>
  <artifactId>commons-lang</artifactId>
  <version>2.2</version>
 </dependency>

 <dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>1.1</version>
 </dependency>
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-entitymanager</artifactId>
  <version>3.4.0.GA</version>
 </dependency>

 <dependency>
  <groupId>javax.persistence</groupId>
  <artifactId>persistence-api</artifactId>
  <version>1.0</version>
 </dependency>

Some of these dependencies are from the JBoss Maven repository. You should add a reference to the JBoss Maven repository to your Maven project.

<repository>
  <id>jboss</id>
  <url>http://repository.jboss.com/maven2/</url>
 </repository>

It's hard to find an exhaustive or conclusive list of supported databases for jBPM, but because it's built on Hibernate, you can expect it's going to work on the big-name databases: Oracle, SQL Server, MySQL, PostgreSQL, and so forth. In this example, we're using PostgreSQL 8.3.

Note

To use PostgreSQL, you need to add a the driver library to the classpath. If you are using Maven, add the following dependency to your project.

<dependency>
   <groupId>postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <version>8.3-603.jdbc3</version>
 </dependency>

Integrating jBPM 4 with Spring

Problem

You want to use jBPM 4 (the newest, and current, release at the time of this writing) but you've got a Spring-based architecture and want to make use of jBPM from within a Spring application context.

Solution

Earlier versions of Spring shipped with a custom bean for use with Spring (org.jbpm.pvm.internal.cfg.SpringConfiguration). Now, however, the initialization is so simple there's no real need, and the class has been removed. We'll create a factory for our jBPM configuration that can be reused across projects. The balance of the configuration is fairly boilerplate transaction management and Hibernate integration, with some caveats. Andries Inzé started the project to integrate Spring and jBPM, and so much of the great work here is because of his efforts, on top of the great work behind jBPM itself, of course.

How It Works

There are many ways to use jBPM. One approach is to use it as a stand-alone process server, perhaps deployed using JBoss. This solution exposes a console into which you can deploy business processes and even test out the process and watch it move state forward. As it's written using Hibernate, it's not too difficult to get it working on JBoss's EJB environment. So, you might use it as a service. Indeed, JBoss itself supports deploying processes to a directory and loading those, with some configuration.

For our purposes, we want to flip that deployment on its head. We want to embed, rather than deploy to, jBPM, in the same way that we can use Spring to invert control for other things such as remote services, message driven POJOs, and the HibernateTemplate.

In this example, we'll host jBPM's services in the Spring context, just like we might any other Hibernate services. jBPM is, fundamentally, a runtime that stores its state and jobs in a database. It uses Hibernate as its persistence mechanism (though it may eventually move to a strict JPA–based model). When jBPM, with Hibernate, interacts with the database, it uses transactions and avails itself of many of the features that Hibernate provides to build a sophisticated object graph mapped to a database schema. Naturally, this isn't so bad, except that in order to use jBPM with other services inside of Spring, particularly ones that leverage transactions (other database resources, XA functionality including but not limited to JMS, other Hibernate models, and so forth), you need to exert control over jBPM's transactions. Otherwise, jBPM will commit and begin transactions independent of the Spring container's transaction lifecycle.

jBPM 4 is built cleanly, in such a way that delegating the life cycle of the system to another container (such as JBoss's microcontainer or Spring) is feasible. The jBPM 4 and Spring integration builds on top of this, providing a recipe for accessing key services that jBPM provides at runtime. This is the key to the integration. With jBPM available to your beans as just another bean in the container, you can leverage it just like you might a Hibernate session or a JMS queue connection. Similarly, you can hide its use behind service methods that you expose to your applications clients.

Imagine exposing a service method for creating customer entities. It might use Hibernate to save the new record to the database, use JMS to trigger integration with an external financial system the company uses, and use jBPM to start a business process for fulfillment. This is a very robust, well-rounded service. You can use the services like you might any other transactional service—confident that the containing transaction will envelope the transactions of the jBPM process.

We will build a simple application context that contains the common elements you'll need to start using jBPM in your application, and then see where you may want to customize the solution to your environment.

In the example for jBPM 4, we will build a solution that models a simple customer registration workflow. In so doing, you will get a feeling for how you would set up the example, as well as get a chance to see a (very simple) jBPM solution. The namespaces of the work we'll do will sit below com.apress.springrecipes.jbpm.jbpm4.

The Application Context

The first thing to do is to set up basic database and Hibernate configuration. We will use the tx, p, aop, and context namespaces to build this solution. The skeletal frame for our Spring XML configuration is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:tx="http://www.springframework.org/schema/tx" 
The Application Context
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
The Application Context
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
The Application Context
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> ... </beans>

In this example, we'll use two Spring application contexts. One application context will configure jBPM (jbpm4-context.xml), and the other will configure our sample application (simply, context.xml). The first application context is geared around configuring jBPM. You should be able to reuse this file later with no changes to the file itself. Mainly, you'd have to update the property file (naturally) and you'd have to tell the session factory about any annotated classes that you want it to know about from your own domain model. In this example, doing so is simple, as it involves overriding an existing List bean named annotatedHibernateClasses in a separate context. It's been done this way because it's not possible to have two sets of Hibernate classes that are registered with the Hibernate SessionFactory. To have an annotated class be registered as a Hibernate entity, it needs to be registered with the AnnotationSessionFactoryBean. The annotatedClasses property expects a list of class names. Because our jBPM configuration uses Hibernate, we have to configure the AnnotatedSessionFactoryBean on behalf of jBPM, which means that you can't create a separate one if you're using the jbpm4-context.xml and want only one Hibernate session in your application. This is probably the case, because there's rarely a need for two Hibernate sessions, because they can't share transactions and so on. So, we provide a template configuration, referencing a list that's created in the context. The list is empty, but you can in your Spring application context include (jbpm4-context.xml) and create your own list bean with the same bean name ("annotatedHibernateClasses"), effectively overriding the configuration. This delegation scheme works in much the same way an abstract method in a base class does.

Because we want this to be as automatic as possible, we'll exploit Spring's AOP schema support and transaction schema support. The first few lines of the application context are boilerplate: they instruct the context to enable annotation configuration and use a property file as values for placeholders in the application context's XML itself. The file jbpm4.properties is included and its values resolved. Those values are then available as expressions to other beans in the context file.

<context:annotation-config />

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="jbpm4.properties" p:ignoreUnresolvablePlaceholders="true" />

Next comes the sessionFactory. It's key to get this right: we need to tell Spring about our database and give it the information on our schema. It does this by resolving the properties in the property file. When we configure the mappingLocations property, we are pointing it to classpath Hibernate mapping files so that it may resolve the various entities that ship with jBPM 4. These entities will exist in your database. They persist such information as process definitions, process variables, and so forth that are important to the successful execution of the jBPM engine.

The final property we've configured here is the annotatedClasses property, which we basically punt. Providing an empty list object here allows us to override it in another context file, so we don't even need to modify the original one. If you don't want to provide any classes, the original empty list declaration will still work, and you won't get any errors from Spring at runtime.

Note that we've specified p:schemaUpdate="true", which let's Hibernate generate the schema for us on load. Naturally, you probably want to disable this in production.

<bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
        p:dataSource-ref="dataSource"
        p:schemaUpdate="true">
                <property name="hibernateProperties">
                        <props>
                                <prop key="hibernate.dialect">${dataSource.dialect}</prop>
                                <prop key="hibernate.show_sql">true</prop>
                                <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
                                <prop key="hibernate.jdbc.batch_size">20</prop>
                                <prop key="hibernate.show_sql">true</prop>
                                <prop key="hibernate.use_sql_comments">true</prop>
                        </props>
                </property>
<property name="mappingLocations">
                        <list>
                                <value>classpath:jbpm.execution.hbm.xml</value>
                                <value>classpath:jbpm.repository.hbm.xml</value>
                                <value>classpath:jbpm.task.hbm.xml</value>
                                <value>classpath:jbpm.history.hbm.xml</value>
                        </list>
                </property>
                <property name="annotatedClasses" ref="annotatedHibernateClasses" />
        </bean>

        <util:list id="annotatedHibernateClasses" />

The next bean—the dataSource—is configured entirely at your discretion. The properties are set using properties in the properties file jbpm4.properties. The contents of jbpm4.properties:

hibernate.configFile=hibernate.cfg.xml
dataSource.password=sep
dataSource.username=sep
dataSource.databaseName=sep
dataSource.driverClassName=org.postgresql.Driver
dataSource.dialect=org.hibernate.dialect.PostgreSQLDialect
dataSource.serverName=sep
dataSource.url=jdbc:postgresql://${dataSource.serverName}/${dataSource.databaseName}
dataSource.properties=user=${dataSource.username};databaseName=${dataSource.databaseName};serverName=${dataSource.serverName};password=${dataSource.password}

Modify the values there to reflect your database of choice. As mentioned before, there are lots of supported databases. If you're using MySQL, I'd suggest something like the InnoDB table type to take advantage of transactions. In our experience, PostgreSQL, MySQL, Oracle, and so forth all work fine. You'll also want to configure a transaction manager. You will use this later when setting up AOP–advised transaction management for our beans.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
                destroy-method="close" p:driverClassName="${dataSource.driverClassName}"
                p:username="${dataSource.username}" p:password="${dataSource.password}"
                p:url="${dataSource.url}" />

        <bean id="transactionManager"
                class="org.springframework.orm.hibernate3.HibernateTransactionManager"
                p:sessionFactory-ref="sessionFactory" />

Additionally, add a HibernateTemplate, as you'll need it to interact with both your and jBPM's entities.

<bean   id="hibernateTemplate"
                class="org.springframework.orm.hibernate3.HibernateTemplate"
                p:sessionFactory-ref="sessionFactory"  />

Finally, we configure the actual factory that sets everything up for us. This class is our own, simple implementation.

<bean id="processEngine" class="com.apress.springrecipes.jbpm.jbpm4.CustomSpringFactory">
        <property name="jbpmCfg" value="jbpm.cfg.xml"/>
    </bean>

Here's the definition of the class:

package com.apress.springrecipes.jbpm.jbpm4;

import org.jbpm.api.ProcessEngine;
import org.jbpm.pvm.internal.cfg.ConfigurationImpl;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;


/**
 * A custom {@link org.springframework.beans.factory.BeanFactory} that we can
 * use to setup the {@link org.jbpm.api.ProcessEngine}. This is based on jBPM's {@link 
The Application Context
org.jbpm.pvm.internal.processengine.SpringHelper}. */ public class CustomSpringFactory implements FactoryBean, InitializingBean,
The Application Context
ApplicationContextAware { private ApplicationContext applicationContext; private ProcessEngine processEngine; private String jbpmCfg; public void setJbpmCfg(final String jbpmCfg) { this.jbpmCfg = jbpmCfg; } @Override public Object getObject() throws Exception { return processEngine; } @Override public Class<?> getObjectType() { return ProcessEngine.class; } @Override public boolean isSingleton() { return true; }
@Override
    public void afterPropertiesSet() throws Exception {
        processEngine = new ConfigurationImpl().springInitiated(applicationContext).
The Application Context
setResource(jbpmCfg).buildProcessEngine(); } @Override public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }

At this point, all that remains is to specify the following jBPM configuration itself (jbpm.cfg.xml), which is fairly boilerplate and you can use, unchanged, for a vast many solutions:

<?xml version="1.0" encoding="UTF-8"?>
<jbpm-configuration>
    <import resource="jbpm.default.cfg.xml"/>
    <import resource="jbpm.jpdl.cfg.xml"/>
    <import resource="jbpm.identity.cfg.xml"/>
    <import resource="jbpm.tx.spring.cfg.xml"/>

    <process-engine-context>
        <repository-service/>
        <repository-cache/>
        <execution-service/>
        <history-service/>
        <management-service/>
        <identity-service/>
        <task-service/>
        <command-service>
            <retry-interceptor/>
            <environment-interceptor/>
            <spring-transaction-interceptor/>
        </command-service>
        <script-manager default-expression-language="juel" default-script-language="juel" 
The Application Context
read-contexts="execution, environment, process-engine, spring" write-context=""> <script-language name="juel" factory="org.jbpm.pvm.internal.script.
The Application Context
JuelScriptEngineFactory"/> </script-manager> <id-generator/> <types resource="jbpm.variable.types.xml"/> <address-resolver/> <business-calendar> <monday hours="9:00-12:00 and 12:30-17:00"/> <tuesday hours="9:00-12:00 and 12:30-17:00"/> <wednesday hours="9:00-12:00 and 12:30-17:00"/>
<thursday hours="9:00-12:00 and 12:30-17:00"/>
            <friday hours="9:00-12:00 and 12:30-17:00"/>
            <holiday period="01/07/2008 - 31/08/2008"/>
        </business-calendar>
    </process-engine-context>
    <transaction-context>
        <repository-session/>
        <db-session/>
        <message-session/>
        <timer-session/>
        <history-session/>
        <hibernate-session current="true"/>
    </transaction-context>
</jbpm-configuration>

By and large, this is a pretty standard configuration for jBPM. It specifies many things that are safe defaults and largely out of the scope of this book. Mainly, the configuration tells jBPM which services to bring up and defines some configuration for those services. Because we're integrating with Spring, we modify the transaction-context element and the command-service element, as those are the touch points with Spring. The hibernate-session element tells jBPM to reuse an existing Hibernate session (the one we created with our Hibernate session factory) instead of creating its own. The spring-transaction-interceptor element is a special element to enable jBPM to defer to the TransactionManager defined in our application context. Here again, jBPM integrates by delegating to the Spring services, making for a very eloquent solution.

Building a Service with Spring

Problem

In the previous recipe, you configured Spring and jBPM, such that Spring's is successfully hosting jBPM. You set about writing a business process and now want to work with jBPM inside of your service code and to be able to delegate actions to Spring beans from within a business process.

Solution

Use Spring normally, injecting the services as you need them. For access within your business process, you can simply reference the services as you would any other process or environment variable in a business process. jBPM will expose beans using the jBPM expression language, and you can then just reference them by name.

How It Works

In this recipe, we'll work through a simple example so you can see the pieces of a typical integration with Spring and jBPM. We've already laid the groundwork in the previous example. Here, we'll actually build a simple Java service that works with Spring to move the process state forward.

The use case is a user registration, like the one described in Figure 23-1.

There are four steps:

  1. A prospective customer (modeled here as a Hibernate entity, Customer) signs up via a form. (We'll leave the web page interactions to your imagination for this example; suffice it to say that the form, when submitted, invokes a service method, which we'll define.)

  2. A verification e-mail is sent.

  3. The user (ideally) will confirm the receipt of the e-mail by clicking a link, which authorizes the user. This could happen in a minute, or in a decade, so the system can't afford to waste resources waiting.

  4. Upon confirmation, the user will receive a "Welcome!" e-mail.

This is a simple use of a business process. It abstracts away the process of incorporating and processing a new Customer. Conceivably, the Customer object could have come from any number of channels: somebody sitting taking phone calls inputs them manually, the user self-subscribes to the system, in a batch process, and so on. All of these different channels can create and reuse this business process, though. When the processing for a new customer is standardized, the challenge becomes about surfacing the functionality for as many end users as possible.

Because the user could conceivably wait a few days or weeks (it's arbitrary) before checking the e-mail and clicking the confirm link, state needs to be maintained but not burden the system. jBPM, and indeed most workflow engines, passivate state for you, allowing a process to wait on external events (signals). Indeed, because you've decomposed your process into a series of isolated steps, each of which contributes to the larger goal while remaining independently useful, you get the best of both worlds: stateful processes and stateless scalability. The state of the global business process is maintained and is persistent throughout the state of customer's sign-up, but you get the benefits of not keeping things in memory when there's no progress in the business process, thus freeing up the memory to handle other requests.

To build our solution, we need to build a simple CustomerService class and configure it appropriately. We'll integrate jBPM and tailor transaction management for the CustomerService class. We'll also make our bean responsible for deploying the process definitions for us as the bean starts up, so that if they weren't already deployed, they will be.

The XML for the application context is stark and simple.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx" 
How It Works
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
How It Works
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
                                                 ">
    <import resource="jbpm4-context.xml"/>

    <context:annotation-config/>

    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method propagation="REQUIRED" name="*"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* 
How It Works
com.apress.springrecipes..jbpm4.*.*(..))"/> </aop:config> <util:list id="annotatedHibernateClasses"> <value>com.apress.springrecipes.jbpm.jbpm4.customers.Customer</value> </util:list> <bean id="customerService" class="com.apress.springrecipes.jbpm.jbpm4.customers.
How It Works
CustomerServiceImpl"> <property name="processDefinitions"> <list> <value>/process-definitions/RegisterCustomer.jpdl.xml</value> </list> </property> </bean> </beans>

The first few elements are familiar: we set up the AOP–based transaction management and apply it to the services deployed under the jbpm4 package in our solution. Next, we override the List bean (with id annotatedHibernateClasses) that we created for the last recipe (jbpm4-context.xml) to provide the session factory with a collection of annotated entities, here, the Customer entity. Finally, we have a bean to handle the customerService bean. This bean leverages Hibernate (through the HibernateTemplate instance) to handle persistence, and it leverages jBPM (through the SpringConfiguration instance) to handle BPM. We provide the customerService bean with a list of business processes we want to ensure are deployed, which the bean handles as part of its duties in its post-initialization phase (the method annotated with @PostConstruct will be run after the bean's been configured to let the user inject custom initialization logic). In this case, we're deploying only one business process. Note that the business process file's name needs to end in jpdl.xml; otherwise, jBPM won't deploy it. The customerService bean is an implementation of the interface CustomerService, whose definition is as follows:

package com.apress.springrecipes.jbpm.jbpm4.customers;

public interface CustomerService {

        void sendWelcomeEmail(Long customerId);

        void deauthorizeCustomer(Long customerId);

        void authorizeCustomer(Long customerId);

        Customer getCustomerById(Long customerId);

        Customer createCustomer(String email, String password, String firstName, 
How It Works
String lastName); void sendCustomerVerificationEmail(Long customerId); }

The interface is trivial and only provides creation and mutation services for a Customer record. The implementation is where we see all the pieces come together.

package com.apress.springrecipes.jbpm.jbpm4.customers;

CustomerServiceImpl is a simple class. At the top, we've injected three dependencies: springConfiguration (which doesn't get used, though its configuration is worth noting because you may use it to access other services), repositoryService, and executionService. The class provides a few salient methods (some of which are required by its interface, CustomerService):

  • void setupProcessDefinitions()

  • Customer createCustomer(String email, String passphrase, String firstName, String lastName)

  • void sendCustomerVerificationEmail(Long customerId)

  • void authorizeCustomer(Long customerId)

In the bean, setupProcessDefinitions is run when the bean is created. It iterates through the processDefinitions collection and deploys the resource whose path it is given. If you monitor the logs, you'll witness SQL being issued against the database, creating the runtime structure of your process definition inside the database.

Building a Business Process

Problem

You've built a service that uses jBPM to create a working service. We've seen how jBPM is configured, and we've even built a service for a business requirement (the sign-up of customers). The last element that remains is the process definition itself. What does a business process definition look like? How does a process definition reference Spring beans?

Solution

We'll build a process definition that codifies the steps diagrammed in Figure 23-1 at the beginning of the chapter. This process definition will reference Spring beans using the JBoss expression language. Finally, we'll walk through how the business process uses our customerService bean and how the customerService bean uses the business process to handle the customer's sign-up.

How It Works

Let's examine the business process itself (RegisterCustomer.jpdl.xml). In jBPM, a business process is built using jPDL. You can use the Eclipse plug-in to model jBPM processes, but the jPDL schema is so simple that you don't really need it. This is not like BPEL where it can become all but intolerably complicated to write the code by hand. What follows is the XML for the business process:

<?xml version="1.0" encoding="UTF-8"?>
<process name="RegisterCustomer" xmlns="http://jbpm.org/4.0/jpdl">

        <start>
                <transition to="send-verification-email" />
        </start>

        <java name="send-verification-email" expr="#{customerService}"
                method="sendCustomerVerificationEmail">
                <arg> <object expr="#{customerId}" /> </arg>
                <transition to="confirm-receipt-of-verification-email" />
        </java>

        <state name="confirm-receipt-of-verification-email">
                <transition to="send-welcome-email" />
        </state>

        <java name="send-welcome-email"
                        expr="#{customerService}" method="sendWelcomeEmail">
                <arg> <object expr="#{customerId}" /> </arg>
        </java>

</process>

In the customerService bean, a client will use createCustomer to create a customer record. In a real-world example, you might imagine exposing these services as a SOAP endpoint to be consumed by various clients, such as a web application or other business applications. You can imagine it being called as a result of a successful form on a web site. When it executes, it creates a new Customer object and uses Hibernate to persist it. Inside the createCustomer method, we use jBPM to start the business process to track the Customer. This is done with the startProcessInstanceByKey method. In the invocation, we give jBPM variables through a Map<String,Object> instance (acting as something of a context for the process variables). Those variables are accessible inside the business process as Expression Language expressions and allow you to parameterize the business process in much the same way you might parameterize a macro or a Java method. We give the process instance a custom business key, instead of letting it generate its own.

executionService.startProcessInstanceByKey(
        REGISTER_CUSTOMER_PROCESS_KEY, vars, Long.toString(customer.getId()));

The last parameter is the key. Here, we're using the String ID of the customer as the key. This makes it easy to find the process instance later, though you could also query for the process instance, predicating on process variables that, taken together, should make the process instance unique. You might also query by roles or users assigned to certain tasks, or simply note the ID of the business process itself in your domain model and reference that later when looking up the process instance. Here, we know that there's only ever going to be one sign-up process for a customer, so we key it with a valid ID that will work only once: the Customer's id value.

When the process starts, it will start executing the steps in your process definition. First, it will go to the <start> element. It will evaluate the one transition it has and proceed through that transition to the next step, send-verification-email.

Once in the java element named send-verification-email, jBPM will invoke the method sendCustomerVerificationEmail on the customerService bean in Spring. It uses an Expression Language construct to reference the Spring bean by name:

<java name="send-verification-email"
        expr="#{customerService}"
        method="sendCustomerVerificationEmail">
        ...
</java>

The sendCustomerVerificationEmail method takes the customer's ID and sends a notification. We leave the functionality of actually sending the e-mail to you—just imagine a unique, hashed link being generated and embedded in the body of the e-mail that lets the server trace the request back to a customer.

Once the process has left the send-verification-email java element, it'll proceed to confirm-receipt-of-verification-email state, where it will wait indefinitely before proceeding. This is called a wait state. An external event is needed to tell it to proceed. In our scenario, this event will come when the user clicks on the link in the e-mail, which, for the sake of the demonstration, will trigger the invocation of the authorizeCustomer method on our customerService bean. This method expects a customer ID as a parameter.

Inside authorizeCustomer, the service queries the server for the any processes waiting at the confirm-receipt-of-verification-email state and having this customer's ID. We know that there's only one instance of this process, but the query returns a collection of Execution instances. We then iterate through the collection, signaling (with the Execution instance's signalExecutionById method) the transition from a wait state to the next state. When a node in jBPM moves from one to another, it takes a transition. As you've seen before, it does so implicitly. Here, however, in the wait state, we have to explicitly tell it to take a transition to signal that it can proceed to the next node.

for (Execution execution : executions) {
                Execution subExecution = execution.findActiveExecutionIn(
                        "confirm-receipt-of-verification-email");
                executionService.signalExecutionById(subExecution.getId());
        }

The authorizeCustomer method also updates the Customer entity, marking it as authorized.

From there, execution proceeds to the send-welcome-email java element. As before, the java element will be used to invoke a method on the customerService bean. This time, it will invoke sendWelcomeEmail to send the newly registered Customer a welcome e-mail.

The name of the process (what we use when we call startProcessInstanceByKey) is in the process element. Here, that name is RegisterCustomer.

Many of the expressions are in the JBoss expression language, which works very similarly to the unified Expression Language (EL) found in JavaServer Faces, or the Spring EL. You can use the EL here to reference parameters to the process.

You'll recall that when we invoked the service method for createCustomer, it, in turn, kicked off a business process whose progression the rest of the code followed. We use a Map<String,Object> to parameterize the business process. In this case, our Map<String,Object> was called vars.

Map<String, Object> vars = new HashMap<String, Object>();
    vars.put("customerId", customer.getId());
    executionService.startProcessInstanceByKey(
     REGISTER_CUSTOMER_PROCESS_KEY,
     vars,
     Long.toString(customer.getId()));

From within the running process, you can access the parameters from Java or as an EL expression. To access the parameter using the EL, use something like: #{customerId}. To access the parameter from Java code at runtime, use the following:

Number customerId = (Number) executionService.getVariable(subExecution.getId(), 
How It Works
"customerId") ;

At this point, you've got a working business process that lives inside of a Spring context, and you've got a working grasp of the constructs required to build a process.

Summary

In this chapter, you were given an introduction to business process management as a technology. You should have a big-picture view of the technology. There's much more to learn, because BPM can become a key piece of architecture, providing a spine to otherwise isolated functionality. No single introduction to one particular brand of BPM will ever be adequate. There are lots of resources out there, though it helps to keep an eye on the bookshelf at your local bookstore, because often these technologies become irrelevant as quickly as the sands of the markets shift.

BPM is a discipline that's rooted in many decades of growth and concepts. The earliest tenants of workflow engines have their bases in a branch of mathematics called Petri nets. The discipline's become more mainstream over the years, and the focus evolved from using BPM as a record-keeping mechanism to an enabling orchestration mechanism.

There are several very good discussions of BPM if you're curious, including several good sites on the Internet and many more good books readily had by searching for "BPM" in a search engine. For a deeper treatment of the discipline (though not necessarily the technology), we recommend the following:

  • Production Workflow: Concepts and Techniques by Frank Leymann and Dieter Roller (Prentice Hall, 2000)

  • Essential Business Process Modeling by Michael Havey (O'Reilly, 2005)

  • http://www.workflowpatterns.com/ (a comprehensive introduction to the patterns of workflow)

In the next chapter, you will learn about OSGi and how to build OSGi solutions using Spring Dynamic Modules and SpringSource dm Server.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset