cancel
Showing results for 
Search instead for 
Did you mean: 

Spring and JPA vs Activiti and call activity

zze_one
Champ on-the-rise
Champ on-the-rise
Good morning Activiti,

Guys I need your help (big time) concerning the integration of Activiti with Spring and JPA.

The context.

My bpmn files contain a text-annotation with a customized version number


<textAnnotation id="versionNumber">
   <text>1.10</text>
</textAnnotation>


I have an entity called ProcessDefVersion.


@Entity
@Table(name = "RF_PROCDEF_VERSION")
public class ProcessDefVersion implements java.io.Serializable, Persistable<String> {
   private static final long serialVersionUID = 4348704348958182088L;
   @Id
   @GeneratedValue
   @Column(name = "ID")
   private String id;
   @Column(name = "PROC_ID")
   @Basic
   private String processId;
   @Column(name = "PROC_KEY")
   @Basic
   private String processKey;
   @Column(name = "PROC_VERSION")
   @Basic
   private String processVersion;
}


I also have a repository, an interface that communicates with the corresponding table in the db.


@Repository
@Transactional
public interface ProcessDefRepository extends JpaRepository<ProcessDefVersion, String>, JpaSpecificationExecutor<ProcessDefVersion> {
     
   @Query(value = "select pdv from ProcessDefVersion pdv where pdv.processKey = ?1 order by pdv.processVersion desc")
   List<ProcessDefVersion> findByProcessKey(String processKey);
   
}


When I deploy a workflow I save the corresponding information (process id, key and version) into the RF_PROCDEF_VERSION table.
When I want to start a specific version of the workflow, I search in this table thanks to the key and the version number, the id of the corresponding process I want to instantiate.

So far so good.

My issue comes with the use of sub processes, and, I think (this is where my comprehension is limited), with how the Application Context passes information to the process engine.

The issue :

When calling a sub process, thanks to call activity: My process engine loads a customized ActivityBehaviorFactory, that uses a customized CallActivityBehavior.


   <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
      <property name="dataSource" ref="dataSource" />
       <property name="transactionManager" ref="transactionManager" />
       <property name="databaseSchemaUpdate" value="true" />
       <property name="jobExecutorActivate" value="false" />
       <property name="activityBehaviorFactory" ref="customizedActivityBehaviorFactory" />
   </bean>
   
   <bean id="customizedActivityBehaviorFactory" class="com.yo.poc.util.CustomizedActivityBehaviorFactory">
     </bean>


CustomizedCallActivityBehavior will then try to load a service implementation ProcessDefVersionServiceImp that will use ProcessDefRepository to query for the needed information:
Same trick, a specific version of a main process will start an instance of a specific version of the sub process.
After retrieving what should be the version number of the sub process (this is hard coded so far, if it wasn't I would I face the same issue I'm trying to describe), my application will look in RF_PROCDEF_VERSION for the id of the corresponding sub process I want to instantiate.

The issue:

Before calling my sub process, my spring configuration does its job by scanning for annotations, autowiring the services, the repository.
But when the sub process starts, it doesn't any more.
And I think this is what I have to resolve !

Not finding the solution I had two approaches:

1. I tried adding all the configuration related to jpa in the activiti.cfg.xml. Not helping

2. I started injecting the beans from inside activiti.cfg.xml, and that was not too bad.
My different beans are not null any more and do their job.
But I can't inject an interface. And my repository ProcessDefRepository is an interface, wired in ProcessDefVersionServiceImpl.


   <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
      <property name="dataSource" ref="dataSource" />
       <property name="transactionManager" ref="transactionManager" />
       <property name="databaseSchemaUpdate" value="true" />
       <property name="jobExecutorActivate" value="false" />
       <property name="activityBehaviorFactory" ref="customizedActivityBehaviorFactory" />
   </bean>
   
   <bean id="customizedActivityBehaviorFactory" class="com.yo.poc.util.CustomizedActivityBehaviorFactory">
      <property name="callActivityBehaviour" ref="customizedCallActivityBehavior" />
     </bean>
 
     <bean id="customizedCallActivityBehavior" class="com.yo.poc.util.CustomizedCallActivityBehavior">
        <property name="processDefVersionService" ref="processDefVersionService" />
     </bean>

     <bean id="processDefVersionService" class="com.yo.poc.service.impl.ProcessDefVersionServiceImpl">
     </bean>
     

The more I go into this direction, the more hacky is my implementation. And I really feel like I'm missing something when loading the Process Engine.
When in CustomizedActivityBehaviorFactory, wiring CustomizedCallActivityBehavior with spring annotation should work.
And then, when in CustomizedCallActivityBehavior, wiring ProcessDefVersionServiceImpl should work too …

Could you guys direct me to either a good documentation or a start of a solution ?

Thanks so much
7 REPLIES 7

trademak
Star Contributor
Star Contributor
Could you include the implementation of your CustomizedActivityBehaviorFactory class?

Thanks,

zze_one
Champ on-the-rise
Champ on-the-rise
sure thing.
Thanks for having a look !

zze_one
Champ on-the-rise
Champ on-the-rise
Hi again.

problem solved.
In CustomizedActivityBehaviorFactory, I Autowired my CustomizedCallActivityBehavior.

I also had to Inject the SpringProcessEngineConfiguration in the Rest Controller I was qwering.

In dispatcher-servlet.xml:
<import resource="classpath:activiti.cfg.xml" />

jbarrez
Star Contributor
Star Contributor
It makes sense then it didn't work before 😉

Thanks for posting back the solution to your problem!

zze_one
Champ on-the-rise
Champ on-the-rise
Hi Joram,

I'm attaching my CustomizedActivityBehaviorFactory class.

The purpose of this class is to create a CustomizedCallActivityBehavior bean instead of a CallActivityBehavior to manage call activities.
I'm overriding createCallActivityBehavior(CallActivity callActivity) for this purpose.

This solution works fine but I feel like it's not the best way of doing it.
If tomorrow you guys make changes in CallActivityBehavior, those changes won't be applied to my code. So maintainability is not optimized.

Is there a way to / do you have any suggestion for me to / provide a new CustomizedCallActivityBehavior without overriding the entire method ?

In <code>
@Override
public CallActivityBehavior createCallActivityBehavior(CallActivity callActivity) {
  String expressionRegex = "\\$+\\{+.+\\}";
 
  /**
   * In process-appContext.xml
   * CustomizedCallActivityBehavior's scope is described as 'prototype'
   * in order for each call Activity to have a newly created bean
   * */
  callActivityBehaviour = (CustomizedCallActivityBehavior) appContext.getBean("customizedCallActivityBehavior");
 
     if (StringUtils.isNotEmpty(callActivity.getCalledElement()) && callActivity.getCalledElement().matches(expressionRegex)) {
      callActivityBehaviour.setProcessDefinitionExpression(expressionManager.createExpression(callActivity.getCalledElement()));
     } else {
      callActivityBehaviour.setProcessDefinitonKey(callActivity.getCalledElement());
     }

     for (IOParameter ioParameter : callActivity.getInParameters()) {
       if (StringUtils.isNotEmpty(ioParameter.getSourceExpression())) {
         Expression expression = expressionManager.createExpression(ioParameter.getSourceExpression().trim());
         callActivityBehaviour.addDataInputAssociation(new SimpleDataInputAssociation(expression, ioParameter.getTarget()));
       } else {
         callActivityBehaviour.addDataInputAssociation(new SimpleDataInputAssociation(ioParameter.getSource(), ioParameter.getTarget()));
       }
     }
    
     for (IOParameter ioParameter : callActivity.getOutParameters()) {
       if (StringUtils.isNotEmpty(ioParameter.getSourceExpression())) {
         Expression expression = expressionManager.createExpression(ioParameter.getSourceExpression().trim());
         callActivityBehaviour.addDataOutputAssociation(new MessageImplicitDataOutputAssociation(ioParameter.getTarget(), expression));
       } else {
         callActivityBehaviour.addDataOutputAssociation(new MessageImplicitDataOutputAssociation(ioParameter.getTarget(), ioParameter.getSource()));
       }
     }
 
     return callActivityBehaviour;
}
</code>
The only thing that changes is
<code>
       callActivityBehaviour = (CustomizedCallActivityBehavior) appContext.getBean("customizedCallActivityBehavior");
 
     if (StringUtils.isNotEmpty(callActivity.getCalledElement()) && callActivity.getCalledElement().matches(expressionRegex)) {
      callActivityBehaviour.setProcessDefinitionExpression(expressionManager.createExpression(callActivity.getCalledElement()));
     } else {
      callActivityBehaviour.setProcessDefinitonKey(callActivity.getCalledElement());
     }
</code>

frederikherema1
Star Contributor
Star Contributor
I don't see any way to make your code less tied to the implementation right now, other than changing activiti-code…

zze_one
Champ on-the-rise
Champ on-the-rise
Thanks Frederik