11 Replies Latest reply on Nov 20, 2009 4:49 AM by fabiomsouto

    [JBPM4] Is there a way to create subprocess programatically?

      Hi,
      I have a requirement to create "n" number of sub process where "n" is not fixed and can be determined only in run time. I am trying to find out if there is a way to do the same in JBPM4. I have seen many solution for JBPM3 but not for JBPM4. Please help this is urgent!!!

        • 1. Re: [JBPM4] Is there a way to create subprocess programatica

          Hi

          Do you find a solution for the issue?

          Thanks
          ZT

          • 3. Re: [JBPM4] Is there a way to create subprocess programatica

            Yes I could do it. Give me some time and I will post the solution. If you want give me your email id I will send you the solution directly...

            • 4. Re: [JBPM4] Is there a way to create subprocess programatica

              Thanks Sushant, my email id is
              My e-mail : zengping.tian at gs.com

              • 5. Re: [JBPM4] Is there a way to create subprocess programatica

                Hi Sushant, could you please help to shed some lights on the issue? it's not necessary to be all sing all dancing, just point us to the right direction.....appreciate your help,

                • 6. Re: [JBPM4] Is there a way to create subprocess programatica

                  Hi ZP,

                  Really sorry for not sending you this earlier. I had the files ready but somehow just couldn't send it to you. Here is the outline of what I did. Where-ever I wanted the fork functionality I have created a simple node and added the following as the handler "CustomNodeHandlerFork.java" and where ever I needed join I added "CustomNodeHandlerJoin.java" as the handler. Above 2 handler are the modified form of the "ForkActivity" and "JoinActivity" class of JBPMv4. Below is the code:

                  =================================================
                  import java.util.ArrayList;
                  import java.util.List;
                  
                  import org.jbpm.api.Execution;
                  import org.jbpm.api.activity.ActivityExecution;
                  import org.jbpm.jpdl.internal.activity.JpdlActivity;
                  import org.jbpm.pvm.internal.model.Activity;
                  import org.jbpm.pvm.internal.model.ActivityImpl;
                  import org.jbpm.pvm.internal.model.Condition;
                  import org.jbpm.pvm.internal.model.ExecutionImpl;
                  import org.jbpm.pvm.internal.model.Transition;
                  import org.jbpm.pvm.internal.model.TransitionImpl;
                  
                  
                  public class CustomNodeHandler extends JpdlActivity {
                  
                   private static final long serialVersionUID = 1L;
                  
                   public void execute(ActivityExecution execution) {
                   execute((ExecutionImpl)execution);
                   }
                  
                   public void execute(ExecutionImpl execution) {
                   Activity activity = execution.getActivity();
                   List<ExecutionImpl> listOfExecution = new ArrayList();
                   int noOfTrantisions = Integer.parseInt(JBPMTransientStorage.getTransientVariable(execution.getId(),"noOfTrantisionsFromNode").toString());
                   // evaluate the conditions and find the transitions that should be forked
                   List<Transition> forkingTransitions = new ArrayList<Transition>();
                   for(int i=0 ; i < noOfTrantisions-1 ; i++) {
                   forkingTransitions.add(activity.getOutgoingTransitions().get(0));
                   }
                   List<Transition> outgoingTransitions = activity.getOutgoingTransitions();
                   for (Transition transition: outgoingTransitions) {
                   Condition condition = transition.getCondition();
                   if ( (condition==null)
                   || (condition.evaluate(execution))
                   ) {
                   forkingTransitions.add(transition);
                   }
                   }
                  
                   // if no outgoing transitions should be forked,
                   if (forkingTransitions.size()==0) {
                   // end this execution
                   execution.end();
                  
                   // if there is exactly 1 transition to be taken, just use the incoming execution
                   } else if (forkingTransitions.size()==1) {
                   execution.take(forkingTransitions.get(0));
                  
                   // if there are more transitions
                   } else {
                   ExecutionImpl concurrentRoot = null;
                   if (Execution.STATE_ACTIVE_ROOT.equals(execution.getState())) {
                   concurrentRoot = execution;
                   execution.setState(Execution.STATE_INACTIVE_CONCURRENT_ROOT);
                   execution.setActivity(null);
                   } else if (Execution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
                   concurrentRoot = execution.getParent();
                   }
                  
                   for (Transition transition: forkingTransitions) {
                   // launch a concurrent path of execution
                   String childExecutionName = transition.getName();
                   ExecutionImpl concurrentExecution = concurrentRoot.createExecution(childExecutionName);
                   concurrentExecution.setActivity(activity);
                   concurrentExecution.setState(Execution.STATE_ACTIVE_CONCURRENT);
                   concurrentExecution.take(transition);
                   listOfExecution.add(concurrentExecution.getSubProcessInstance());
                   if (concurrentRoot.isEnded()) {
                   break;
                   }
                   }
                   JBPMTransientStorage.setTransientVariable(execution.getId(),"LIST_OF_EXECUTIONS", listOfExecution);
                   }
                   }
                  }
                  =================================================
                  import java.util.ArrayList;
                  import java.util.List;
                  
                  import org.hibernate.LockMode;
                  import org.hibernate.Session;
                  import org.jbpm.api.Execution;
                  import org.jbpm.api.JbpmException;
                  import org.jbpm.api.activity.ActivityExecution;
                  import org.jbpm.jpdl.internal.activity.JpdlActivity;
                  import org.jbpm.pvm.internal.env.Environment;
                  import org.jbpm.pvm.internal.model.Activity;
                  import org.jbpm.pvm.internal.model.ExecutionImpl;
                  import org.jbpm.pvm.internal.model.Transition;
                  
                  
                  public class CustomNodeHandlerJoin extends JpdlActivity {
                  
                   private static final long serialVersionUID = 1L;
                  
                   int multiplicity = -1;
                   LockMode lockMode = LockMode.UPGRADE;
                  
                   public void execute(ActivityExecution execution) {
                   execute((ExecutionImpl)execution);
                   }
                  
                   public void execute(ExecutionImpl execution) {
                   Activity activity = execution.getActivity();
                   // if this is a single, non concurrent root
                   if (Execution.STATE_ACTIVE_ROOT.equals(execution.getState())) {
                   // just pass through
                   Transition transition = activity.getDefaultOutgoingTransition();
                   if (transition==null) {
                   throw new JbpmException("join must have an outgoing transition");
                   }
                   execution.take(transition);
                   } else if (Execution.STATE_ACTIVE_CONCURRENT.equals(execution.getState())) {
                   // force version increment in the parent execution
                   Session session = Environment.getFromCurrent(Session.class);
                   session.lock(execution.getParent(), lockMode);
                   execution.setState(Execution.STATE_INACTIVE_JOIN);
                   execution.waitForSignal();
                   ExecutionImpl concurrentRoot = execution.getParent();
                   List<ExecutionImpl> joinedExecutions = getJoinedExecutions(concurrentRoot, activity);
                   if (isComplete(joinedExecutions, activity, concurrentRoot)) {
                   endJoinedExecutions(joinedExecutions);
                   ExecutionImpl outgoingExecution = null;
                   if (concurrentRoot.getExecutions().size()==0) {
                   outgoingExecution = concurrentRoot;
                   outgoingExecution.setState(Execution.STATE_ACTIVE_ROOT);
                   } else {
                   outgoingExecution = concurrentRoot.createExecution();
                   outgoingExecution.setState(Execution.STATE_ACTIVE_CONCURRENT);
                   }
                   execution.setActivity(activity, outgoingExecution);
                   Transition transition = activity.getDefaultOutgoingTransition();
                   if (transition==null) {
                   throw new JbpmException("join must have an outgoing transition");
                   }
                   outgoingExecution.take(transition);
                   }
                   } else {
                   throw new JbpmException("invalid execution state");
                   }
                   }
                  
                   protected boolean isComplete(List<ExecutionImpl> joinedExecutions, Activity activity, ExecutionImpl concurrentRoot) {
                   int nbrOfExecutionsToJoin = multiplicity;
                   if (multiplicity==-1) {
                   nbrOfExecutionsToJoin = getActiveExecutions(concurrentRoot).size();
                   }
                   return joinedExecutions.size()==nbrOfExecutionsToJoin;
                   }
                  
                   protected List<ExecutionImpl> getJoinedExecutions(ExecutionImpl concurrentRoot, Activity activity) {
                   List<ExecutionImpl> joinedExecutions = new ArrayList<ExecutionImpl>();
                   List concurrentExecutions = (List)concurrentRoot.getExecutions();
                   for (ExecutionImpl concurrentExecution: (List<ExecutionImpl>)concurrentExecutions) {
                   if ( (Execution.STATE_INACTIVE_JOIN.equals(concurrentExecution.getState()))
                   && (concurrentExecution.getActivity()==activity)
                   ) {
                   joinedExecutions.add(concurrentExecution);
                   }
                   }
                   return joinedExecutions;
                   }
                   protected List<ExecutionImpl> getActiveExecutions(ExecutionImpl concurrentRoot) {
                   List<ExecutionImpl> activeExecutions = new ArrayList<ExecutionImpl>();
                   List concurrentExecutions = (List)concurrentRoot.getExecutions();
                   for (ExecutionImpl concurrentExecution: (List<ExecutionImpl>)concurrentExecutions) {
                   // if ( !concurrentExecution.isEnded()) {
                   activeExecutions.add(concurrentExecution);
                   //}
                   }
                   return activeExecutions;
                   }
                   protected void endJoinedExecutions(List<ExecutionImpl> joinedExecutions) {
                   for (ExecutionImpl joinedExecution: joinedExecutions) {
                   joinedExecution.end();
                   }
                   }
                  
                   public void setMultiplicity(int multiplicity) {
                   this.multiplicity = multiplicity;
                   }
                   public void setLockMode(LockMode lockMode) {
                   this.lockMode = lockMode;
                   }
                  }
                  =================================================
                  import java.util.HashMap;
                  import java.util.Map;
                  
                  public class JBPMTransientStorage {
                   private static ThreadLocal storage = new ThreadLocal();
                   //private static Map executionContextMap = new HashMap();
                  
                   public static void setTransientVariable(String executionId,String key,Object value){
                   Object obj = storage.get();
                   System.out.println("The object in thread local == "+obj+" while setting key == "+key);
                   Map executionContextMap = (null != obj?(Map)obj:new HashMap());
                  
                   if(executionContextMap.containsKey(executionId)){
                   HashMap contextSpecificMap = (HashMap) executionContextMap.get(executionId);
                   contextSpecificMap.put(key, value);
                   }else{
                   Map contextSpecificMap = new HashMap();
                   contextSpecificMap.put(key, value);
                   executionContextMap.put(executionId, contextSpecificMap);
                   }
                   storage.set(null);
                   storage.set(executionContextMap);
                  
                   }
                  
                   public static Object getTransientVariable(String executionId,String key){
                   Map executionContextMap = (Map) storage.get();
                   Object value = ((Map)executionContextMap.get(executionId)).get(key);
                   return value;
                   }
                  
                   public static void clear(){
                   storage.set(null);
                   }
                  }
                  =================================================


                  • 7. Re: [JBPM4] Is there a way to create subprocess programatica

                    Hi Sushant, thanks for your post. It's very helpful. We did the similar experiments. However your implementation is more thorough and the way to deal variable is better( just use a hardcoded instance variable for "noOfTrantisionsFromNode", and another one for "LIST_OF_EXECUTIONS")

                    We'll look into your approach in details and will come back if any questions,

                    Thanks
                    Zengping

                    • 8. Re: [JBPM4] Is there a way to create subprocess programatica

                      Hi there,

                      Thanks for your solution. Can you provide an example usage of your solution.

                      1) I am not sure how you set the noOfTrantisionsFromNode value in JBPMTransientStorage. Can we call static methods from the java activity?

                      2) Do we just define the nodes we want forked as a transition in the custom node?

                      Thanks

                      • 9. Re: [JBPM4] Is there a way to create subprocess programatica

                        @Priyesh

                        JBPMTransientStorage is a substitute for the transient variable . I used it bcoz transient variables were not working as expected in my case. So if you want you can use the transient variable as well. noOfTrantisionsFromNode variable is getting set from my application based on the criteria which determines how many parallel subporcesses I want.

                        I did not get your point 2 can you please elaborate it bit more?

                        • 10. Re: [JBPM4] Is there a way to create subprocess programatica

                          I managed to get it to work.

                          Your solution is very elegant :-)

                          Thanks

                          • 11. Re: [JBPM4] Is there a way to create subprocess programatica
                            fabiomsouto

                            Does this solution solve the JIRA posted on the beggining?
                            Anyone could give an example?

                            Thank you.