Reasons to migrate to jbpm Enterprise:
In order to benefit from the clustering and transaction management capabilities of enterprise beans, and the persistence of ejb timers which ensures fail over it was decided to try conversion of the java thread based scheduling to ejb timer based scheduling
Once of the key reasons for migration was that, some of the logic execution in workflows were time consuming and resulted in increased response time to the user. Enterprise beans provide messaging features which can be configured to make nodes asynchronous and thereby leverage the Java Messaging System implementation of the Jbpm enterprise archive.
It is light weight and extremely easy to use
Steps to Migrate Jbpm 3.3.1 GA to Jboss 7.2:
- The steps described here are required for deployment of the JBPM 3 enterprise beans in applications that use their api for workflow processing.
- Setting up jboss 7 AS.
- In standalone/configuration/standalone-full.xml add the following under <jms-destinations>
<jms-queue name="JobQueue"> <entry name="queue/JobQueue"/> <entry name="java:jboss/exported/jms/queue/JobQueue"/> </jms-queue> <jms-queue name="JbpmCommandQueue"> <entry name="queue/JbpmCommandQueue"/> <entry name="java:jboss/exported/jms/queue/JbpmCommandQueue"/> </jms-queue> <jms-queue name="DeadLetterQueue"> <entry name="queue/DLQ"/> <entry name="java:jboss/exported/jms/queue/DLQ"/>
- Download jbpm-installer-3.3.1.GA from jboss site and unzip the contents. The enterprise source code can be found under \src\jbpm-enterprise-sources folder
- The enterprise archive provided in Jbpm 3 are of the EJB 2 specification. Some changes are required to deploy the enterprise beans in Jboss 7 AS.
- In ejb-jar.xml the following changes are requrired.
- Change
<ejb-jar version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
To
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd" version="3.0">
- Remove the references to JMS queues in the session bean CommandServiceBean
- Add the following under CommandListenerBean in ejb-jar.xml
<message-destination-ref> <description> Messages that do not contain a command are sent to the queue referenced here. Optional; if absent, such messages are rejected, which may cause the container to redeliver. </description> <message-destination-ref-name>queue/JbpmCommandQueue</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <message-destination-usage>Produces</message-destination-usage> <message-destination-link>java:/queue/JbpmCommandQueue</message-destination-link> </message-destination-ref> <message-destination-ref> <description> Messages that do not contain a command are sent to the queue referenced here. Optional; if absent, such messages are rejected, which may cause the container to redeliver. </description> <message-destination-ref-name>queue/DLQ</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <message-destination-usage>Produces</message-destination-usage> <message-destination-link>java:/queue/DLQ</message-destination-link> </message-destination-ref> <activation-config> <activation-config-property> <activation-config-property-name>destinationType</activation-config-property-name> <activation-config-property-value>javax.jms.Queue</activation-config-property-value> </activation-config-property> </activation-config> <activation-config> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>java:/queue/JbpmCommandQueue</activation-config-property-value> </activation-config-property> </activation-config> <activation-config> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>java:/queue/DLQ</activation-config-property-value> </activation-config-property> </activation-config>
Add the following under JobListenerBean
<message-destination-link>java:/queue/JobQueue</message-destination-link> <message-destination-ref> <description> Messages which do not contain a job ID are sent to the queue referenced here. Optional; if absent, such messages are rejected, which may cause the container to redeliver. </description> <message-destination-ref-name>queue/DLQ</message-destination-ref-name> <message-destination-type>javax.jms.Queue</message-destination-type> <message-destination-usage>Produces</message-destination-usage> <message-destination-link>java:/queue/DLQ</message-destination-link> </message-destination-ref> <activation-config> <activation-config-property> <activation-config-property-name>destinationType</activation-config-property-name> <activation-config-property-value>javax.jms.Queue</activation-config-property-value> </activation-config-property> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>java:/queue/JobQueue</activation-config-property-value> </activation-config-property> </activation-config> <activation-config> <activation-config-property> <activation-config-property-name>destination</activation-config-property-name> <activation-config-property-value>java:/queue/DLQ</activation-config-property-value> </activation-config-property> </activation-config>
Remove from ejb-jar.xml
<message-destination> <message-destination-name>JobQueue</message-destination-name> </message-destination> <message-destination> <message-destination-name>CommandQueue</message-destination-name> </message-destination>
- Jboss 7 AS requires the jboss-ejb3.xml . It is a custom deployment descriptor which takes precedence over ejb-jar.xml. It is primarily used to declare non-standard namespaces. It is required to link the reference names of resources mentioned in the ejb-jar.xml with the jndi names of the resources. The file is attached here.
- Source Code changes: Some changes have to be made to the enterprise sources to look up the correct JNDI name.
- Edit Manifest.mf and add the following dependencies Dependencies: org.hornetq, org.dom4j ,org.apache.commons.collections
- Change the code
org/jbpm/ejb/impl /CommandListenerBean
jmsConnectionFactory = (ConnectionFactory) jndiContext.lookup("java:comp/env/jms/JbpmConnectionFactory"); to jmsConnectionFactory = (ConnectionFactory) jndiContext.lookup("java:/JmsXA"); LocalCommandServiceHome commandServiceHome = (LocalCommandServiceHome) jndiContext.lookup("java:comp/env/ejb/LocalCommandServiceBean"); to LocalCommandServiceHome commandServiceHome = (LocalCommandServiceHome) jndiContext.lookup("java:global/jbpmenterprise/jbpm-enterprise/CommandServiceBean!org.jbpm.ejb.LocalCommandServiceHome"); deadLetterQueue = (Destination) jndiContext.lookup("java:comp/env/jms/DeadLetterQueue"); to deadLetterQueue = (Destination) jndiContext.lookup("java:/queue/DLQ");
org/jbpm/ejb/impl /TimerEntityBean
LocalCommandServiceHome commandServiceHome = (LocalCommandServiceHome) JndiUtil.lookup("java:comp/env/ejb/LocalCommandServiceBean", LocalCommandServiceHome.class); to LocalCommandServiceHome commandServiceHome = (LocalCommandServiceHome) JndiUtil.lookup("java:global/jbpmenterprise/jbpm-enterprise/CommandServiceBean!org.jbpm.ejb.LocalCommandServiceHome");
org.jbpm.scheduler.ejbtimer.EntitySchedulerServiceFactory
String timerEntityHomeJndiName = "java:jbpm/TimerEntityBean"; to String timerEntityHomeJndiName = "java:global/jbpmenterprise/jbpm-enterprise/TimerEntityBean!org.jbpm.ejb.LocalTimerEntityHome";
org/jbpm/msg/jms/JMSMessageServiceFactory.java
String connectionFactoryJndiName = "java:/JmsXA"; String destinationJndiName = "java:/queue/JobQueue"; String commandDestinationJndiName = "java:/queue/JbpmCommandQueue";
Some times you may come across Antlr issue leading to org.hibernate.QueryException: ClassNotFoundException: org.hibernate.hql.ast.HqlToken
Included antlr that was shipped with jbpm into hibernate3 module. If your application is using hibernate without JPA , you could continue to use it by creating a separate module in jboss 7 AS for hibernate 3 and older versions. Just make sure that you include the dependency to the module in the jboss-deployment-structure.xml
Change hibernate-cfg.xml as
Changed hibernate config to specify jndi name of user transaction
<property name="jta.UserTransaction">java:jboss/UserTransaction</property>
- Changed jbpm-cgf.xml to use jtadbpersistence factory
<service name="persistence"> <factory> <bean class="org.jbpm.persistence.jta.JtaDbPersistenceServiceFactory"> <field name="isTransactionEnabled"><false /></field> <field name="isCurrentSessionEnabled"><false/> </field> </bean> <!-- The above session and transaction enabled properties are set to false so as to use container managed transactions. This is required when using jbpm enterprise resources. --> <!--Set --> <service name="message" factory="org.jbpm.msg.jms.JmsMessageServiceFactory" /> <service name="scheduler" factory="org.jbpm.scheduler.ejbtimer.EntitySchedulerServiceFactory" />
set executor as
<null name="jbpm.job.executor" />
Comment JobExecutor configuration as
<!-- <bean name="jbpm.job.executor" class="org.jbpm.job.executor.JobExecutor"> <field name="jbpmConfiguration"><ref bean="jbpmConfiguration" /></field> <field name="name"><string value="JbpmJobExecutor" /></field> <field name="nbrOfThreads"><int value="1" /></field> <field name="idleInterval"><int value="5000" /></field> <field name="maxIdleInterval"><int value="3600000" /></field> -->
Test Cases covered:
s.no | Type | Scenario | Result | Workflow tested |
1 | Normal timers | Timer created by an action class which is triggered on-enter event of a state | Passed | EjbTimers |
|
| Shut down jboss after timer has been created restart before timer expired | Passed | EbTimers |
|
| Timer created in workflow configuration IDE | Passed | EjbTimers |
2 | Async nodes | Created a workflow with one asych node between two nodes | Passed | Message sent to job queue and picked by commandlistener bean |
|
| Created a workflow with one asychnod between two nodes : used DBMessageServiceFactory | Passed | Async node never got executed |
3 |
| Created a workflow with one asychnode between two nodes : used JMSMessageServiceFactory | Passed | It executed node before async node and ended transaction , new transaction started by command listener bean |
|
| Created a workflow with one asychnode between two nodes : used JMSMessageServiceFactory and stopped the server and started before timer due date | Passed | The timer was executed and process was completed |
|
| Created a workflow with one asychnode between two nodes : used JMSMessageServiceFactory and stopped the server and started after timer due date | Passed |
|
Benefits
In the java thread mechanism, a few threads were continuously running to pick the queued jobs from the job table and execute them. By migrating to enterprise beans, the client invoked the ejb method which creates a timer and registers a callback in the EJB timer service. The EJB container invoked the timeout method in the bean instance when the timer expires. Hence no separate thread pool is required and we expect a better out-of-box performance
The timers being part of the EJB specification makes the application portable across containers.
Some issues
Since timers are persisted as files, when the application is redeployed, and the files are removed, the timers would be gone. To prevent this, the files in the following folders should be backed up in case of a redeployment of application.
{JBOSS-HOME}\bin\standalone\data\timer-service-data
Comments