Raible's Wiki

Raible Designs
Wiki Home
News
Recent Changes

AppFuse

Homepage
  - Korean
  - Chinese
  - Italian
  - Japanese

QuickStart Guide
  - Chinese
  - French
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish
  - Japanese

User Guide
  - Korean
  - Chinese

Tutorials
  - Chinese
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish

FAQ
  - Korean

Latest Downloads

Other Applications

Struts Resume
Security Example
Struts Menu

Set your name in
UserPreferences


Referenced by
AmeerAhmed
Articles
IntegratingJBPMIntoA...
IntegratingJBPMIntoA...




JSPWiki v2.2.33

[RSS]


Hide Menu

IntegratingJBPMIntoAppFuse


This is version 1. It is not the current version, and thus it cannot be edited.
[Back to current version]   [Restore this version]


Part I: Integrating jBPM into AppFuse - A HowTo for integrating jBPM Support (via Spring-Modules) into AppFuse.

About this tutorial

This tutorial will show you how to configure jBPM 3.1 into AppFuse. It will also show you how to create process definitions and use them in the appfuse application context.

You will use the jbpm-db package to create the jbpm tables in your database for workflow persistence and will author process definitions in Eclispe. And finally you will write some JUnit classes to test if the integration is working.

AppFuse uses Hibernate for its default persistence layer. jBPM also uses Hibernate for its persistence layer. It can be configured to either use the same session factory as Apppfuse or use its own dedicated session factory.

I use Oracle 10g for persistence, please see RunningOnOracle to configure appfuse for Oracle.

Font Conventions (work in progress)

Literal strings intended to be executed at the command prompt look like this: ant test-all.
References to files, directories and packages which exist in your source tree: build.xml.
And suggestions for how to do stuff in the "Real World" are in blue italics.

Table of Contents

  • [1] Download jBPM package and install jBPM database
  • [2] Add classpath references to the appfuse build process
  • [3] Download and configure Spring Modules for jBPM
  • [4] Test basic configuration

[#1] Download jBPM package and install jBPM database

Download the JBoss jBPM Starters Kit. Although you won't need all of them for integration, it is a good idea to browse through the sample code once you have the simple setup working. For this tutorial, I used v. 3.1.2.

Unzip them into a temporary folder. jBPM uses its own database, before you proceed any further you have to decide whether you will use a dedicated databse for jBPM or use the same database as AppFuse. To keep things simple we will use a dedicated db for jBPM. The following steps will create sql scripts to create/drop jBPM schema.

Create a new user in oracle with the username/pass as jbpm/jbpm. Copy file ojdbc14.jar into jbpm-db/oracle/lib/ directory (if you are not using oracle, copy the neccessary driver to jbpm-db/**database_of_choice**/lib/ directory). Modify hibernate.properties located in jbpm-db/oracle/ directory to reflect your connection information:

hibernate.dialect=org.hibernate.dialect.Oracle9Dialect
# for Oracle 8 compatibility use
#hibernate.dialect=org.hibernate.dialect.OracleDialect

hibernate.connection.driver_class=oracle.jdbc.driver.OracleDriver
hibernate.connection.url=jdbc:oracle:thin:@localhost:1521:AMEER
hibernate.connection.username=jbpm
hibernate.connection.password=jbpm

hibernate.show_sql=true
hibernate.c3p0.min_size=1
hibernate.c3p0.max_size=3

hibernate.query.substitutions=true 1, false 0

You are now ready to generate the scripts. Execute ant oracle.scripts this will generate the scripts in jbpm-db/build/oracle/scripts/ directory. Execute oracle.create.sql in your favorite database tool, I use TOAD, and voila! jBPM schema should be created and ready for use.

[#2] Add classpath references to the appfuse build process

Create the following directories under your appfuse's lib folder: appfuse/lib/jbpm-3.1.2 appfuse/lib/springmodules-0.5

Copy jbpm/lib/jboss/bsh-1.3.0.jar jbpm/build/jbpm-3.1.2.jar jbpm/build/jbpm.sar.cfg.jar jbpm/build/jbpm-identity-3.1.2.jar to appfuse/lib/jbpm-3.1.2 folder.

Modify appfuse/lib/lib.properties file to include:

jbpm.version=3.1.2
springmodules.version=0.5

jbpm.dir=${lib.dir}/jbpm-${jbpm.version}
springmodules.dir=${lib.dir}/springmodules-${springmodules.version}

Modify appfuse/properties.xml to include the classpath references:

    <path id="dao.compile.classpath">
        <path refid="hibernate.classpath"/>
        <pathelement location="${commons-lang.jar}"/>
        <pathelement location="${commons-logging.jar}"/>
        <pathelement location="${commons-collections.jar}"/>
        <fileset dir="${spring.dir}" includes="*.jar"/>
        <pathelement location="${lucene.jar}"/>
        <fileset dir="${jbpm.dir}" includes="*.jar"/>
        <fileset dir="${springmodules.dir}" includes="*.jar"/>
    </path>

[#3] Download and configure Spring Modules for jBPM

Download and unzip Spring Modules v 0.5. Copy spring-modules-jbpm31.jar to appfuse/lib/springmodules-0.5 directory. The following text is from the default jbpm.cfg.xml configuration file, this controls what services will be used by jbpm, you can comment out any service which you don't want. We will be working with the "persistence", "scheduler" and "logging" services.
<jbpm-configuration>

  <jbpm-context>
    <service name="persistence" factory="org.jbpm.persistence.db.DbPersistenceServiceFactory" />
    <service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />
    <service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
    <service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
    <service name="authentication" factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
  </jbpm-context>
  
  <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
  <string name="resource.business.calendar" value="org/jbpm/calendar/jbpm.business.calendar.properties" />
  <string name="resource.default.modules" value="org/jbpm/graph/def/jbpm.default.modules.properties" />
  <string name="resource.converter" value="org/jbpm/db/hibernate/jbpm.converter.properties" />
  <string name="resource.action.types" value="org/jbpm/graph/action/action.types.xml" />
  <string name="resource.node.types" value="org/jbpm/graph/node/node.types.xml" />
  <string name="resource.parsers" value="org/jbpm/jpdl/par/jbpm.parsers.xml" />
  <string name="resource.varmapping" value="org/jbpm/context/exe/jbpm.varmapping.xml" />

  <int name="jbpm.byte.block.size" value="1024" singleton="true" />
  <bean name="jbpm.task.instance.factory" class="org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl" singleton="true" />
  <bean name="jbpm.variable.resolver" class="org.jbpm.jpdl.el.impl.JbpmVariableResolver" singleton="true" />
  <long name="jbpm.msg.wait.timout" value="5000" singleton="true" />

</jbpm-configuration>
The file should be saved in appfuse/src/dao/org/appfuse/jbpm/ directory (or you can save it in a directory of your choice, however, you would have to update the reference in the applicationContext-hibernate.xml file).

At this point we want to ensure that all our previous steps are working so it is a good idea to test the configuration. We will dive into writing more complex definitions later on(using the process design tool in eclipse), for now write a simple process definition as:

<process-definition name="testprocess.xml">
  <start-state>
    <transition to='s' />
  </start-state>
  <state name='s'>
    <transition to='end'/>
  </state>
  <end-state name='end' />
</process-definition>
and save it as testprocess.xml into appfuse/src/dao/org/appfuse/jbpm/process directory. The last piece in this puzzle is to configure the appfuse/src/dao/org/appfuse/dao/hibernate/applicationContext-hibernate.xml file. Add the following information to complete the basic integration:
    <!-- JBPM Datasource -->
    <bean id="jbpmDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName"><value>oracle.jdbc.driver.OracleDriver</value></property>
        <property name="url"><value>jdbc:oracle:thin:@localhost:1521:AMEER</value></property>
        <property name="username"><value>jbpm</value></property>
        <property name="password"><value>jbpm</value></property>
    </bean>

    <!-- JBPM Hibernate SessionFactory -->
    <bean id="jbpmSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="dataSource" ref="jbpmDataSource"/>
        <property name="mappingResources">
            <list>
                <value>org/jbpm/graph/action/Script.hbm.xml</value>
		<value>org/jbpm/identity/User.hbm.xml</value>
		<value>org/jbpm/identity/Group.hbm.xml</value>
		<value>org/jbpm/identity/Membership.hbm.xml</value>
		<value>org/jbpm/db/hibernate.queries.hbm.xml</value>
		<value>org/jbpm/graph/def/ProcessDefinition.hbm.xml</value>
		<value>org/jbpm/graph/def/Node.hbm.xml</value>
		<value>org/jbpm/graph/def/Transition.hbm.xml</value>
		<value>org/jbpm/graph/def/Event.hbm.xml</value>
		<value>org/jbpm/graph/def/Action.hbm.xml</value>
		<value>org/jbpm/graph/def/SuperState.hbm.xml</value>
		<value>org/jbpm/graph/def/ExceptionHandler.hbm.xml</value>
		<value>org/jbpm/instantiation/Delegation.hbm.xml</value>
		<value>org/jbpm/graph/node/StartState.hbm.xml</value>
		<value>org/jbpm/graph/node/EndState.hbm.xml</value>
		<value>org/jbpm/graph/node/ProcessState.hbm.xml</value>
		<value>org/jbpm/graph/node/Decision.hbm.xml</value>
		<value>org/jbpm/graph/node/Fork.hbm.xml</value>
		<value>org/jbpm/graph/node/Join.hbm.xml</value>
		<value>org/jbpm/graph/node/State.hbm.xml</value>
		<value>org/jbpm/graph/node/TaskNode.hbm.xml</value>
		<value>org/jbpm/context/def/ContextDefinition.hbm.xml</value>
		<value>org/jbpm/context/def/VariableAccess.hbm.xml</value>
		<value>org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml</value>
		<value>org/jbpm/taskmgmt/def/Swimlane.hbm.xml</value>
		<value>org/jbpm/taskmgmt/def/Task.hbm.xml</value>
		<value>org/jbpm/taskmgmt/def/TaskController.hbm.xml</value>
		<value>org/jbpm/module/def/ModuleDefinition.hbm.xml</value>
		<value>org/jbpm/bytes/ByteArray.hbm.xml</value>
		<value>org/jbpm/file/def/FileDefinition.hbm.xml</value>
		<value>org/jbpm/scheduler/def/CreateTimerAction.hbm.xml</value>
		<value>org/jbpm/scheduler/def/CancelTimerAction.hbm.xml</value>
		<value>org/jbpm/graph/exe/Comment.hbm.xml</value>
		<value>org/jbpm/graph/exe/ProcessInstance.hbm.xml</value>
		<value>org/jbpm/graph/exe/Token.hbm.xml</value>
		<value>org/jbpm/graph/exe/RuntimeAction.hbm.xml</value>
		<value>org/jbpm/module/exe/ModuleInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/ContextInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/TokenVariableMap.hbm.xml</value>
		<value>org/jbpm/context/exe/VariableInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml</value>
		<value>org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml</value>
		<value>org/jbpm/msg/Message.hbm.xml</value>
		<value>org/jbpm/msg/db/TextMessage.hbm.xml</value>
		<value>org/jbpm/command/ExecuteActionCommand.hbm.xml</value>
		<value>org/jbpm/command/ExecuteNodeCommand.hbm.xml</value>
		<value>org/jbpm/command/SignalCommand.hbm.xml</value>
		<value>org/jbpm/command/TaskInstanceEndCommand.hbm.xml</value>
		<value>org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml</value>
		<value>org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml</value>
		<value>org/jbpm/taskmgmt/exe/PooledActor.hbm.xml</value>
		<value>org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml</value>
		<value>org/jbpm/scheduler/exe/Timer.hbm.xml</value>
		<value>org/jbpm/logging/log/ProcessLog.hbm.xml</value>
		<value>org/jbpm/logging/log/MessageLog.hbm.xml</value>
		<value>org/jbpm/logging/log/CompositeLog.hbm.xml</value>
		<value>org/jbpm/graph/log/ActionLog.hbm.xml</value>
		<value>org/jbpm/graph/log/NodeLog.hbm.xml</value>
		<value>org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml</value>
		<value>org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml</value>
		<value>org/jbpm/graph/log/SignalLog.hbm.xml</value>
		<value>org/jbpm/graph/log/TokenCreateLog.hbm.xml</value>
		<value>org/jbpm/graph/log/TokenEndLog.hbm.xml</value>
		<value>org/jbpm/graph/log/TransitionLog.hbm.xml</value>
		<value>org/jbpm/context/log/VariableLog.hbm.xml</value>
		<value>org/jbpm/context/log/VariableCreateLog.hbm.xml</value>
		<value>org/jbpm/context/log/VariableDeleteLog.hbm.xml</value>
		<value>org/jbpm/context/log/VariableUpdateLog.hbm.xml</value>
		<value>org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml</value>
		<value>org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml</value>
		<value>org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml</value>
		<value>org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml</value>
		<value>org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml</value>
		<value>org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml</value>
		<value>org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml</value>
		<value>org/jbpm/taskmgmt/log/TaskLog.hbm.xml</value>
		<value>org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml</value>
		<value>org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml</value>
		<value>org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml</value>
		<value>org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml</value>
		<value>org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml</value>
		<value>org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml</value>
			</list>
        </property>
        <!-- The property below is commented out b/c it doesn't work when run via
             Ant in Eclipse.  It works fine for individual JUnit tests and in IDEA ??
        <property name="mappingJarLocations">
            <list><value>file:dist/appfuse-dao.jar</value></list>
        </property>
        -->
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
                <!-- Create/update the database tables automatically when the JVM starts up
                <prop key="hibernate.hbm2ddl.auto">update</prop> -->
                <!-- Turn batching off for better error messages under PostgreSQL 
                <prop key="hibernate.jdbc.batch_size">0</prop> -->
            </props>
        </property>
    </bean>

    <!-- jBPM Configuration -->
    <bean id="jbpmConfig" class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
	<!-- pass in existing sessionFactory -->
		<property name="sessionFactory" ref="jbpmSessionFactory"/>
		<property name="configuration" value="org/appfuse/jbpm/jbpm.cfg.xml"/>
		<property name="processDefinitions">
			<list>
				<ref local="testProcess"/>
			</list>
		</property>
		<property name="createSchema" value="false"/>
	</bean>

	<bean id="testProcess" class="org.springmodules.workflow.jbpm31.definition.ProcessDefinitionFactoryBean">
		<property name="definitionLocation" value="org/appfuse/jbpm/process/testprocess.xml"/>
	</bean>

Apparently there is another way to reference the mapping files(.hbm.xml) using "org/jbpm/**/**/**.hbm.xml" syntax, however, i couldn't get it to work.

[#4] Test basic configuration

Now that you have all the pieces setup, its time to write a little test case which will do the following:


public class ExampleProcessTest extends BaseDaoTestCase {

    JbpmConfiguration config = null;
    JbpmContext context = null;
    ProcessDefinition definition = null;
    
  protected void onSetUpBeforeTransaction() throws Exception {
  config = (JbpmConfigurationapplicationContext.getBean("jbpmConfig");
        context = config.createJbpmContext();
      definition = context.getGraphSession().findLatestProcessDefinition("testProcess");
  }

  public void testProcessDefinition() throws Exception {

      ProcessDefinition def = context.getGraphSession().findLatestProcessDefinition("testProcess");
      
      assertNotNull("Definition should not be null", def);
  }

  public void testStartState() throws Exception {
    
        // The next line creates one execution of the process definition.
        // After construction, the process execution has one main path
        // of execution (=the root token) that is positioned in the
        // start-state.
        ProcessInstance inst = definition.createProcessInstance();
        
        // Also after construction, the main path of execution is positioned
        // in the start-state of the process definition.
        assertEquals("Instance is in start state", definition.getStartState(), inst.getRootToken().getNode());
  
  }

  public void testTransitions() throws Exception {
    
        ProcessInstance inst = definition.createProcessInstance();

        // Let's start the process execution, leaving the start-state 
        // over its default transition.
        inst.signal();
        // The signal method will block until the process execution 
        // enters a wait state.

        // The process execution will have entered the first wait state
        // in state 's'. So the main path of execution is now 
        // positioned in state 's'
        assertEquals("Instance is in 's' state", inst.getRootToken().getNode().getName()"s");

        // Let's send another signal.  This will resume execution by 
        // leaving the state 's' over its default transition.
        inst.signal();
        // Now the signal method returned because the process instance 
        // has arrived in the end-state.
  
        assertEquals("Instance is in 'end' state", inst.getRootToken().getNode().getName()"end");
  }

}

A few words regarding the above code. We grab the config bean(previously configured) and use it to initialize a context. Using jBPM context we can make use of all the services defined in the jbpm.cfg file. GraphSession is used to load the latest instance of process definition. by executing an hql query (defined in org/jbpm/db/hibernate.queries.hbm.xml). Once we have the defintion we can create instances from that workflow and perform transition, tasks, adding variables, scheduling tasks etc (more on this later). So now its time to run this test case. The first time you fire up this test case, jbpm will initialize and deploy your process defintion to its database. Infact everytime you start it up it deploys a new version of the defintion.

In the next installment we will dive into creating more real-life like (tasks, variables, scheduling, custom queries) process defintions using the graphical plugin tool for Eclipse. We will also cover how to avoid reploying process defintions across server restarts, and finally we will inject some spring beans into our jBPM actions to demonstrate what a poweful and easy workflow mechanism jBPM really is!

Part II: Integrating jBPM into AppFuse - Part Deux - A HowTo for enhancing jBPM Support into AppFuse.



Go to top   More info...   Attach file...
This particular version was published on 06-Nov-2006 13:52:57 MST by unknown.