Version 10

    Spring  Jbpm  JSF Maven Project Example

    -


    This is a simple example of an app that runs a simple process and shows how to integrate Spring with Jbpm

     

    The view technology chosen is JSF and the database chosen is MySql.

     

    Requirements

    The basic requirement is that you have running a MySql database that connects to your schema.

     

    House Cleaning

    The application described can be downloaded (maven project) and then you can mvn clean install to create

     

    your simpleForms.war and deploy it on your Tomcat server (i am running on 6.0.9)

     

    Your may need to manually install the jbpm lib files jbpm-3.1.2.jar and jbpm-identity-3.1.2.jar for your

     

    maven to get them correctly.  Then modify the pom file accordingly that is,

    <dependency>
                <groupId>jboss</groupId>
                <artifactId>jbpm</artifactId>
                <version>3.1.2</version>
                <scope>compile</scope>
            </dependency>
            <dependency>
                <groupId>jboss</groupId>
                <artifactId>jbpm-identity</artifactId>
                <version>3.1.2</version>
                <scope>compile</scope>
            </dependency>
    

     

    Setting your infrastructure

    JBPM Tables

    For starters we need to have the jbpm tables into your MySql database.

     

    The easiest way I found is to let hibernate create them for you: (Using the attached project)

    1. grab a hold on WEB-INF/jdbc.properties file and modify the required fields, save the file.

      1. That is, make sure you modified username and password fields

    2. Open up WEB-INF/applicationWebContext.xml and uncomment <prop key="hibernate.hbm2ddl.auto">create</prop>

    located under jbpmSessionFactory bean, save the file.

    1. On your terminal window run mvn clean install

    2. Fetch the simpleForms.war from the target directory created by maven

    3. Copy and paste the simpleForms.war into your tomcat webapps directory

    4. Start up your Tomcat server and make sure it starts up successfully.

    5. Check against your database (I use DBVisualizer or any other database viewer) and make

    sure you can see a large array of tables created eg JBPM_PROCESSDEFINITION etc..

    1. Shut down your Tomcat Server, go back to your project and comment out the <prop key="hibernate.hbm2ddl.auto">create</prop> in your WEB-INF/applicationWebContext.xml file

     

    Integrating Spring into the mix

    This will give you an explanation on how Spring is wired into the application and allows for easier management

     

    of your processes.

    web.xml

    Things to note for

    • We will currently use only one spring application context file

     

    (look under <param-name>contextConfigLocation</param-name>).

     

     

    • We use JSF 1.1 and is configured in the web.xml file with only one faces-config file

    • The Faces Servlet uses the url pattern of .faces meaning our applyHolidayForm.jsp maps to

     

       applyHolidayForm.faces

     

    applicationWebContext.xml

    The way Spring knows of our process definition location is via the holidayProcessDefinition bean

     

    <!-- holiday form process -->
        <bean id="holidayProcessDefinition" class="org.springmodules.workflow.jbpm31.definition.ProcessDefinitionFactoryBean">
            <property name="definitionLocation" value="classpath:/holidaydefinition.xml"></property>
        </bean>

     

    jbpmTemplate is the main helper bean that you use to interface to your process

     

    it has references to the holidayProcessDefinition bean and to the jbpmConfig bean

     

    <bean id="jbpmTemplate" class="org.springmodules.workflow.jbpm31.JbpmTemplate">
            <constructor-arg index="0" ref="jbpmConfig"></constructor-arg>
            <constructor-arg index="1" ref="holidayProcessDefinition"></constructor-arg>
            <property name="hibernateTemplate" ref="hibernateTemplate"></property>
        </bean>

     

    jbpmConfig contains information of the jbpmSessionFactory and the jbpm.cfg.xml file

    <bean id="jbpmConfig" class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
            <property name="sessionFactory" ref="jbpmSessionFactory"></property>
            <property name="configuration" value="classpath:/jbpm.cfg.xml"></property>
            <property name="processDefinitions">
                <list>
                    <ref local="holidayProcessDefinition"></ref>
                </list>
            </property>
            <property name="createSchema" value="false"></property>
        </bean>

     

    jbpmSessionFactory manages the session note that its name MUST be sessionFactory otherwise it wont

     

    be use by the openSessionInViewInterceptor

     

    the sessionFactory also knows about the hibernate mapping tables via hibernate.cfg.xml file and knows

     

    about database connectivity via dataSource bean

    <bean id="jbpmSessionFactory" name="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource"></property>
            <property name="configLocation">
                <value>classpath:/hibernate.cfg.xml</value>
            </property>
            <property name="hibernateProperties">
                <props>
                    <!-- <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> -->
                    <!-- <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> -->
                    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                    <!-- Create/update the database tables automatically when the JVM starts up -->
                    <!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->                
                    <prop key="hibernate.cache.use_query_cache">false</prop> -->
                    <prop key="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</prop>
                    <!-- Turn batching off for better error messages under PostgreSQL 
                    <prop key="hibernate.jdbc.batch_size">0</prop> -->
                </props>
            </property>
        </bean>

     

    OpenSessionInViewInterceptor manages hibernate session  a la Spring, each request is intercepted via the

     

    OpenSessionInViewFilter declared in web.xml note that is autowire by name

     

    meaning that jbpmSessionFactory bean must have a name of sessionFactory

    <bean name="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor" autowire="byName"></bean>

     

    txManager manages the hibernate transactions and is used by our DAOs / Service beans it has

     

    knowledge of the sessionFactory too.

    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" >
            <property name="sessionFactory" ref="jbpmSessionFactory"></property>
        </bean>

     

    Setting up our DAOs beans

     

    We first develop our DAO target bean which has autowire by name.  This means that our actual DAO needs to have

     

    a reference to JbpmTemplate bean with a property name of jbpmTemplate.

     

    Thus in code it will look

    private JbpmTemplate jbpmTemplate;

    with its corresponding getter and setters

     

    Our DAO is called ProcessFormDAO so to create our target DAO bean we do :

    <bean id="processFormDAOTarget" class="au.com.qtc.simpleForms.jbpm.dao.ProcessFormDAO" autowire="byName"></bean>

    JSF managed beans or in a real live application our manager beans will have a reference not to

     

    processFormDAOTarget but to processFormDAO and this bean is set up as follows:

    <bean id="processFormDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
           <property name="transactionManager" ref="txManager"></property>
           <property name="target" ref="processFormDAOTarget"></property>
           <property name="proxyTargetClass" value="true" ></property>
           <property name="transactionAttributes">
             <props>
               <prop key="*">PROPAGATION_NESTED</prop>
             </props>
           </property>
         </bean>

    Note that it has a reference to :

    • txManager bean

    • processFormDAOTarget bean

    And we allow for propagation nested.

     

    Setting up email notification

     

    At the moment I havent caught up with how do you let Jbpm to send email notifications

     

    So for the meantime you can use Spring Mail support service.

     

    The basic idea is this.  I want an email notification to be triggered upon entering a decision node. 

     

    In essence after doing my decision logic in the node I want to send a corresponding email that will inform the

     

    originator of the outcome of the decision.

     

    However I wired up my email using Spring and as such Spring needs to load the email manager bean on its

     

    context loader.  So how to let my decision node know about my email manager in Spring?

     

    Initially the process definition looked like this:

    <decision name="evaluateLeave">
            <handler class="au.com.qtc.simpleForms.jbpm.decision.EvaluateLeaveDecisionHandler"></handler>
            <transition to="end" name="yes">
                 <action class="au.com.qtc.simpleForms.jbpm.action.NotifyAboutLeaveActionHandler"></action>
            </transition>
            <transition to="giveDonuts" name="no">
                <action class="au.com.qtc.simpleForms.jbpm.action.NotifyAboutLeaveActionHandler"></action>
             </transition>
        </decision>  
    

    I want my NotifyAboutLeaveActionHandler to know about my mail manager Spring bean.

    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
                   <property name="host" value="mail.optusnet.com.au"></property>
               </bean>
               
               <bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
                   <property name="from" value="simpleForms@nando.com"></property>
               </bean>
               
               <bean id="notifyEmailManager" class="au.com.qtc.simpleForms.mail.SimpleMailManager">
                   <property name="mailSender" ref="mailSender"></property>
                   <property name="templateMessage" ref="templateMessage"></property>
               </bean>

    I want to inject notifyEmailManager into the NotifyAboutLeaveActionHanlder object.

     

     

    This is the solution:

     

     <decision name="evaluateLeave">
            <handler class="au.com.qtc.simpleForms.jbpm.decision.EvaluateLeaveDecisionHandler"></handler>
            <transition to="end" name="yes">
                <action name="notificationAction" config-type="bean" class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
                    <targetBean>notifyAboutLeaveActionHandler</targetBean>
                </action>
            </transition>
            <transition to="giveDonuts" name="no">
                <action name="notificationAction" config-type="bean" class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
                    <targetBean>notifyAboutLeaveActionHandler</targetBean>
                </action>
             </transition>
        </decision>

    Note the notificationAction nae refers to a spring module JbpmHandlerProxy bean.  which as the name suggest

     

    acts as a proxy to my notifyAboutLeaveActionHandler bean.

     

    So what is this notifyAboutLeaveActinoHandler bean coming from? you may ask!

     

    This is where spring injection is made possible!

     

    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
                   <property name="host" value="mail.optusnet.com.au"></property>
               </bean>
               
               <bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage">
                   <property name="from" value="simpleForms@nando.com"></property>
               </bean>
               
               <bean id="notifyEmailManager" class="au.com.qtc.simpleForms.mail.SimpleMailManager">
                   <property name="mailSender" ref="mailSender"></property>
                   <property name="templateMessage" ref="templateMessage"></property>
               </bean>
               
               <bean id="notifyAboutLeaveActionHandler" class="au.com.qtc.simpleForms.jbpm.action.NotifyAboutLeaveActionHandler">
                   <property name="mailManager" ref="notifyEmailManager"></property>
               </bean>

    Notice now that my notifyAboutLeaveActionHandler is now a Spring bean loaded in the same Spring loader context

     

    as such it can now inject any Spring declared beans.  As such I am now able to inject the appropriate

     

    mailManager bean.

     

    Thus in code the notifyAboutLeaveActionHandler has the following :

        private MailManager mailManager;
    
        public MailManager getMailManager() {
            return mailManager;
        }
    
        public void setMailManager(MailManager mailManager) {
            this.mailManager = mailManager;
        }
    ...
    

    So it is now possible to have a preloaded instance of mailManager inside my NotifyAboutLeaveActionHandler.