5. Introducing jBPM – Open Source SOA

Chapter 5. Introducing jBPM

We've spent the last two chapters talking about the Service Component Architecture (SCA) and its implementation using Apache Tuscany. You've learned how to use this framework to create reusable services that can be exposed through a variety of protocols, such as SOAP-based web services, JMS, and RMI. The ability to create and propagate such component-based services is one of the central principles of SOA. Now, in the next three chapters we'll address business process management (BPM), which, at its core, is about leveraging these services to create business processes. In other words, we're transitioning from how to create the services to how they can be consumed and used. As you'll learn, BPM represents a new paradigm for software application development where services can be woven together into visual models that reflect actual business processes.

There's reason to believe that BPM won't suffer the same ignominious fate as other business and technology initiatives such as total quality management and process reengineering. This is because it's grounded in the notion of the process, which, after all, is what defines a business. What exactly constitutes a business process? Prahalad and Krishnan define it as "the link between the business strategy, business models, and day-to-day operations" [Prahalad]. Or, as Smith and Fingar put it, "Processes are the business" [Fingar]. BPM, unlike reengineering, strives to leverage the information systems already in place. BPM's aim is multifold, but its main objectives are to

  • Streamline business processes

  • Improve/maximize automation

  • Improve visibility/control of ongoing processes

  • Rapidly orient processes to support new or changed business initiatives

The last point is particularly noteworthy—improving business agility is a central objective of nearly any organization today. When process and workflow are codified into business systems such as ERP or CRM, changing them can be an enormous and disruptive undertaking. Moreover, such systems often only span a portion of the entire value chain used for supporting a product or service. BPM, on the other hand, is intended to encompass the entire value chain process, including interactions with external partners or customers.

An often-overlooked benefit of BPM is how it accelerates the rollout of new business processes within the enterprise. Back in the day, modifying or rolling out a new business process usually relied on manual training to implement. That entailed new procedure guidelines, training materials, and some wishful thinking that the process would be followed. With BPM, automation eliminates the human interpretation factor. In a recent article in Harvard Business Review titled "Investing in the IT That Makes a Competitive Difference," the authors cite this ability as a key differentiator, noting that "a company's unique business processes can now be propagated with much higher fidelity across the organization by embedding it in enterprise information technology. As a result, an innovator with a better way of doing things can scale up with unprecedented speed to dominate the industry" [McAfeeBrynjolfsson].

Another unique concept introduced by BPM is that of visualization. A BPM system is designed to enable business users to craft and design business processes in a visual fashion, resembling a flowchart. While Visio and other modeling tools have been used for years by subject matter experts for conveying requirements, they were static in nature, and the resulting codification by developers often bore little or no resemblance to what was modeled. Ultimately, developers, who lacked the deep understanding of the business processes, were left to interpret their meaning, often with disastrous results. BPM models, however, are intended to be executable manifestations of actual business processes. Thus, they can be interrogated at runtime to determine status as well as optimized for efficiency and for service-level monitoring.

Note

It's not practical to assume that a business model developed by an analyst is going to be "execution-ready" without some technical embellishment by developers. At least both the developer and analyst are working with a common visual notation.

The benefits of BPM may now seem obvious, but what's the relationship between BPM and SOA? Quite simply, a service-oriented architecture is a critical enabler for BPM. Workflow systems in the past have failed because there was often no easy way to integrate the steps within the workflow to the functions within the business applications (this is also what doomed early proponents of rule-based systems). For example, a purchasing workflow application of yesteryear might have had convenient ways to collect the details of a purchase order (PO) and offer routing and approval capabilities, but there was often no easy way to tie the approved PO into the system used by accounts payable or inventory management. Thus, "swivel" chair integration resulted, with someone rekeying the data from the workflow app into the other systems. More complex workflow scenarios had even more challenging integration requirements that often went unmet due to the historically stovepipe nature of IT systems. Now, since SOA is all about exposing discrete business services in an easily accessible fashion, BPM can easily tap into and exploit these capabilities.

Now that we've covered some of the background of BPM and its benefits, we can examine more closely its role within SOA. After that, we'll explore the basics of our Open SOA Platform's BPM product of choice: JBoss jBPM. In the next chapter we'll more closely dive into human interface tasks, which constitute an important part of any BPM solution. We'll conclude with a final chapter on advanced jBPM concepts and also describe how jBPM can be integrated with Apache Tuscany SCA to create a truly compelling service and orchestration solution. Let's get started!

BPM: the "secret sauce" of SOA

Bear in mind that SOA, by itself, is not the endgame. Instead, it's intended to help forge a more flexible, easy-to-manage, and ultimately higher-quality software infrastructure. The promises of BPM can be realized when surrounded by a SOA-based environment. Figure 5.1 depicts the relationship among the components, services (which were the topic of the last two chapters on SCA), and BPM.

Figure 5.1. The relationship among BPM, services, and components<br></br> 

As figure 5.1 illustrates, the services exposed through SCA, which themselves consist of one or more components, can then be consumed by BPM, for which we're using jBPM. Of course, business processes may also invoke externally accessible services and include human interface tasks.

In addition to being a benefactor of SOA, BPM also nicely complements other SOA-related technologies such as complex event processing (CEP). This is because, by decomposing a process into individual steps within a workflow, you can transmit events at each node throughout the lifecycle of the process. This can be most easily illustrated through a sample BPM process, as shown in figure 5.2.

In figure 5.2, the event notifications are illustrated by the little bell icon. Where shown, each transition would fire an event that could be consumed by a CEP (covered in chapter 8) or business activity monitoring (BAM) dashboard. Additionally, the highlighted nodes (ship item, update books) represent callouts to external services, such as those that can be created through SCA (chapters 3 and 4).

While figure 5.2 illustrates the complementary nature of many of the technologies used in building our Open SOA Platform, there's enough overlap between some of the technologies to cause confusion. One area in particular that comes to mind is how to distinguish between an ESB's flow control capabilities versus the process flows inherent in a BPM. Understanding this distinction will help you better frame where the technology boundaries lie between these technologies, and thereby become a more effective advocate of BPM within your organization.

Figure 5.2. The BPM process, with events and services highlighted using JBoss jBPM<br></br> 

As we'll discuss in chapters 9 and 10 in our coverage of Apache Synapse, our open source ESB component of our Open SOA Platform, an ESB is most effectively positioned as a messaging broker between various systems and protocols. In other words, it's most effective when used to bridge between different communication protocols and/or transforming messages from one format or vocabulary to another.

BPMs, on the other hand, are designed to model and execute complex business processes. As such, they are frequently long-running in nature and often involve a "human-in-the-loop" at various points along the way. The notion of wait states is a central concept to a BPM solution. On the other hand, an ESB's routing capabilities are intended for real-time processing. Best practices thus suggest that BPM be used for modeling business processes, with ESB's routing limited to real-time data flows required for brokering and transforming messages between systems and protocols.

Now that we've established the benefits of BPM and you understand its role in relation to SOA and its place within the enterprise, we can proceed with exploring JBoss jBPM, which is our Open SOA Platform's BPM selection. Let's begin by looking at a bit of its history as a product, and then we can examine key concepts and features. For those anxious to dive into some actual examples, rest assured that will be happening shortly.

History and overview of JBoss jBPM

The first significant release of jBPM occurred with the 2.0 edition that was introduced in 2004. It represented a major milestone, as many of the core features that represent today's product (release 3.2.6 as of this writing) were first introduced in that version. jBPM includes the features required of any BPM system, and commercial alternatives often start with a price tag in the six-figure range. The product's popularity has steadily grown, according to the download statistics on SourceForge.net. There have been over 150,000 downloads of the most recent release, contrasting with about 15,000 for the 3.0 product that was released in 2005. The jBPM user forms hosted on the JBoss community site also testify to the increasing popularity of the solution. One of the most significant features released in the 3.2 edition was the jBPM Console, which provided an easy-to-use graphical interface for managing processes and instances.

Besides its rich set of functionality, one of the main selling points for jBPM is the proven and fast process execution engine that represents the core foundation of the product. Further, the engine itself is lightweight and represents just a handful of JAR files. The persistence engine, where the processes, process instances, and state metadata reside, supports nearly all popular databases, both commercial and open source. Since the product is open source and designed for adding new functionality extensions, it's also highly flexible.

Before we get into the features and capabilities, let's first examine the lifecycle of how a jBPM process is typically developed and deployed, as this will provide context for our technical discussion later.

Development lifecycle of a jBPM process

There are six major phases in developing a jBPM process (and indeed, likely any BPM product). They're illustrated in figure 5.3.

Some of the steps resemble those in a typical software development project, but others are unique to developing a BPM process. Let's take a look at each step in the process in more detail.

Figure 5.3. BPM process lifecycle steps using JBoss jBPM<br></br> 

IDENTIFYING PROCESS WORKFLOW

As we pointed out earlier, BPM is suited for modeling, automating, and executing business processes. A business process, by definition, is a sequence of steps (or collection of activities) necessary to perform a business function. Examples abound in any organization, from on-boarding of new employees or customers to order processing, and invoice and purchase order approval. Other processes are specific to a given industry, such as a patient registration process for a hospital, or the setup of clinical trials for a pharmaceutical company. Identifying a candidate process in which to use BPM can be challenging, since the complexity of some processes make them more difficult than others to implement. The biggest benefit can likely be achieved where there is a clearly defined existing, high-volume process that involves integration with one or more systems and perhaps includes human interface tasks. An example is the web-based sales order process shown earlier in figure 5.2 (which admittedly is simplified).

MODELING THE PROCESS VISUALLY

Once an appropriate business process is identified, the next step is to model it using the visual process editor. The jBPM Graphical Process Designer (GPD) editor, which is an Eclipse IDE plug-in (and like the rest of jBPM, fully open source), is shown in figure 5.4 and is included in what's known as the jBPM Suite.

As you can see, the editor likely resembles other modeling or illustration tools you have previously used. There is a tools palette you can click on to select any of the available nodes or controls, and then click again within the design palette to paint the object. In the next section we'll describe what each of the objects represented by the various icons means in figure 5.4.

Figure 5.4. jBPM Graphical Process Designer<br></br> 

The process design tools are kept purposely at a fairly abstract level so that an analyst can model processes without being overly concerned with the underlying implementation. It's not realistic to assume that an analyst can develop completely production-ready models, because executable business processes usually contain some amount of programming code, which is addressed next.

DEVELOPING THE RUNTIME COMPONENTS

In figure 5.4, you'll notice the diagram items identified as <<state>> (categorize, assign) and <<node>> (notify-partner, product-improvement). These represent examples where underlying code is required for implementation. For example, let's assume that the notify-partner node is used to send the support request to a business partner via XML over HTTP. Obviously, this will entail a programmatic exercise (even assuming you developed custom components for such things, it would still entail configuration that likely must be done by a developer). While the analyst can develop the overall workflow, a developer is needed to "fill in the holes" and provide the implementation logic. This can be done within jBPM without having to complicate the visual model by implementing action handlers, or classes, that perform the actual work. So the visual process model created in the previous step is then followed by development of the runtime implementation. Examples of such implementation code will follow shortly.

DEPLOYING TO THE RUNTIME ENGINE

Once the process definition and its corresponding runtime implementation code are completed, it can then be deployed. Within jBPM, there are several different deployment scenarios. The most common one, which we'll focus on, is where the jBPM engine is running within the context of a web application, such as the jBPM Console. However, the engine instance can also be instantiated within any standard Java class. This is most applicable in scenarios where you're embedding jBPM entirely within an application (an illustration of this method can be seen when creating a new jBPM project within the GDP, as it creates a JUnit test class that uses this method).

In the scenario where the jBPM process is deployed to run within the jBPM Console, the Deployment tab shown in figure 5.4 is used for creating a deployment package. When selected, it provides configuration options for packaging, as shown in figure 5.5.

As illustrated in figure 5.5, a jBPM process can be packaged for deployment in one of two ways. (The archive file is sometimes called Process Archive file, or PAR). The first option is to export the archive file to the file system as a PAR, which can then be manually uploaded as a new process using the jBPM Console. The second option is to deploy the process directly to a running instance of the jBPM Console. Each time a process is loaded into the jBPM, it's given a separate version number—this is the only practical approach, since an existing version may have active process instances, or historical instances that must be preserved for auditing purposes.

Figure 5.5. Deployment options for the jBPM process<br></br> 

Note

In actuality, a PAR file is really just a zip file that contains all the necessary artifacts required for deployment, such as the process diagram. In the sample code for chapter 6, Ant targets can be found that will (a) prepare the PAR files, and (b) deploy them automatically to the jBPM Console.

INSTANTIATING THE RUNTIME INSTANCE

Once a process is deployed, a new instance of it can be instantiated. How this is done, in part, depends on the nature of the business process. In our example from figure 5.4, it would begin once a new customer service request has arrived. What exactly does that mean? If you recall from chapters 3 and 4, we developed a SOAP web service that could receive inbound problem tickets. The web service implementation, written in Java using SCA, could act as a jBPM client and initiate a new process instance directly. You'll learn how to do this in chapter 7. Another approach is to use the jBPM Console to start a new process instance manually.

Keep in mind that, like BPEL-based orchestrations, an instance represents a single execution of a given process/definition. So in our example of a customer service request, each request would instantiate a new process instance. That process instance, in turn, is persisted to the database when it's not active (that is, it's in a wait state, such as waiting for a human interface task to be completed) and then reactivated when some activity is triggered within it. In the BPEL world, they refer to the process of persisting an idle process's data and state to a database as dehydration. Hydration then occurs when the process becomes active, and the data and state are placed in memory. Without such an approach, a proliferation of running process instances could quickly consume available memory. (This approach is one reason why BPM is fundamentally different from ESB-based routing, which takes a queue-based approach when it passes messages from one state to another.)

Once processes are running, it's obviously essential that they be able to be monitored for ongoing progress. Although monitoring isn't a particularly glamorous topic, widespread BPM deployment depends on this capability.

MONITORING, OBSERVING, AND AUDITING

This is a fairly broad topic, and we devote considerable coverage to it in chapter 8, which focuses on event stream processing. One of the big benefits of BPM is that, because it's intended to encapsulate and execute business processes in a manner that reflects the "real-life" process, the metrics that can be captured throughout the process should be business relevant. For example, in figure 5.4 each step in the process can emit events that can be presented and analyzed in real time. In this case, it may include monitoring whether the number of high-priority problems has exceeded certain thresholds or exhibits greater variance than normal. These metrics can form what is sometimes called a sense and respond solution. This involves jBPM, like most BPM solutions, addresses some of these requirements through built-in functionality. For example, tasks within jBPM can be assigned timers, so that if items aren't completed in the appropriate period, an escalation path can be triggered. Also, jBPM Console provides a convenient way to monitor process instances, as you can see in figure 5.6.

  • Event monitoring—Each step, or "hop," in the process should publish events.

  • Determining what matters to customers and partners, and focus monitoring efforts on these critical areas (the "sense")—For example, verifying that service-level agreements are being satisfied by monitoring response or completion time.

  • Alerting people and systems when unanticipated trends or anomalies occur—This is the "response."

Figure 5.6. jBPM Console Process Instances view<br></br> 

As highlighted in figure 5.6, you can filter by process instance status (R=Running, S=Suspended, E=Ended). Additional filtering can be done by searching on the instance key (the field to the left of the status flags), which in this example represents the subject line of the problem report. If you select the Examine link associated with each process instance, you can see the particulars about the process instance. This includes process variables, tasks, instance comments, tokens (which represent execution paths, discussed in the next section), and a visual depiction of the current state of the instance, as shown in figure 5.7.

Figure 5.7. jBPM process instance detail view displaying the process instance image<br></br> 

The jBPM Console view thus provides considerable details about running process instances, and enables the administrator to perform actions such as stopping, starting, and suspending instances. (Note that within the instance image graph, you can click on the highlighted node text to drill down into the details of the node, such as the review node shown in figure 5.7). The jBPM API can be used to retrieve additional information and allows for querying the database that houses all instance data. We'll cover this topic in detail in the next chapter.

Although the overview of the product and its development lifecycle may have had you reaching for your coffee, we'll now switch gears and lift up the hood (where all the exciting stuff happens). We'll begin by looking at the jBPM process language and touch on the integration points where runtime code can be introduced.

Graph-oriented programming and jBPM

The authors of jBPM like to refer to jBPM as a form of graph-oriented programming. By this, they refer to the use of visual graphs that can be used to describe and execute application logic. The graphical nature obviously deviates from conventional programming, as does its inherent support for wait states and long-running transactions. How a process is defined is determined by the process language that's used. In jBPM's case, you have two choices: jPDL (jBPM Process Definition Language) and BPEL. So, at its core, jBPM can be considered a platform or engine that can support multiple process languages. For the reasons cited earlier, our focus is on jPDL, which is better suited for BPM than BPEL (jPDL was initially the only language supported by jBPM).

While jPDL isn't a standard per se—it hasn't been formally submitted to any standards body—it's well documented and licensed as open source. Compared with competing process languages such as XML process definition language (XPDL), business process definition metamodel (BPDM), or BPEL, it's very elegant in its simplicity and is easily extendable. For example, the source jPDL used for the process illustrated in figure 5.7 is about one page of XML code, which is easily understandable by those not even well versed in jPDL (numerous examples can be found in the sample source code, and are usually named processdefinition.xml).

Note

Those familiar with BPEL can attest to what a contrast jPDL represents compared with that standard. Further, the semantics of BPEL can be confusing to those first trying to learn it—jPDL's XML Schema is refreshingly concise and easy to follow.

Let's now examine the constructs that constitute the language, which at its core is composed of nodes and transitions. These form the basis for how processes are constructed.

Understanding nodes

In the process diagrams you have seen so far, the steps (or blocks) in the process flow represent nodes and the lines between them transitions. As you may recall from figure 5.4, different types of nodes are available, including task, node, state, mail, and decision (the types were specified by the text surrounded by << and >>). Regardless of the type, nodes serve two purposes: (a) they can execute Java code, and (b) they're responsible for moving forward or pausing the process execution. When a node is entered, it begins executing any code associated with it. This may entail entering a wait state pending some outside action, or it may propagate the execution, which simply means that it advances the execution forward. Since there can be more than one transition path leaving the node, the node is responsible for telling the process engine which transition to follow.

Let's examine the built-in nodetypes that come included in the jPDL language: node, task-node, state, mail, decision, and fork/join.

Node nodetype

The node nodetype is the basis from which the other nodetypes are derived (or custom ones you might create), but it can be used by itself in situations where you want to code the runtime behavior. You can think of the node nodetype as a generic style node, with no specific behaviors associated with it. For example, a node nodetype could be used by an a nalyst to graphically depict that a call to an external system must be made. However, the implementation logic to perform this action will be left for development. In figure 5.7, a node was used to identify that the problem ticket should be sent to a partner for resolution. The developer would then write the implementation action handler, which is discussed in section 5.5. What's important to recognize, however, is that the implementation code for this type of node must identify and trigger the transition path to be followed when the execution exits the node. Here's an example of how a node is defined within jPDL:

<node name="notify-partner">
  <action
    name="partnerNotification"
    class="info.open-soa.actions.NotifyPartnerAction">
  </action>
  <description>
Notify partner of problem ticket for them resolve.
</description>
  <transition to="notify-customer"/>
</node>

Notice in this example that an action element is defined, and the @class attribute specifies the name of the Java class used to implement the logic. The description element is used to enrich the node definition, and the transition element identifies the possible transition paths that can be followed; in this example there's just one transition called notify-customer. The action handler assigned to the node, as we pointed out earlier, must propagate the execution—in other words, move it along.

A standard set of possible elements and attributes is available that can be used when defining any of the nodetypes. Table 5.1 lists them (attributes are prefixed with an @), and the description of the subsequent nodetypes will identify any deviations from this standard set (some of the others will add configuration options).

Table 5.1. Standard nodetype attributes and elements<br></br> 

Element/Attribute Name

Description

@name

Specifies the name of the node. Required.

description

Describes the node for documentation purposes. Optional.

event

Describes actions to be performed when events are triggered at various moments in the execution of the node. Common events include transition, node-enter, and node-leave. The actions defined as part of an event cannot influence the flow control of the process. This contrasts with the action element defined as a descendant of the node nodetype, which must do so. Section 5.8 will discuss events in more detail. Optional.

exception-handler

Allows the developer to define a list of actions to take based on the type of error being caught. However, like event actions, they can't modify the graphs event flows. Exception handling is a relatively advanced topic and will be discussed in more detail in chapter 7. Optional.

timer

A timer can be assigned to a node with the clock starting when the node is entered. Actions can be triggered based on timer expiration. Since timers are most often used in conjunction with tasks, they're covered in more detail in chapter 6. Optional.

transition

Defines the destination node for the execution path. Transition names should generally be unique to avoid unexpected problems. A transition path can be explicitly invoked by name, but if no name is specified and multiple transitions are available, the first one will be selected. Transitions can also define an action class or script that's invoked when it's entered. Optional.

The only thing unique to the node nodetype is the action element, which is required. As we pointed out earlier, for the node nodetype, the action handler specified in the action element is responsible for signaling the advance of the process and for identifying the transition path.

Task-node nodetype

The task-node nodetype is used to specify one or more tasks that are to be performed by a human (but they can be processed programmatically through the API as well). The individual performing the task then signals what transition path to following moving forward. Unlike the other nodetypes described thus far, tasks involve human interaction with the business process.

Note

Although tasks are usually associated with human interface activities, using the API you can interact with them in any fashion you want. The next chapter focuses on using tasks.

As you might imagine, there's a significant amount of functionality that necessarily accompanies the task nodetype. This includes how the task information is conveyed to the user, how an actor or user is assigned a task, and how it can be monitored for timely completion. Because of the broad scope that tasks involve, the next chapter is dedicated to covering this subject.

State nodetype

Described in the jBPM documentation as a "bare-bones" node, the state nodetype is somewhat of a hybrid between a node nodetype and a task-node nodetype. It's similar to the latter in that it introduces a wait state not unlike a task pending completion. However, a state nodetype is waiting for what presumably is another system to complete, not a human. The external system could be notified via an event action, such as node-enter, and then waits for a stimulus to signal advancement of the token. The only real distinction between a state nodetype and a node nodetype is that a state doesn't allow a direct action handler to be defined. Additionally, the node nodetype doesn't introduce a wait state and is expected to be processed immediately.

What's an example of where a state nodetype would be useful? One scenario is where some additional information is required from a third-party system. In such a case, the original system must wait for a response from the third-party system before proceeding. For example, a quote request that's part of a process might forward the information to a CRM system, and a user of that system would receive and process it. Once the quote is completed, a notification would be sent from the CRM to a service, which would receive it and interact with the jBPM API to advance the process.

Mail-node nodetype

As the name suggests, the mail-node nodetype is used to send outgoing email, which is triggered when the node is entered. Here's an example of how you can use it:

<mail-node
  name="notify-customer"
  actors="jdoe"
  subject="Urgent customer email received: #{problem_subj}"
  text="#{problem_desc}">
  <transition to="product-improvement"/>
</mail-node>

In the example, you'll notice the use of tokenized variables, which take the form of #{varname}. These allow you to dynamically assign variable values at runtime (section 5.7 will discuss how variables are created). In the preceding code fragment, the mailing address will be resolved by using the @actors attribute (actors are described in more detail in the next chapter, in our coverage of tasks). In lieu of the @actors attribute, the @to attribute can be used to explicitly set the destination email address. See table 5.2 for a full description of the mail-node nodetype elements and attributes.

Table 5.2. Mail-node nodetype elements and attributes<br></br> 

Element/Attribute name

Description

subject

Subject of the outbound email. Optional.

@subject

A convenience alternative to using the subject element. Optional.

text

Text/body of the outbound email. Optional.

@text

A convenience alternative to using the text element. Optional.

@async

A boolean used to indicate whether to use an asynchronous continuation to send the email (asynchronous continuations are covered in chapter 7). Defaults to false and is optional.

@template

Enables you to specify a different template to use. This allows you to tailor the format and content of the outbound message. Optional.

@actors

Specifies one or more actors to send the outbound message. Uses the actorId to resolve the email address associated with that individual. The @actors attribute, or @to is required.

@to

Specifies an actual email address to which the mail will be sent. Either it or the @actors attribute is required.

Within the task-node nodetype, you can also specify that an email be created when the task is assigned, followed by reminder emails that adhere to a timer-style configuration. Thus, in the case of emails associated with a task, there may not be the need to use the mail nodetype.

As briefly mentioned in table 5.2, you can use a mail template file called jbpm.mail.templates.xml to define customized messages (if a different name is used, it must be defined within the jbpm.cfg.xml file in the format of <string name="resource.mail.templates" value="jbpm.mail.myfile.xml" />, for example). Within this file, you specify one or more mail-templates and then reference a given template name when configuring your mail node. Here's an example of a mail template definition:

<mail-templates>
 <variable name="BaseTaskListURL"
   value="http://yourhost:8080/jbpm-console/sa/task.jsf?id=" />

 <mail-template name='task-assign'>
  <actors>
    #{taskInstance.actorId}
  </actors>
  <subject>
    Task '#{taskInstance.name}' has been assigned to you!
  </subject>
  <text><![CDATA[Hello,

Task '#{taskInstance.name}' has been assigned to you by
your friendly Business Process Manager application.

Please visit #{BaseTaskListURL}#{taskInstance.id} to complete

Thanks.]]></text>
 </mail-template>
</mail-templates>

Notice the use of the variable substitutions, which are delimited by #{}. The variables that can be exposed in this fashion are the properties associated with these objects: TaskInstance, ProcessInstance, ProcessDefinition, Token, TaskMgmtInstance, ContextInstance, and any process variable. You can also create your own variables within the template itself, as demonstrated earlier when we created the variable BaseTaskListURL. To reference this template in your mail node definition, you'd specify the template name task-assign in the @template attribute.

Decision nodetype

Within jBPM you have multiple ways in which decisions can be made as to which transition or path to follow. One method is to specify a condition within the definition of the transition itself (discussed in the next section). Another method, which can be used in node, state and task-node nodetypes, is for an action handler or individual (in the case of a task) to elect a transition path to take. The last approach is to use a decision nodetype; this is appropriate when you want the process to make the decision based on a BeanShell expression result. Here's an example of a decision nodetype definition:

<decision name="high-priority?"
  expression='#{ ( priority == "HIGH" ? "yes" : "no") }'>
  <transition to="email-notification" name="yes"/>
  <transition to="assign" name="no"/>
</decision>

In this example, priority is a process variable that contains values such as HIGH, MEDIUM, or LOW. This decision rule will direct HIGH priority messages to the transition named yes. The same effect can be achieved using the following variation:

<decision name="high-priority?" >
  <transition to="email-notification" name="yes">
    <condition>#{priority == "HIGH"}</condition>
  </transition>
  <transition to="assign" name="no">
    <condition>#{priority != "HIGH"}</condition>
  </transition>
</decision>

Lastly, you can also use a handler class to determine which transition path to follow. Simply specify the class in the handler child element, for example:

<decision name="high-priority?" >
   <handler class="com.sample.MyDecisionHandler"/>
</decision>

In this case MyDecisionHandler must implement DecisionHandler and its required method decision. This method must return a string value to indicate which transition path to follow (we'll demonstrate several handler examples beginning in section 5.5, and all handlers follow a similar usage convention and are simple to implement). Unlike in many other cases, there doesn't appear to be support for using an embedded BeanShell script to function as a handler in the case of a decision node.

Fork and join nodetypes

In many business processes, there can be concurrent paths of execution. For example, the on-boarding process for a new hire may involve several tasks that can be done in parallel, such as issuance of security credentials, I-9 form processing (used for employment eligibility in the United States), parking pass assignment, and HR systems registration. While they can be done concurrently, they must all be completed and closed for the process instance to conclude. This is where the fork and join nodetypes can be used (see figure 5.8 for an example).

Figure 5.8. An example of jBPM fork and join<br></br> 

While conceptually the fork and join construct is rather straightforward and intuitive, it's considerably more complicated from a process engine standpoint. In particular, each forked path necessarily results in a new token being created. These represent child tokens of the parent root token. This behavior can be best witnessed through the jBPM Console when viewing the tokens for an active process instance that includes a fork, as shown in figure 5.9.

In the example shown in figure 5.9, the parent token is 565 as it has no parent and resides at the forked node. The remaining tokens are child tokens and will be merged back when all of them enter the join node. When a token execution arrives at the join, the join will determine whether any active siblings of the parent remain. If not, the parent execution token is reactivated. Otherwise, it will wait for the remaining tokens to complete.

We've done a whirlwind tour of the nodetypes, and you're probably a bit fatigued at this point. The next section on transitions will conclude our coverage of how processes are diagrammed. After that, we'll move on to how you can incorporate your own custom implementation code.

Figure 5.9. BPM Console illustrations of forked tokens in a process instance<br></br> 

Using transitions

We've already demonstrated some of the capabilities associated with transitions, particularly in the coverage of the decision nodetype in the previous section. Transitions represent a central role in the definition of a process, as they link the various nodes to create a directed graph. In other words, transitions can be thought of as the glue that holds together the nodetypes that we just described into a complete business process. The configurable elements and attributes available to transitions are displayed in table 5.3.

Table 5.3. Transition elements and attributes<br></br> 

Element/Attribute name

Description

description

Description of the transition; beneficial for documentation purposes. Optional.

condition

Optional expression that acts as a guard to determine whether or not the transition should be followed. The expression can either exist as element text (i.e., <condition>#{a > 5}</condition) or as empty element when the @expression attribute is used (i.e., <condition expression="#{a > 5}"/>). Optional.

action

Allows for a Java action class or BeanShell script to be used for custom logic. See the next section. Optional.

exception-handler

Allows the developer to define a list of actions to take based on the type of error being caught. However, like event actions, they can't modify the graph's event flows. Exception handling is discussed in chapter 7. Optional.

@name

The name of the transition. Each transition leaving a specific node must have a unique name. Optional.

@to

The name of the destination node. Required.

Deciding which transition to follow when multiple transitions exist depends in part on the nodetype being exited. For example, in a task node, the decision on which transition to follow is usually made by the user. A web-based task form, for instance, might include buttons for each of the available transitions (such as send-back, approve, reject, etc.). For node and state nodetypes, a custom Java class or script can dictate the transition path through explicit signaling.

We've now covered the basics of diagramming within the process model. We've identified the available nodetypes, and you learned how they can be wired together via transitions. Of course, using the nodes as is doesn't provide all that much capability. The real power behind jBPM lies in actions, which are hooks where developers add their own programming logic.

Extending using actions

Actions and events allow programming logic to be inserted within a process model in a way transparent to the analyst or subject matter expert who designed the model. In most circumstances, the business modeler won't have the requisite skills or inclination to deal with the underlying plumbing necessary to create a truly runtime execution process. Indeed, if the model becomes complicated by visual representation of such technical details, the utility of the model in conveying the flow of the business process will quickly become lost. Instead, Visio and other static modeling tools will again become prevalent, with the models developed by them entirely abstract in nature. jBPM attempts to balance between creating a descriptive model and producing an executable process. This balance is achieved through the use of actions.

Actions are the integration points for interfacing with other applications or services. It's through actions that the services designed using SCA (chapters 3 and 4) can be readily used and consumed (you'll learn more about this in chapter 7). Depending on how it's configured, an action can be used to asynchronously send out a message to another system; used synchronously, such as when calling an external web services; or even used in a callback scenario where a request to a remote system is made and initiates a method call when it's completed.

The action element within jPDL defines the locations where actions can be used. They are as follows:

  • Node nodetype—An action handler is used to implement the custom functionality associated with the node and to advance its token to the proper transition. An action is required when using a node, as no other inherent functionality is provided by this nodetype.

  • Transitions—As a child of the transition element, an action can be invoked when the transition is triggered. This is often useful for setting process instance variables.

  • Events—Discussed in more detail in section 5.6, actions are used within events to invoke programming logic based on various triggers, such as when a node is entered or exited. Actions, when used as part of events, can't directly influence the flow of the process but instead should be used for notification purposes, such as setting or updating process instance variables.

  • Exceptions—We dive into exception handling in chapter 7, but suffice it to say that actions can be triggered when an exception occurs. Similar to event actions, they can't (or shouldn't) affect the flow of the process, but can indirectly do so by setting process variables (which downstream may use the variables in decision nodes, etc). Also, actions in this context are useful for sending error notifications.

  • Timers—Actions can be triggered when a timer expires.

Creating a Java action class is straightforward—it's just a plain old Java object (POJO) that simply implements ActionHandler. The only method required to be implemented by ActionHandler is execute. The execute method, which returns void, takes a single parameter of type ExecutionContext, which is passed to it by the jBPM engine at runtime. The simplest way to get started with creating an action class is to create a new process project using the jBPM Eclipse-based Graphical Process Designer. This creates a skeleton project that includes a sample process with an example of an action class. The sample process's jPDL code and image are depicted in figure 5.10.

As shown in figure 5.10, the jPDL XML code contains a reference to the generated sample action class called MessageActionHandler. This sample class, shown in listing 5.1, illustrates how property values can be injected into the action class, and in turn, stored within a process variable.

Figure 5.10. A jBPM sample process created automatically when a new process project is created<br></br> 

Example 5.1. Sample action class generated when new a process project is created

Since this class is defined as the implementation for an action (shown in figure 5.10), it must implement the ActionHandler

In some cases, you may find that you're repeating the same action class in multiple locations within your jPDL. For example, you may have an event that triggers an action class for event stream processing or BAM purposes. Rather than repeating that action definition, you can define it at the root level of the jPDL, and then reference it where needed by using the @ref-name attribute of the action element (in fact, you can reference it anywhere, even if it's within a child element elsewhere). Here's an example:

<action ref-name='shared-action'/>

shared-action is the assigned name of an action located elsewhere (or at the top level) of the jPDL. This points out the importance of using unique names for your actions.

When creating the new process project, the generated code will also contain a JUnit test class called SimpleProcessTest. This test class illustrates how to locally instantiate a jBPM instance, install a process, run the process, and step through it programmatically. Although limited in what it achieves, the sample process and code provide a useful template or starting point for developing your process. The generated action class created in listing 5.1 when the project was created only demonstrates one way to populate property values. As it turns out, there are four methods for populating property values from within the jPDL—let's take a look.

Action class property instantiation

An optional attribute of the action element not shown in figure 5.10 is called @config-type. This attribute is used to specify the method by which property values can be injected into an action class upon instantiation. If omitted, it defaults to a value of field, which is the method used in figure 5.10 and the corresponding action class shown in listing 5.1. Other @config-type values include bean, constructor, and configuration-property. In general, we don't see any real advantage to using anything other than the default field type, so we won't bother explaining the others (they're covered in the official documentation, if you're interested).

When specifying the field-style method of class instantiation, jBPM will attempt to automatically populate class member variables that match the XML element name passed as a child to the action element. We witnessed this earlier when we used a message element to pass a string that was populated into a similarly named class variable. When more complex data must be passed, choose one of two other approaches: use arbitrarily complex XML data that will then be converted into a dom4j Element, or use a <map><entry><key> XML format to pass the data as a Java Map object. All three approaches are shown here using a jPDL snippet, with message passed as a string, xml as a dom4j Element, and map as a Java Map object:

<action class="com.sample.action.MessageActionHandlerField"
 config-type="field" name="action">
<message>Going to the field state!</message>
<xml>
  <value1>Value 1</value1>
  <value2>Value 2</value2>
  <value3 value="Value 3"/>
</xml>
<map>
  <entry><key>key1</key> <value>Value 1</value></entry>
  <entry><key>key2</key> <value>Value 2</value></entry>
</map>
</action>

As we pointed out, the field style will attempt to match each top-level XML element with a corresponding Java member class. Here's a fragment of the MessageActionHandler-Field class used to receive the injected data (the complete class is in the example code):

public class MessageActionHandlerField implements ActionHandler {

  String message;
  Element xml; //org.dom4j.Element;
  Map<String, String> map;
  public void execute(ExecutionContext context) throws Exception {

   context.getContextInstance().setVariable("message", message);

   System.out.println("map is: " + map.size());
   System.out.println("xml is: " + xml.asXML());
  }
}

When the process is run and the node encountered, the three member variables are automatically populated with the corresponding data from the jPDL (map.size() is 2, xml.asXML() shows the XML nodeset). As you can see, this provides considerable flexibility for populating data on invocation. Let's now switch gears a bit and examine action expressions.

Using action expressions

One of the most interesting, but we believe underused, capabilities of the action element is the @expression attribute. This option enables you to invoke a method on a process variable (variables will be discussed in more detail in the next section). Thus, a process variable can not only be used to store process-related data, but can also be a complex Java object whose method is invoked with the @expression attribute specified. In that sense, process variables can encapsulate both data and function. This concept is most easily illustrated with an example. In listing 5.2 is a class that will be stored as a process variable within jBPM (notice that it's serializable for this reason).

Example 5.2. Java class used as a process variable object

Let's assume that a start task action is responsible for instantiating the SalaryObject as a process variable identified with a name of salary. Then, during the lifecycle of the process instance, the two methods will be called. The populateVars method

How will the two methods in listing 5.2 be invoked? Let's take a look at a sample process diagram (jPDL) that uses this object (see listing 5.3).

Example 5.3. jPDL demonstrating the action element's expression attribute

The highlighted code in listing 5.3 shows how the two methods are invoked using the @expression attribute. In the start-state node, the method populateVars is invoked on the object represented by salary in a process variable during the node-leave event

What's the implication of this capability? One clear benefit of this approach is that the process instance now has the encapsulated functionality within its unique context. By that, it becomes more immune to changes that may otherwise occur through the use of normal action classes. For example, if a regular action class is used but its functionality has changed through a new version, this would impact all in-progress instances that subsequently make calls to it. That may or may not be desirable. By using methods within a serialized Java class, this becomes less of a concern (though change management challenges always exist). It's also more consistent with the principles behind object-oriented programming, which is based on the notion of behavior and data being stored logically together.

What you may be left wondering in this example is how the salary process variable is initialized. Since it's a complex Java object (SalaryObject in listing 5.2), it can't be set via the standard property approach we discussed in section 5.5.1. We'll cover that shortly in section 5.7, but for now, suffice it to say that it's through the ContextInstance, which is used in listing 5.2 to retrieve a process variable.

The other remaining attributes that can be defined for an action element are @accept-propagated-events and @async. You'll learn more about both in the next chapter, which covers advanced features.

One of the main ways in which actions are used is in conjunction with events. Events, as the name implies, are triggered at various points in the process and reflect state changes that are occurring. The ability to invoke action code based on these events provides many interesting opportunities, ranging from activity monitoring and logging to asynchronous messaging. Let's investigate events further.

Using events for capturing lifecycle changes in a process

Events, as the name suggests, are triggers that are fired throughout the course of the lifecycle of a process instance. For example, when a token execution arrives in a given node, the node-enter event is fired. For each event that can occur, you can inject code via actions. This enables great flexibility for instituting custom behaviors within your process. Also, events represent a wonderful way to monitor the ongoing activity within a process. They can be used to monitor for any abnormalities or unusual trends that may be occurring, which we identified in chapter 1 as an important part of a SOA environment.

Table 5.4 categorizes the types of events that occur for the various objects (events aren't limited to nodes, but also affect transitions and the overall process).

Table 5.4. Object events<br></br> 

Event

Literal value

Object supported

[a]

EVENTTYPE_TRANSITION

transition

Transition, SuperState, Process

EVENTTYPE_BEFORE_SIGNAL

before-signal

Node[a]

EVENTTYPE_AFTER_SIGNAL

after-signal

Node

EVENTTYPE_PROCESS_START

process-start

Process

EVENTTYPE_PROCESS_END

process-end

Process

EVENTTYPE_NODE_ENTER

node-enter

Node, SuperState, Process

EVENTTYPE_NODE_LEAVE

node-leave

Node, SuperState, Process

EVENTTYPE_SUPERSTATE_ENTER

superstate-enter

SuperState, Transition, Process

EVENTTYPE_SUPERSTATE_LEAVE

superstate-leave

SuperState, Transition, Process

EVENTTYPE_SUBPROCESS_CREATED

subprocess-created

SuperState, Process

EVENTTYPE_SUBPROCESS_END

subprocess-end

SuperState, Process

EVENTTYPE_TASK_CREATE

task-create

SuperState, Process, Task

EVENTTYPE_TASK_ASSIGN

task-assign

SuperState, Process, Task

EVENTTYPE_TASK_START

task-start

SuperState, Process, Task

EVENTTYPE_TASK_END

task-end

SuperState, Process, Task

EVENTTYPE_TIMER

timer

SuperState, Process

[a] The Node nodetype in this table refers to it and all implementing types, such as State, Decision, Join, Fork, Task, and Mail.

In listing 5.3, you may recall that we showed how an event is defined. Besides the action element, the only other configurable option is the required @type attribute, which specifies one of the literal values shown in table 5.4 (node-leave in this case). While the use of events is fairly self-explanatory, one noteworthy and perhaps not obvious feature is that you can assign events at the process definition level. For example, consider the jPDL shown in figure 5.11.

Figure 5.11. An example of a root-level event definition<br></br> 

Notice the event element is defined directly as a child element of the top-level process-definition root node. The event's action class is triggered for every node-enter event that occurs within the process. In this example, that means the EventTest class is instantiated and its execute method called when both state1 and state2 are entered. Notice as well that an additional node-enter trigger was defined within the definition of the state1 node. Hence, they aren't mutually exclusive—both EventTest and EventTestNode are fired when state1 is entered. Using root-level events can be beneficial for scenarios such as BAM, where you want events fired for certain event types across the board (this obviously simplifies configuration, because otherwise you'd have to configure each node individually).

As we pointed out earlier, it's important to remember that events can't directly influence the execution of the process. For instance, an action defined within an event can't dictate which transition to follow. Events are best used for triggering notifications to external services, and they can indirectly influence execution through the setting of process variables (for example, a decision node may use a process variable to determine which transition path to follow).

Managing context using variables

In several of the examples so far, we've demonstrated how variables can be used within a process instance. Variable value types supported include String, Boolean, Float, Double, or Long. In addition, most serializable Java objects can be stored, and any classes that are persisted with Hibernate. If a class instance can't be persisted, an error will occur when you attempt to retrieve it (not when you attempt to persist it). The types we've used so far have been process variables, but there are two other types: local and transient. Let's take a look at each type.

PROCESS VARIABLES

In the logApprovedSalary method in listing 5.2, we used the ContextInstance to retrieve a process variable called approver:

ContextInstance contextInstance =
ExecutionContext.currentExecutionContext().getContextInstance();
approvedBy = (String) contextInstance.getVariable("approver");

Setting a process variable can be done in a similar fashion using the setVariable method. Here's how we might set the approver variable that we retrieved earlier:

ContextInstance contextInstance =
 ExecutionContext.currentExecutionContext().getContextInstance();
contextInstance.setVariable("approver", "john_doe_approver");

In this example, since we're using one of the supported simple Java types (String, Boolean, etc.), nothing additional had to be done to store the string john_doe_approver using the variable key approver. If you want to store a more complex Java object, the class must implement Serializable (the class in listing 5.2 was stored as a process variable, you may recall).

Note that, although we're calling this type of variable a process variable, that definition varies slightly from the official jBPM documentation. My definition of a process variable is one that's available throughout any node in the process instance. This is accomplished by using the setVariable and getVariable methods. setVariable accepts as its signature a supported data type or a serializable Object. The variable is automatically available within the root token of the process instance. Because it's at the root token level, it's visible to all child tokens that may be created throughout the life of the process instance (for example, in a fork scenario, the child tokens created would have visibility to that variable). You can, alternatively, make a variable local within a token execution, which is described next.

LOCAL VARIABLES

A local variable is one in which the variable is only accessible within the scope of a token. This can be accomplished by use of the ContextInstance's setVariableLocally method. As you may recall, a token represents a given path of execution. Using a local variable, you can define a variable that will be visible only within a specific token. Multiple tokens are most typically found in processes that include fork/join nodes, as shown in figure 5.12.

Figure 5.12. A token scope example when using fork and join nodes<br></br> 

In figure 5.12, you can assign a local variable to the /t1 path by using code such as

Token t1 = instance.findToken("/t1");
t1.instance.getContextInstance()
 .setVariableLocally("t1TokenVar", "true", t1);

In this example, the t1 child token that's created from the fork node is assigned to a variable called t1 (notice the token name is assigned, by default, to the transition name). Then the ContextInstance is used to set a local variable key called t1TokenVar to true for the token t1. Once it's assigned, you can retrieve the local variable by using the getVariableLocally method, which receives two parameters: variable key and token. If you attempt to fetch the value using the getVariable method that only accepts as its signature the variable key, null will be returned, because this is used for accessing process variables only. However, you can add the additional token parameter to the getVariable method to achieve the same result (that is, getVariable("t1TokenVar", t1)).

On what occasion would you want to use local variables? For highly complex processes, it might be beneficial to limit visibility and scope. In other circumstances, local variables might be used to initially populate task nodes, since it may not be necessary to expose them to the entire process. A close cousin of local variables are transient variables, which, as the name suggests, are limited to runtime actions and aren't persisted to the database with the process instance. For example, you could use them to populate static values that are required by downstream classes but that aren't relevant for keeping as part of the historical record.

The last topic we'll briefly touch on is converters. You may recall that many standard Java types can be stored automatically in jBPM. These include the types String, Date, Double, and Long. They can be saved as-is because converters have been defined that manage how these types can be converted into a jBPM VariableInstance class. You can provide your own converters to augment those that come out of the box. For example, you could create a converter that takes a Tuscany SDO object, marshals it into XML, and then stores the XML as a String when it's persisted. A reverse process could extract the XML and unmarshal it into an SDO object upon retrieval. The jBPM User Guide describes the process of creating converters in more detail.

Summary

One of the fundamental selling points of SOA is the promise that new solutions and processes can be developed quickly. By exposing reusable services through frameworks like SCA (covered in chapters 3 and 4), functionality that was locked into stand-alone applications or systems can be creatively and quickly leveraged and meshed into new business processes. BPM solutions are designed for creating such processes, and they represent a revolutionary alternative to conventional software development. Using BPM, subject matter experts can graphically craft new business processes and, when augmented by development, create executable models that can be deployed and managed. The upshot? Dramatically reduced development costs; better alignment between analysts and developers; and improved business agility.

JBoss jBPM is a mature BPM solution that offers many of the advanced features that have historically only been found in expensive commercial solutions. Capabilities include a graphical process designer, extensive task management features, a capable administrative console, and a powerful API. Perhaps more importantly, jBPM is highly extensible. You learned how custom functionality and code can be injected at nearly every point in the business process. What's more, this can be done in a transparent fashion that doesn't needlessly obfuscate the visual executable model.

One important feature of any BPM solution that we haven't covered in any great detail yet is tasks. I've reserved that subject for the next chapter, so turn the page.