Version 1

    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:

     

    1. The steps described here are required for deployment of the JBPM 3 enterprise beans in applications that use their api for workflow processing.
    2. Setting up jboss 7 AS.
      1. 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"/>
    
    
        1. 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
        2. 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.
        3. In ejb-jar.xml the following changes are requrired.
          1. 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">
    
    
          1. Remove the references to JMS queues in the session bean CommandServiceBean
          2. 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>
    
    

    1. 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.
    2. Source Code changes: Some changes have to be made to the enterprise sources to look up the correct JNDI name.
      1. Edit Manifest.mf and  add the following dependencies                                                                                                                                                                                                           Dependencies:  org.hornetq, org.dom4j ,org.apache.commons.collections
      2. 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>
    
    1.   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