In the previous post I spoke about how create a business model process using jBPM engine in Eclipse.
In this post I’ll show you how integrate the business process with Mule flow.
You need some files to complete the configuration of our Business process. One of these files is jbpm.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <jbpm-configuration> <import resource="jbpm.default.cfg.xml" /> <import resource="jbpm.jpdl.cfg.xml" /> <import resource="jbpm.tx.hibernate.cfg.xml" /> <process-engine-context> <object /> </process-engine-context> </jbpm-configuration>
The above file include the hibernate definition.
<?xml version="1.0" encoding="WINDOWS-1251"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "<a href="http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd</a>"> <!-- NOTE: This file is essentially the same as the default one that comes with jbpm. However, because hibernate defaults derby blob columns to be only 255 bytes, jbpm tests fail because the column is too short. The only difference is the following: Original version: <property name="blob" type="blob"><column name="BLOB_VALUE_"/></property> New version: <property name="blob" type="blob"><column name="BLOB_VALUE_" length="10000000"/></property> --> <hibernate-mapping package="org.jbpm.pvm.internal.model" default-access="field"> <!-- ### TYPEDEFS ####################################################### --> <typedef name="converter"> <param name="org.jbpm.pvm.internal.type.converter.BooleanToStringConverter" >bool-str</param> <param name="org.jbpm.pvm.internal.type.converter.ByteToLongConverter" >byte-long</param> <param name="org.jbpm.pvm.internal.type.converter.CharacterToStringConverter" >char-str</param> <param name="org.jbpm.pvm.internal.type.converter.DateToLongConverter" >date-long</param> <param name="org.jbpm.pvm.internal.type.converter.DateToStringConverter" >date-str</param> <param name="org.jbpm.pvm.internal.type.converter.DoubleToStringConverter" >double-str</param> <param name="org.jbpm.pvm.internal.type.converter.FloatToDoubleConverter" >float-double</param> <param name="org.jbpm.pvm.internal.type.converter.FloatToStringConverter" >float-str</param> <param name="org.jbpm.pvm.internal.type.converter.IntegerToLongConverter" >int-long</param> <param name="org.jbpm.pvm.internal.type.converter.SerializableToBytesConverter">ser-bytes</param> <param name="org.jbpm.pvm.internal.type.converter.ShortToLongConverter" >short-long</param> </typedef> <!-- ### EXECUTION ############################################# --> <class name="ExecutionImpl" table="JBPM4_EXECUTION" discriminator-value="pvm"> <id name="dbid" column="DBID_"> <generator /> </id> <discriminator><column name="CLASS_" /></discriminator> <version name="dbversion" column="DBVERSION_" /> <property name="activityName" column="ACTIVITYNAME_" /> <property name="processDefinitionId" column="PROCDEFID_" /> <property name="hasVariables" column="HASVARS_" /> <map name="variables" cascade="all-delete-orphan"> <key foreign-key="FK_VAR_EXECUTION"> <column name="EXECUTION_" index="IDX_VAR_EXECUTION"/> </key> <map-key type="string" column="KEY_" /> <one-to-many /> </map> <map name="systemVariables" cascade="all-delete-orphan"> <key foreign-key="FK_VAR_EXESYS"> <column name="EXESYS_" index="IDX_VAR_EXESYS"/> </key> <map-key type="string" column="KEY_" /> <one-to-many /> </map> <property name="name" column="NAME_" /> <property name="key" column="KEY_" /> <property name="id" column="ID_" unique="true" /> <property name="state" column="STATE_" /> <property name="suspendHistoryState" column="SUSPHISTSTATE_" /> <property name="priority" column="PRIORITY_" /> <property name="historyActivityInstanceDbid" column="HISACTINST_" /> <list name="executions" cascade="all-delete-orphan" inverse="false" lazy="false"> <key column="PARENT_" foreign-key="FK_EXEC_PARENT" /> <list-index column="PARENT_IDX_" /> <one-to-many /> </list> <map name="swimlanes" cascade="all-delete-orphan"> <key foreign-key="FK_SWIMLANE_EXEC"> <column name="EXECUTION_" index="IDX_SWIMLANE_EXEC"/> </key> <map-key type="string" column="NAME_" /> <one-to-many /> </map> <many-to-one name="parent" column="PARENT_" foreign-key="FK_EXEC_PARENT" index="IDX_EXEC_PARENT" lazy="false" /> <many-to-one name="processInstance" column="INSTANCE_" foreign-key="FK_EXEC_INSTANCE" index="IDX_EXEC_INSTANCE" lazy="false" /> <many-to-one name="superProcessExecution" column="SUPEREXEC_" foreign-key="FK_EXEC_SUPEREXEC" index="IDX_EXEC_SUPEREXEC" /> <many-to-one name="subProcessInstance" column="SUBPROCINST_" foreign-key="FK_EXEC_SUBPI" index="IDX_EXEC_SUBPI" /> </class> <!-- ### VARIABLE ####################################################### --> <class name="org.jbpm.pvm.internal.type.Variable" abstract="true" discriminator-value=" " table="JBPM4_VARIABLE"> <!-- discriminator values: date : org.jbpm.pvm.internal.type.variable.DateVariable double : org.jbpm.pvm.internal.type.variable.DoubleVariable hibl : org.jbpm.pvm.internal.type.variable.HibernateLongVariable long : org.jbpm.pvm.internal.type.variable.LongVariable hibs : org.jbpm.pvm.internal.type.variable.HibernateStringVariable string : org.jbpm.pvm.internal.type.variable.StringVariable null : org.jbpm.pvm.internal.type.variable.NullVariable blob : org.jbpm.pvm.internal.type.variable.BlobVariable clob : org.jbpm.pvm.internal.type.variable.ClobVariable --> <id name="dbid" column="DBID_"> <generator /> </id> <discriminator column="CLASS_"/> <version name="dbversion" column="DBVERSION_" /> <property name="key" column="KEY_"/> <property name="converter" type="converter" column="CONVERTER_" /> <property name="isHistoryEnabled" column="HIST_" /> <many-to-one name="execution" column="EXECUTION_" foreign-key="none"/> <many-to-one name="task" column="TASK_" foreign-key="none"/> </class> <subclass name="org.jbpm.pvm.internal.type.variable.BlobVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="blob"> <many-to-one name="lob" column="LOB_" cascade="all" foreign-key="FK_VAR_LOB" index="IDX_VAR_LOB" /> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.DateVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="date"> <property name="date" column="DATE_VALUE_" type="timestamp"/> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.DoubleVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="double"> <property name="d" column="DOUBLE_VALUE_" type="double"/> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.HibernateLongVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="hib-long"> <any name="hibernatable" id-type="long"> <column name="CLASSNAME_"/> <column name="LONG_VALUE_"/> </any> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.HibernateStringVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="hib-string"> <any name="hibernatable" id-type="string"> <column name="CLASSNAME_"/> <column name="STRING_VALUE_"/> </any> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.LongVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="long"> <property name="l" column="LONG_VALUE_" type="long"/> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.NullVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="null"> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.StringVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="string"> <property name="string" column="STRING_VALUE_" type="string"/> </subclass> <subclass name="org.jbpm.pvm.internal.type.variable.TextVariable" extends="org.jbpm.pvm.internal.type.Variable" discriminator-value="text"> <property name="text" column="TEXT_VALUE_" type="text"/> </subclass> <!-- ### LOB ############################################################ --> <class name="org.jbpm.pvm.internal.lob.Lob" table="JBPM4_LOB"> <id name="dbid" column="DBID_"> <generator /> </id> <version name="dbversion" column="DBVERSION_" /> <property name="blob" type="blob"><column name="BLOB_VALUE_" length="10000000" /></property> <!-- should only be re-introduced if there comes a concrete need for it. <property name="bytes" type="binary" column="BINARY_VALUE_"/> <property name="clob" type="clob" column="CLOB_VALUE_" /> <property name="text" type="text" column="TEXT_VALUE_"/> --> </class> <class name="org.jbpm.pvm.internal.job.JobImpl" table="JBPM4_JOB" discriminator-value="Job"> <id name="dbid" column="DBID_"> <generator /> </id> <discriminator column="CLASS_" /> <version name="dbversion" column="DBVERSION_" /> <property name="dueDate" column="DUEDATE_" type="timestamp" index="IDX_JOBDUEDATE" /> <property name="state" column="STATE_" /> <property name="isExclusive" column="ISEXCLUSIVE_" /> <property name="lockOwner" column="LOCKOWNER_" /> <property name="lockExpirationTime" column="LOCKEXPTIME_" index="IDX_JOBLOCKEXP" /> <property name="exception" column="EXCEPTION_" type="text" /> <property name="retries" column="RETRIES_" index="IDX_JOBRETRIES" /> <many-to-one name="processInstance" column="PROCESSINSTANCE_" cascade="none" foreign-key="none" index="IDX_JOB_PRINST" lazy="false"/> <many-to-one name="execution" column="EXECUTION_" cascade="none" foreign-key="none" index="IDX_JOB_EXE"/> <many-to-one name="configurationBytes" column="CFG_" cascade="all" foreign-key="FK_JOB_CFG" index="IDX_JOB_CFG" /> <subclass name="org.jbpm.pvm.internal.job.MessageImpl" discriminator-value="Msg"> <subclass name="org.jbpm.pvm.internal.model.op.ExecuteActivityMessage" discriminator-value="ExeAct" /> <subclass name="org.jbpm.pvm.internal.model.op.ExecuteEventListenerMessage" discriminator-value="ExeEvtLsnr" /> <subclass name="org.jbpm.pvm.internal.job.CommandMessage" discriminator-value="Cmd" /> </subclass> <subclass name="org.jbpm.pvm.internal.job.TimerImpl" discriminator-value="Timer"> <property name="signalName" column="SIGNAL_" /> <property name="eventName" column="EVENT_" /> <property name="repeat" column="REPEAT_" /> <subclass name="org.jbpm.pvm.internal.job.StartProcessTimer" discriminator-value="PeriodicStartProcess" /> </subclass> </class> <!-- ### HibernatePvmDbSession QUERIES ################################## --> <query name="findTimers"> <![CDATA[ select t from org.jbpm.pvm.internal.job.TimerImpl as t order by dueDate asc ]]> </query> <query name="findMessages"> <![CDATA[ select m from org.jbpm.pvm.internal.job.MessageImpl as m ]]> </query> <query name="findJobsWithException"> <![CDATA[ select job from org.jbpm.pvm.internal.job.JobImpl as job where job.retries = 0 order by dueDate asc ]]> </query> <!-- ### HibernateJobDbSession QUERIES ################################## --> <query name="findFirstAcquirableJob"> <![CDATA[ select job from org.jbpm.pvm.internal.job.JobImpl as job where (job.lockExpirationTime is null or job.lockExpirationTime <= :now) and (job.dueDate is null or job.dueDate <= :now) and job.retries > 0 and job.state != 'suspended' order by job.dueDate asc ]]> </query> <query name="findExclusiveJobs"> <![CDATA[ select job from org.jbpm.pvm.internal.job.JobImpl as job where job.lockOwner is null and job.processInstance = :processInstance and job.isExclusive = true and job.retries > 0 and job.state != 'suspended' and (job.dueDate is null or job.dueDate <= :now) order by job.dueDate asc ]]> </query> <query name="findFirstDueJob"> <![CDATA[ select job from org.jbpm.pvm.internal.job.JobImpl as job where job.lockOwner is null and job.retries > 0 and job.state != 'suspended' order by job.dueDate asc ]]> </query> <!-- ### HibernatePvmDbSession QUERIES ############################################# --> <query name="findExecutionById"> <![CDATA[ select execution from org.jbpm.pvm.internal.model.ExecutionImpl as execution where execution.id = :id ]]> </query> <query name="findProcessInstanceById"> <![CDATA[ select processInstance from org.jbpm.pvm.internal.model.ExecutionImpl as processInstance where processInstance.id = :processInstanceId and processInstance.parent is null and processInstance.state != 'suspended' ]]> </query> <query name="findProcessInstanceByIdIgnoreSuspended"> <![CDATA[ select processInstance from org.jbpm.pvm.internal.model.ExecutionImpl as processInstance where processInstance.id = :processInstanceId and processInstance.parent is null ]]> </query> <query name="findProcessInstanceIds"> <![CDATA[ select processInstance.id from org.jbpm.pvm.internal.model.ExecutionImpl as processInstance where processInstance.processDefinitionId = :processDefinitionId and processInstance.parent is null ]]> </query> </hibernate-mapping>
As Last you have to configure the jbpm.hibernate.cfg.xml file. I’m using derbyDB in memory mode.
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "<a href="http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd</a>"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect">org.hibernate.dialect.DerbyDialect</property> <property name="hibernate.connection.driver_class">org.apache.derby.jdbc.EmbeddedDriver</property> <property name="hibernate.connection.url">jdbc:derby:memory:muleEmbeddedDB;create=true</property> <property name="hibernate.hbm2ddl.auto">create-drop</property> <property name="hibernate.format_sql">true</property> <!-- This is a tweaked version of Hibernate's DriverManagerConnectionProvider which works with Mule's hot deployment --> <property name="hibernate.connection.provider_class"> org.hibernate.connection.SimpleConnectionProvider</property> <!-- This property improves thread contention with Mule services a bit --> <property name="hibernate.transaction.flush_before_completion">true</property> <!-- <property name="hibernate.cache.use_second_level_cache">false</property> --> <!-- Standard jBPM mappings --> <mapping resource="jbpm.repository.hbm.xml" /> <mapping resource="jbpm.history.hbm.xml" /> <mapping resource="jbpm.task.hbm.xml" /> <mapping resource="jbpm.identity.hbm.xml" /> <!-- Hibernate defaults Derby's BLOB columns to be too small --> <mapping resource="jbpm.execution.derby.hbm.xml" /> </session-factory> </hibernate-configuration>
I know what you’re thinking: Can I avoid using a persistence system? The answer, at last for me, is not.
I have never found any solution for it. Anyway, you have to think that we are talking about Memory database.
Once you have concluded this part it’s time to understand how mule can comunicate with business process. This is possible by two connector inside orderprocess.jpdl.xml file.
<mule-receive> : Wait state which expects a message to arrive from the Mule endpoint.
<mule-send>: Activity which sends a message with the payload to the Mule endpoint.
This two tags are the start and the end point of our business process model. Unfortunately there aren’t a graphic rappresentations of in eclipse plugin. So, you can see it only through the source code.
The mule config file accepts an input by the user and run the process:
<?xml version="1.0" encoding="UTF-8"?> <mule xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:stdio="http://www.mulesoft.org/schema/mule/stdio" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpm="http://www.mulesoft.org/schema/mule/bpm" xmlns:vm="http://www.mulesoft.org/schema/mule/vm" xsi:schemaLocation=" http://www.mulesoft.org/schema/mule/stdio http://www.mulesoft.org/schema/mule/stdio/3.3/mule-stdio.xsd http://www.mulesoft.org/schema/mule/bpm http://www.mulesoft.org/schema/mule/bpm/3.3/mule-bpm.xsd http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/3.3/mule.xsd http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/3.3/mule-vm.xsd"> <bpm:jbpm /> <stdio:endpoint name="toOrderprocess" system="IN" /> <stdio:endpoint name="fromOrderprocess" system="OUT" /> <stdio:connector name="stdio" promptMessage="Order Number: " messageDelayTime="1000"/> <flow name="echo"> <stdio:inbound-endpoint ref="toOrderprocess"/> <bpm:process processName="orderprocess" processDefinition="orderprocess.jpdl.xml"/> <stdio:outbound-endpoint ref="fromOrderprocess"/> </flow> </mule>
The user inserts the order number id and the process search for this order.
Once find it, the process decides if accept or refuse the order. The output of this is given to output endpoint mule connector.
Very important definition are the global endpoint “toOrderprocess” and “fromOrderprocess”. They are used even in process model definition.
The output will be:
Order Number: AZ-1265 orderId: AZ-1265 quantity: 100 amount: 2011.9 response:Accepted INFO 2012-06-08 14:35:17,424 [echo.stage1.02] org.mule.module.bpm.Process: New process started, ID = orderprocess.32 execution[orderprocess.32] Order Number: AZ-6522 orderId: AZ-6522 quantity: 10 amount: 115.9 response:Refused INFO 2012-06-08 14:35:28,486 [echo.stage1.02] org.mule.module.bpm.Process: New process started, ID = orderprocess.44 execution[orderprocess.44] Order Number:
Summary
I’ve found a bit of confusion among bpm implementations. Even Mule is partecipating in Activiti project and, probably, Mule will give more integration on this direction rather than other implementation (this version implements BPMN 2.0).
Be careful when you use the diagram designer because, sometimes, I’ve found the cutted code when I switched from design to source code.
I hope I’ve given to you a little guide to start using BPM in your project. As usual, my suggestion is to spend time to learn it.