- A new enhancement was necessary to access a custom database table, so I needed to inject a DAO bean into the activiti serviceTask.
- Refactoring of the code base was needed. Having Spring manage the java delegate service task versus instantiating new objects for each process execution is always a better way to go, if the application is already Spring managed (which Alfresco is).
- i.e. I needed access to the DAO bean and alfresco available spring beans.
- NOTE: You now have to make sure your class is thread safe though!
- Another side-effect is it's a little bit easier to write junit tests when using Spring dependency injection than instantiating new objects inside methods (take a look at one of my older posts about that).
I wanted to piggy-back off of the activiti workflow engine that is already embedded in Alfresco 4, so decided not define our own activiti engine manually. The Alfresco Summit 2013 had a great video tutorial, which helped immensely to refactor the "Old Method" to the "New Method", described below.
Example:
For our example, we'll use a simple activiti workflow that defines two service tasks, CherryJavaDelegate and ShoeJavaDelegate (The abstract AbstractCherryShoeDelegate is the parent). The "Old Method" does NOT have spring managing the activiti service task java delegates. The "New Method" has spring manage and inject the activiti service task java delegates, and also adds an enhancement for both service tasks to write to a database table.
Old Method:
- Notice that the cherryshoebpmn.xml example below is defining the serviceTask's to use the "activiti:class" attribute; this will have activiti instantiate a new object for each process execution:
- Since we have multiple service tasks that need access to the same activiti engine java delegate, we defined an abstract class that defined some of the functionality. The specific concrete classes would provide / override any functionality not defined in the abstract class.
<process id="cherryshoeProcess" name="Cherry Shoe Process" isExecutable="true">
...
<serviceTask id="cherryTask" name="Insert Cherry Task" activiti:class="com.cherryshoe.activiti.delegate.CherryJavaDelegate"></serviceTask>
<serviceTask id="shoeTask" name="Insert Shoe Task" activiti:class="com.cherryshoe.activiti.delegate.ShoeJavaDelegate"></serviceTask>
...
</process>
...
import org.activiti.engine.delegate.JavaDelegate;
...
public abstract class AbstractCherryShoeDelegate implements JavaDelegate {
...
@Override
public void execute(DelegateExecution execution) throws Exception {
...
}
...
}
public class CherryJavaDelegate extends AbstractCherryShoeDelegate {
...
...
}
Here's a summary of all that had to happen to have Spring inject the java delegate Alfresco 4 custom activiti service tasks (tested with Alfresco 4.1.5) and to write to database tables via injecting DAO beans.
- The abstract AbstractCherryShoeDelegate class extends activiti engine's BaseJavaDelegate
- There are class load order issues where custom spring beans will not get registered. Set up depends-on relationship with the activitiBeanRegistry for the AbstractCherryShoeDelegate abstract parent
- The following must be kept intact:
- In the spring configuration file,
- Abstract AbstractCherryShoeDelegate class defines parent="baseJavaDelegate" abstract="true" depends-on="activitiBeanRegistry"
- For each concrete Java Delegate:
- The concrete bean id MUST to match the class name, which in term matches the activiti:delegateExpression on the bpmn20 configuration xml file
- NOTE: Reading this alfresco forum looks like the activitiBeanRegistry registers the bean by classname, not by bean id, so likely this is not a requirement
- The parent attribute MUST be defined as an attribute
- Define spring beans for the abstract parent class AbstractCherryShoeDelegate and each concrete class that extends AbstractCherryShoeDelegate (i.e. CherryJavaDelegate and ShoeJavaDelegate). Have spring manage the custom activiti java delegates where the concrete class. The abstract parent must define it's own parent as "baseJavaDelegate", abstract="true", and depends-on="activitiBeanRegistry".
- Notice that the cherryshoebpmn.xml example below is using the "activiti:delegateExpression" attribute and referencing the spring bean. This means only one instance of that Java class is created for the serviceTask it is defined on, so the class must be implemented with thread-safety in mind:
- The abstract class is now changed to extend the BaseJavaDelegate. The specific concrete classes would provide / override any functionality not defined in the abstract class.
<bean id="AbstractCherryShoeDelegate" parent="baseJavaDelegate" abstract="true" depends-on="activitiBeanRegistry"></bean>
<bean id="CherryJavaDelegate"
class="com.cherryshoe.activiti.delegate.CherryJavaDelegate" parent="AbstractCherryShoeDelegate">
<property name="cherryDao" ref="com.cherryshoe.database.dao.CherryDao"/>
</bean>
<bean id="ShoeJavaDelegate"
class="com.cherryshoe.activiti.delegate.ShoeJavaDelegate" parent="AbstractCherryShoeDelegate">
<property name="shoeDao" ref="com.cherryshoe.database.dao.ShoeDao"/>
</bean>
Note below won't work!
- Do NOT put any periods to denote package structure in the bean id! Alfresco/Activiti got confused by the package ".", where spring normally works fine with this construct.
- Also just because the concrete class is extending the parent abstract class, is not enough to make it work.
<bean id="com.cherryshoe.activiti.delegate.CherryJavaDelegate"
class="com.cherryshoe.activiti.delegate.CherryJavaDelegate" >
<property name="cherryDao" ref="com.cherryshoe.database.dao.CherryDao"/>
</bean>
<bean id="com.cherryshoe.activiti.delegate.ShoeJavaDelegate"
class="com.cherryshoe.activiti.delegate.ShoeJavaDelegate" >
<property name="shoeDao" ref="com.cherryshoe.database.dao.ShoeDao"/>
</bean>
<process id="cherryshoeProcess" name="Cherry Shoe Process" isExecutable="true">
...
<serviceTask id="cherryTask" name="Insert Cherry Task" activiti:delegateExpression="${CherryJavaDelegate}"></serviceTask>
<serviceTask id="shoeTask" name="Insert Shoe Task" activiti:delegateExpression="${ShoeJavaDelegate}"></serviceTask>
...
</process>
...
import org.alfresco.repo.workflow.activiti.BaseJavaDelegate;
...
public abstract class AbstractCherryShoeDelegate extends BaseJavaDelegate {
...
@Override
public void execute(DelegateExecution execution) throws Exception {
...
}
...
}
public class CherryJavaDelegate extends AbstractCherryShoeDelegate {
...
}
No comments:
Post a Comment
I appreciate your time in leaving a comment!