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)
grab a hold on WEB-INF/jdbc.properties file and modify the required fields, save the file.
That is, make sure you modified username and password fields
Open up WEB-INF/applicationWebContext.xml and uncomment <prop key="hibernate.hbm2ddl.auto">create</prop>
located under jbpmSessionFactory bean, save the file.
On your terminal window run mvn clean install
Fetch the simpleForms.war from the target directory created by maven
Copy and paste the simpleForms.war into your tomcat webapps directory
Start up your Tomcat server and make sure it starts up successfully.
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..
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 the Spring's OpenSessionInViewFilter to handle hibernate sessions under the hood
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.
Comments