Transaction Workflow with jBPM

waisttherapeuticΛογισμικό & κατασκευή λογ/κού

4 Νοε 2013 (πριν από 3 χρόνια και 10 μήνες)

107 εμφανίσεις

Transaction

Workflow with jBPM

Doug Smith, CIS 764, Fall Semester 2007


Purpose

The purpose of this tutorial is introduce jBPM, and show
it
s use for
transaction

workflow,
using the session façade pattern to demonstrate how jBPM can fit into J2EE applicatio
ns.


jBPM

JBoss jBPM is a workflow engine that can be used in settings from standalone Java
programs coordinating tasks to full large scale application server deployments involving
business process implementations i
nvolving long running processes,
system o
rchestration
and human interaction.


jBPM provides a graphical process designer,

an execution engine, an administrative
console application, and components for identity management, email integration, and
database integration, among others.


At its core, j
BPM provides a facility for a graph oriented programming abstraction. Many
computing problems can be modeled as the execution of a directed graph, such as
business process management, web page flow, service orchestration, and so on.


For an in
-
depth treatm
ent of graph oriented programming with jBPM, refer to the Graph
Oriented Programming section of the
jBPM user guide:
http://docs.jboss.com/jbpm/v3/userguide/graphoriented
programming.html


An Example Scenario

For the purposes of this tutorial, we will use a simplified version of enrolling in a
university course as an example.
Figure
1

shows a BPMN diagram of the process.
Basically, when a student
requests enrollment for a course, if the course is not full, the
student is added to the course, and an entry is made to their transcript.



Figure
1

Enrollment Process


This is a very simplified view of the process, but it should

suffice for the purposes of this
tutorial.

The Session Façade Pattern

One of the patterns we’ve seen in CIS 764 is the session façade pattern. The typical use
of the session façade pattern in J2EE applications is to provide a transaction oriented
interfac
e to clients, scripting the necessary interactions between EJBs needed to provide a
service. It is also used to reduce round
-
trips between the client and server required for a
transaction, as well as to shield the client from the complexity and baggage ass
ociated
with using EJBs.


One of the most important components of the session façade pattern, however, is to not
make the client responsible for implementing the business logic associated with the
transaction. A session façade provides a service
-
oriented v
iew of a transaction: the client
can consume the service without needing to understand how it is implemented.


Based on the above scenario, the following diagram shows
the classes that can be used to
implement the enrollment process.



Figure
2

Class model for Enrollment service


The following sequence diagram shows how the session façade can coordinate the
interactions with the entity classe
s to implement the enrollment process.



This pattern has worked well in practice, but in some cases an alternative approach may
be warranted:



If the workflow in th
e session façade represents a business process, as opposed to
domain logic in an application, class and sequence diagrams are not a good
communication vehicle when interacting with business analysts and other
stakeholders from the business community.



If th
e process is subject to frequent change, implementation in a session façade
may not provide the right level of agility to deal with change.



Processes that are long duration and combine tasks performed by humans with
tasks performed by systems require a dif
ferent solution paradigm, and in the
interest of consistency it may make sense to use the same technology for all
process execution, both short lived straight through processing and longer
duration processes.


Using jBPM in the Session Façade

jBPM provides

a way to implement the workflow shown in the scenario above, using a
technology that promotes agility by putting the business logic in an XML file, executed
by infrastructure that can also host long duration processes that involve humans, too. This
demons
tration will
implement the enrollment process using jBPM. As the focus is on the
use of jBPM, plain old Java objects with stubbed methods will be used as proxies for
actual entities.


Tools

To complete this tutorial, you will need:



The jBPM jPDL suite, ava
ilable from
http://labs.jboss.com/downloading/?projectId=jbossjbpm&url=http://downloads.s
ourceforge.net/jbpm/jbpm
-
jpd
l
-
suite
-
3.2.2.zip



Eclipse 3.3, available from
http://repository.jboss.com/eclipse/sdk/3.3/eclipse
-
SDK
-
3.3
-
win32.zip



The AspectJ development tools (AJDT 1.5 for eclipse
3.3) , available from
http://www.eclipse.org/ajdt/downloads/



Apache ant, available from
http://ant.apache.org/


The tutorial assumes you have a JDK installed


the tutorial was done with a 1.5 JDK.
The tutorial was also done on Windows, so installation procedures may differ for other
platforms.


To set up the tools, first install ant. Once ant is installed, unzip the jBPM jPDL suite.
Next, copy
the eclipse 3.3 SD
K zip file you downloaded
into the jBPM jPDL suite’s
designer directory
.


Edit the build.properties file in the designer directory to set the path to the eclipse zip file
as:


eclipse.local.path=.


Remove or comment out any other definitions for elipse.loc
al.path. Save the file, then run
ant, this will install eclipse. Note the installation process is backwards from most eclipse
plugins and environments: jBPM provides an eclipse directory with only their add ons,
then have you unzip eclipse into their direc
tory. If you delete their eclipse directory, you
will lose the jBPM functionality.


After the ant script finishes, install the AspectJ development tools as an eclipse plugin:
simply unzip the AJDT zip file into the root eclipse directory in the designer fo
lder. Note
that AspectJ is used to print entry into the Java stubs provided for the tutorial: if you’d
like to avoid installing AspectJ, simply added the appropriate logging or
System.out.println statements to the stubs to view execution
, and adjust the in
structions
below
.


Create the Project

Once the tools are installed, start the jBPM designer by double clicking designer.bat. This
will launch eclipse. Once eclipse is launched, create a new jBPM Project.

1.

Select File > New Project…

2.

In the New Project dialog
, expand JBoss jBPM and select Process Project.

3.

Name the project Tutorial, the select Finish.


This will create a project configured with everything needed to execute a jBPM process,
including a sample process and JUnit test. For this tutorial, we will sim
ply add some
additional files to the project.


Add the Sample Code

Grab the sample code from the link in the references section, and unzip it in a temporary
location on your hard drive.


Before adding the code to the project, convert the project to an Aspe
ctJ project:

1.

Select the project in the package explorer.

2.

Right click, and select Convert to AspectJ Project from the AspectJ Tools context
menu.


Next, create folders for the example code:

1.

Select src/main/java in the package explorer.

2.

Right click and selec
t New > Folder.

3.

Create a folder named aspects.

4.

Repeat this process, creating folders named enroll and run.


Finally, drag and drop the source files from the aspects, enroll, and run folders on your
hard drive into the corresponding folders in the eclipse p
ackage explorer.

Implement the Sample Process

At this point, it is time to outline the process shown in
Figure
1
.

1.

In the package explorer, select the src/main/jpdl folder.

2.

Right click and select New > Other…

3.

Expand JBoss jBPM and
select Process Definition.

4.

Click next, and name the process enrollment1.

5.

Click the Finish button.


This will create a folder named enrollment1, and will open the processdefinition.xml file
in Diagram mode. At this point, you can draw the process diagram.
It should look
something like this:




Note this diagram is much closer to something you could show to a business analyst than
a sequence diagram


the essential pieces of the process are shown, with the details
needed to implement the process to be embed
ded in its XML representation (soon to be
shown below).


After drawing the diagram, click the source tab. The XML representation of the process
should look like the following. Note that you may have to update the namespace for the
document to be “
urn:jbpm.
org:jpdl
-
3.2
”:


<?xml version="1.0" encoding="UTF
-
8"?>

<
process
-
definition

xmlns
="
urn:jbpm.org:jpdl
-
3.2
"

name
="
enrollment1
">


<
start
-
state

name
="
initiate enrollment
">



<
transition

to
="
check current enrollment
">



</
transition
>


</
start
-
state
>


<
state

name
="
check current enrollment
">



<
transition

to
="
Is space available?
">



</
transition
>


</
state
>


<
decision

name
="
Is space available?
">



<
transition

to
="
end
"

name
="
No
">



</
transition
>



<
transition

to
="
add student to course
"

name
="
yes
">



</
transition
>


</
decision
>


<
state

name
="
add student to course
">



<
transition

to
="
add course to transcript
">



</
transition
>


</
state
>


<
state

name
="
add course to transcript
">



<
transition

to
="
end
">



</
transition
>


</
state
>


<
end
-
state

name
="
end
"/>

</
process
-
definition
>


At this point the states and transitions have been established, but the associated Java
interactions and decision logic needs to be added.


To add calls to Java objects in the process definition, jBPM uses the beanshell scripting
language and engine to
add Java calls to the process execution. For decision logic,

according to the JPDL part of the user guide,

jBPM uses
a “
JSP/JSF EL like expression
language” for expressing transition guards. I have not found a reference to the exact
language, but it seems
to align with JSP/JSF EL, through it is embedded slightly
differently, e.g. using #{}


The following XML adds the object scripting and condition expressions needed to
complete the process.


<?
xml
version
=
"1.0"
encoding
=
"UTF
-
8"
?>


<
process
-
definition
xmlns
=
"urn:jbpm.org:jpdl
-
3.2"
name
=
"enrollment1"
>

<
start
-
state
name
=
"initiate enrollment"
>



<
transition
to
=
"check current enrollment"
>




<
script
>





<
variable
name
=
"semester"
/>





<
variable
name
=
"courseId"
/>





<
variable
name
=
"theCourse"
/>





<
expression
>






import enroll.*;






CourseCatalog catalog = new
CourseCatalog();






theCourse = catalog.getCourse(courseId,
semester);





</
expression
>




</
script
>



</
transition
>


</
start
-
state
>


<
state
name
=
"check current enrollment"
>



<
transition
to
=
"Is s
pace available?"
>




<
script
>





<
variable
name
=
"spaceAvailable"
/>





<
variable
name
=
"theCourse"
/>





<
expression
>






spaceAvailable =
theCourse.spaceAvailable();





</
expression
>




</
script
>



</
transition
>


</
state
>


<
decision
name
=
"Is space avail
able?"
>



<
transition
to
=
"end"
name
=
"No"
>




<
condition
expression
=
"#{spaceAvailable == false}"
/>



</
transition
>



<
transition
to
=
"add student to course"
name
=
"yes"
>




<
condition
expression
=
"#{spaceAvailable == true}"
/>



</
transition
>


</
decision
>


<
sta
te
name
=
"add student to course"
>



<
transition
to
=
"add course to transcript"
>




<
script
>





<
variable
name
=
"theStudent"
/>





<
variable
name
=
"theCourse"
/>





<
variable
name
=
"studentId"
/>





<
expression
>






import enroll.*;






Registrar registrar =
new Registrar();






theStudent =
registrar.getStudent(studentId);






theCourse.addStudent(theStudent);





</
expression
>




</
script
>



</
transition
>


</
state
>


<
state
name
=
"add course to transcript"
>



<
transition
to
=
"end"
>




<
script
>





<
variable
n
ame
=
"theStudent"
/>





<
variable
name
=
"courseId"
/>





<
variable
name
=
"semester"
/>





<
expression
>






import enroll.*;






Transcript transcript =
theStudent.getTranscript();






transcript.addCourse(courseId, semester);





</
expression
>




</
script
>



</
transition
>


</
state
>


<
end
-
state
name
=
"end"
/>

</
process
-
definition
>


Note that variables are introduced into scripts using the variable tag. Variables can either
be variables already present in the process context, or new variables to be introduced i
n
the process context.


Next, examine the RunEnrollment1 class:


package run;


import org.jbpm.context.exe.ContextInstance;

import org.jbpm.graph.def.ProcessDefinition;

import org.jbpm.graph.exe.ProcessInstance;


public class RunEnrollment1 {


public stati
c void main(String[] args) {



try {




//Instantiate a process instance.




ProcessDefinition processDefinition =
ProcessDefinition.parseXmlResource("enrollment1/processdefinition.xml")
;




ProcessInstance instance = new
ProcessInstance(processDefinition)
;








//Set the process data




ContextInstance ctx = instance.getContextInstance();




ctx.setVariable("studentId", "123");




ctx.setVariable("courseId", "CIS764");




ctx.setVariable("semester", "Fall 2007");








//Execute the process




while(!in
stance.hasEnded()) {





instance.signal();




}



} catch(Throwable t) {




t.printStackTrace();



}


}

}


Some things to note about the process:

1.

The process variables are provided to the process instance via the ContextInstance
class.

2.

The instance is sig
naled to move it between states.

3.

Obviously a real implementation would have to
check the process state,
distinguish a fully completed process from an aborted process, and handle errors
and exceptions.



This is a simple program that executes the process w
e just defined. To see the process
run:

1.

Edit log4j.properties in src/main/config, setting the jBPM root logger level to
INFO: change
log4j.logger.org.jbpm=DEBUG

to log4j.logger.org.jbpm=INFO.

2.

Select RunEnrollment1 (in the src/main/java/run folder) in the p
ackage explorer.

3.

Right click, then select Run As > Java Application


Your output should look like:


Entering run.RunEnrollment1.main

11:22:28,968 [main] INFO JbpmConfiguration : using jbpm configuration
resource 'jbpm.cfg.xml'

11:22:29,078 [main] INFO St
aleObjectLogConfigurer : stale object
exceptions will be hidden from logging

Entering enroll.CourseCatalog.getCourse

Entering enroll.Course.spaceAvailable

Entering enroll.Registrar.getStudent

Entering enroll.Student.setStudentId

Entering enroll.Course.addS
tudent

Entering enroll.Student.getTranscript

Entering enroll.Transcript.addCourse


At this point we’ve shown how object interactions to provide a client transaction can be
executed using a jBPM process and some simple object scripting.


Modifying the Proce
ss

To demonstrate how easy it is to modify a jBPM process, consider the following update
to the enrollment process.



Figure
3

Updated Enrollment Process


We will start by creating an enrollment2 process


follow the same procedur
e used to
create enrollment1, except name it enrollment2. Copy the XML from enrollment1’s
processdefinition.xml file into enrollment2’s processdefinition.xml file, then change the
process name to enrollment2 in the enrollment2 file.


In modifying enrollmen
t1 (with the modified process captured in enrollment2), note that:



A check of the student’s standing with the bursar has been added, along with a
decision to continue or end the process based on the student’s standing.



A check of the student’s transcript a
gainst the prerequisites of the course they
enrolling in have been added, along with a decision to continue or end the process
based on the student’s standing.


We will make the modifications directly to the XML.


First, add the new states and decisions:


<state name="check bursar status">



<transition to="In good standing?">



</transition>


</state>




<decision name="In good standing?">



<transition to="end" name="No">



</transition>



<transition to="check prerequisites" name="Yes">



</transition>


</decision>




<state name="check prerequisites">



<transition to="Prequisites satisfied?">



</transition>


</state>




<decision name="Prequisites satisfied?">



<transition to="end" name="No">



</transition>



<transition to="check current enrollment"

name="Yes">



</transition>


</decision>


Update the 'next' state in the start
-
state, and remove the transition script :


<start
-
state name="initiate enrollment">



<transition to="check bursar status">







</transition>


</start
-
state>


Add code to get

bursar status:


<state name="check bursar status">



<transition to="In good standing?">




<script>





<variable name="studentId"/>





<variable name="bursarStatus"/>





<expression>






import enroll.*;






Bursar bursar = new Bursar();






bursar
Status =
bursar.inGoodStanding(studentId);





</expression>




</script>



</transition>


</state>


Add conditions to decision transitions:


<decision name="In good standing?">



<transition to="end" name="No">




<condition expression="#{bursarStatus ==
false}"/>



</transition>



<transition to="check prerequisites" name="Yes">




<condition expression="#{bursarStatus == true}"/>



</transition>


</decision>


Similarly, add scripts and conditions for the prereq check:



<state name="check prerequisites">



<transition to="Prequisites satisfied?">




<script>





<variable name="courseId"/>





<variable name="studentId"/>





<variable name="theStudent"/>





<variable name="prerequisitesOk"/>





<expression>






import enroll.*;






Registrar registra
r = new Registrar();






theStudent =
registrar.getStudent(studentId);






Transcript transcript =
theStudent.getTranscript();






CourseCatalog catalog = new
CourseCatalog();






prerequisitesOk =
catalog.satisfiedPrerequisites(courseId, transcript);





</expression>




</script>



</transition>


</state>




<decision name="Prequisites satisfied?">



<transition to="end" name="No">




<condition expression="#{prerequisitesOk == false}"/>



</transition>



<transition to="check current enrollment" name
="Yes">




<condition expression="#{prerequisitesOk == true}"/>



</transition>


</decision>


Finally, modify the check current enrollment transition script:


<state name="check current enrollment">



<transition to="Is space available?">




<script>





<
variable name="spaceAvailable"/>





<variable name="theCourse"/>





<variable name="courseId"/>





<variable name="semester"/>





<expression>






import enroll.*;






CourseCatalog catalog = new
CourseCatalog();






theCourse = catalog.getCourse(co
urseId,
semester);






spaceAvailable =
theCourse.spaceAvailable();





</expression>




</script>



</transition>


</state>


You can run the process as shown above, using RunProcess2 in the run package. Your
output should look like:


Entering run.RunEnro
llment2.main

12:07:29,751 [main] INFO JbpmConfiguration : using jbpm configuration
resource 'jbpm.cfg.xml'

12:07:29,851 [main] INFO StaleObjectLogConfigurer : stale object
exceptions will be hidden from logging

Entering enroll.Bursar.inGoodStanding

Enter
ing enroll.Registrar.getStudent

Entering enroll.Student.setStudentId

Entering enroll.Student.getTranscript

Entering enroll.CourseCatalog.satisfiedPrerequisites

Entering enroll.CourseCatalog.getCourse

Entering enroll.Course.spaceAvailable

Entering enroll.Re
gistrar.getStudent

Entering enroll.Student.setStudentId

Entering enroll.Course.addStudent

Entering enroll.Student.getTranscript

Entering enroll.Transcript.addCourse


Conclusions

jBPM provides a nice mechanism for implementing process logic and transaction
workflows in J2EE applications. It’s combination of graphical process representation and
XML scripting provide a nice separation of concerns for collaboration between business
analysts and developers.

Resources

jBPM Community Web Site:
http://labs.jboss.com/jbossjbpm/


jBPM User Guide:
http://docs.jboss.com/jbpm/v3/userguide/


Session Façade Pattern:
http://java.sun.com/blueprints/corej2eepatterns/Patterns/SessionFacade.html


TIBCO Business Studio

(free BPMN modeling and simulation tool)
:
h
ttp://www.tibco.com/devnet/business_studio/default.jsp


Beanshell Scripting Language:
http://www.beanshell.org/


Example project export (with source code):
http://people.cis.ksu.edu/~dougs/cis764homework/jbpm_tutorial_srcs.zip