Users familiar with Spring will see that the jBPM module structure resembles with the orm package from the main Spring distribution. The module offers a central template class for working with jBPM, a callback to access the native JbpmContext and a local factory bean for configuration and creating a jBPM instance.
<beans>
<!-- DataSource definition -->
<bean id="dataSource" class="...">
...
</bean>
<!-- Hibernate SessionFactory definition -->
<bean id="hibernateSessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
...
</bean>
<!-- helper for reading jBPM process definitions -->
<bean id="simpleWorkflow"
class="org.springmodules.workflow.jbpm31.definition.ProcessDefinitionFactoryBean">
<property name="definitionLocation"
value="classpath:org/springmodules/workflow/jbpm31/simpleWorkflow.xml"/>
</bean>
<!-- jBPM configuration -->
<bean id="jbpmConfiguration"
class="org.springmodules.workflow.jbpm31.LocalJbpmConfigurationFactoryBean">
<property name="sessionFactory" ref="hibernateSessionFactory"/>
<property name="configuration" value="classpath:jbpm.cfg.xml"/>
<property name="processDefinitions">
<list>
<ref local="simpleWorkflow"/>
</list>
</property>
<property name="createSchema" value="true"/>
<property name="processDefinitionsResources">
<list>
<value>classpath:/org/springmodules/workflow/jbpm31/someOtherWorkflow.xml</value>
</list>
</property>
</bean>
<!-- jBPM template -->
<bean id="jbpmTemplate" class="org.springmodules.workflow.jbpm31.JbpmTemplate">
<constructor-arg index="0" ref="jbpmConfiguration"/>
<constructor-arg index="1" ref="simpleWorkflow"/>
</bean>
set
</beans>
The example above shows how (existing) Spring-managed Hibernate SessionFactories and transaction management can be reused with jBPM.
9.2.1. LocalJbpmConfigurationFactoryBean
The main element is LocalJbpmConfigurationFactoryBean which should be familiar to users acustomed to Spring. Based on the jbpm configuration file and the given SessionFactory, it will create a jBPM configuration which can be used for working with the given process definitions. It is possible to replace jBPM xml configuration with jBPM 3.1.x newly added ObjectFactory - note that if both are present the xml configuration is preffered. LocalJbpmConfigurationFactoryBean allows the creation of the underlying schema based on the process definitions loaded automatically at startup.
Note that the sessionFactory property is not mandatory - Hibernate SessionFactory can be reused with jBPM or jBPM can work by itself without any integration with the existing infrastructure. However, in most scenarios, using LocalJbpmConfigurationFactoryBean allows one to take advantage of Spring transaction management infrastructure so it's possible without any code change to use jBPM, Hibernate and jdbc-based code inside the same transactional context, be it managed locally or globally (JTA). Moreover, it is possible to use thread-bound session or OpenSessionInView patterns with jBPM.
LocalJbpmConfigurationFactoryBean is also aware of the enclosing applicationContext lifecycle - jBPM will be initialized once the context is started (usually application startup) and will be closed properly when the context is destroyed (application is shutdown).
Note that LocalJbpmConfigurationFactoryBean can be configured programatically and can be used standalone only to build an jBPM context which can be used independently of Spring Modules jBPM support.
9.2.2. Inversion of Control: JbpmTemplate and JbpmCallback
Another important feature of Spring Modules jBPM support is JbpmTemplate. The template offers very convient ways of working directly with process definitions as well as jBPM API taking care of handling exceptions (be it jBPM or Hibernate based) in respect to the ongoing transaction (if it's present), the underlying Hibernate session (if pesistent services are used) and the jBPM context. jBPM exceptions (and the underlying Hibernate information) are translated into Spring's DAO exception hierarchy. Everything happens in a transparent and consistent manner.This is possible, as with every Spring-style template,even when direct access to the native JbpmContext is desired, through the JbpmCallback:
public ProcessInstance findProcessInstance(final Long processInstanceId) {
return (ProcessInstance) execute(new JbpmCallback() {
public Object doInJbpm(JbpmContext context) {
// do something
...
return context.getGraphSession().loadProcessInstance(processInstanceId.longValue());
}
});
}
As well, as LocalJbpmConfigurationFactoryBean, the JbpmTemplate can be configured programatically and can be used standalone on a pre-existing jbpmContext (configured through LocalJbpmConfigurationFactoryBean or not) and can be used independently of Spring Modules jBPM support.
9.2.3. ProcessDefinitionFactoryBean
ProcessDefinitionFactoryBean is a simple reader that loads jBPM process definition using Spring's ResourceLoaders. Thus, the xml files can be load using the classpath, relative or absolute file path or even from the Servlet Context. See the official documentation for more information.
|
Note |
As reported on the forums, using ProcessDefinitionFactoryBean jBPM 3.1.1will trigger a new process definition to be persisted(through deployProcessDefinition) at each startup. While this is useful in development when the database is created on application startup and destroyed on closing, for cases where the definition doesn't change, the process should not be declared inside Spring XML files.
|
|
Note |
As reported here, due to the static nature of jBPM, process definitions which include sub processes are not loaded properly if a JbpmContext does not exist at the time of the loading (no exception is thrown whatsoever). As a workaround consider using the LocalJbpmConfigurationFactoryBean's processDefinitionsResources property.
|
9.2.4. Outside Spring container
It is important to note that while our example showed LocalJbpmConfigurationFactoryBean and JbpmTemplate template inside a Spring xml, these classes do not depend on each other or on Spring application context. They can be just as well configured programatically and can
9.3. Accessing Spring beans from jBPM actions
Another important feature of Spring Modules jBPM integration is allowing Spring configured beans to be reused inside jBPM actions. This allows one to leverage Spring container capabilities (bean lifecycles, scoping, injection, proxying just to name a few) in a transparent way with jBPM. Consider the following Spring application context:
<beans>
<!-- Spring bean visible inside jBPM processed -->
<bean id="jbpmAction" class="org.MyJbpmActionHandler" singleton="true">
<property name="someProp" ref="anotherBean"/>
...
</bean>
..
</beans>
and jBPM process definition:
<?xml version="1.0" encoding="UTF-8"?>
<process-definition name="simpleWorkflow">
<start-state>
<transition to="myState">
</transition>
</start-state>
<state name="myState">
<transition to="end">
<action name="myAction" config-type="bean"
class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
<targetBean>jbpmAction</targetBean>
<factoryKey>jbpmConfiguration</factoryKey>
</action>
</transition>
</state>
<end-state name="end"/>
</process-definition>
JbpmHandlerProxy transparently locates Spring applicationContext and searches the bean identified by the targetBean parameter (in this case jbpmAction) and delegate all calls to the jBPM action. This way, one is not limited only to the injection offered by jBPM container and can integrate and communicate in a very easy manner with other Spring managed beans. Moreover, your action lifecycle can be sigleton (one shared instance) or prototype (every call gets a new instance) or in Spring 2.0 scoped to a certain application component (like one instance per http session).
The optional factoryKey parameter specified in this example should be used when one is dealing with more then one jBPM configuration inside the same classloader (not common in practice). The factoryKey should be the same as the bean name of the LocalJbpmConfigurationFactoryBean to be used (in our case jbpmConfiguration).