14 Replies Latest reply on Oct 7, 2005 4:26 PM by brittm

    Reassign Swimlanes NullpointerException

    julian_k

      I have predefined swimlanes that need to have the assignment expression dynamically set on process start. Is the best way the following:

      ProcessInstance processInstance = new ProcessInstance(processDefinition);
      Token token = processInstance.getRootToken();
      token.signal();
      SwimlaneInstance swimlane = processInstance.getTaskMgmtInstance().getSwimlaneInstance("swimalneid");
      swimlane.setActorId("new dynamic expression");
      


      Each time I try this I get a NullPointerException.

      Thanks,
      Julian

        • 1. Re: Reassign Swimlanes NullpointerException
          icyjamie

          I hope your swimlane really is named : "swimalneid" and not "swimlaneid"

          • 2. Re: Reassign Swimlanes NullpointerException
            kukeltje

            or daleniwims

            • 3. Re: Reassign Swimlanes NullpointerException
              icyjamie

              well,

              Indeed you get a null, but if you want to do it "dynamically", the first time you really want to create a taskInstance, the swimLane will be initialized. If you "hard-assign" somebody to the taskInstance (setActorId), the swimlane will inherit this as well. So next tasks will get the actorId.
              IF you really want to do "dynamically", you need an assignmentHandler on the task or the swimlane.
              Swimlanes are conceptual (the swimlane does not impose anything, it kinda learns).

              • 4. Re: Reassign Swimlanes NullpointerException
                julian_k

                Sorry for the confusion, but I posted very early in the morning. My process definition starts as follows:

                <process-definition name="Test Process">
                 <swimlane name="swimlaneid"/>
                 <start-state name="Start State">
                 <task swimlane="swimlaneid">
                 <controller>
                 .....
                


                And yes, the new swimlane id would be coming from a variable (not hard coded).

                Thanks,
                Julian

                • 5. Re: Reassign Swimlanes NullpointerException
                  brittm

                  Julian,

                  If I am not mistaken, your swimlane instance needs to be initialized--by default swimlane instances are not initialized until the first task that is associated with them is created. I initialize all my swimlanes up front for some of the same reasons you wish to. The code I use is

                  Map swimlanes = pd.getTaskMgmtDefinition().getSwimlanes();
                   Iterator itr = swimlanes.keySet().iterator();
                   while(itr.hasNext()) {
                   Swimlane swimlane = (Swimlane)swimlanes.get(itr.next());
                   SwimlaneInstance swi = pi.getTaskMgmtInstance().getInitializedSwimlaneInstance(new ExecutionContext(pi.getRootToken()), swimlane);
                   }

                  Notice the .getInitializedSwimlaneInstance() method.

                  Hope this helps.

                  -Britt

                  • 6. Re: Reassign Swimlanes NullpointerException
                    julian_k

                    Thanks Britt, it worked beautifully.

                    Julian

                    • 7. Re: Reassign Swimlanes NullpointerException
                      julian_k

                      I read in the documentation that any task that is assigned to a swimlane should not have an actorId set. However, when trying to retrieve the taskinstances assigned to the newly instantiated swimlane, no taskinstances are returned. I then set the actorId on the TaskInstance and had no problem. Will this cause problems?

                      btw, I am using the findTaskInstances(...) method as follows:

                      session.getTaskMgmtSession().findTaskInstances(newSwimlaneId);
                      


                      where newSwimlaneId is the dynamically set swimlaneid referenced in the above posts.

                      Thanks again,
                      Julian

                      • 8. Re: Reassign Swimlanes NullpointerException
                        julian_k

                        ok, so I tried this and this works as well...

                        ((TaskInstance)processInstance.getTaskMgmtInstance().getTaskInstances().iterator().next()).assign(executionContext);
                        


                        Which would be the preferred use case?

                        Thanks,
                        Julian

                        • 9. Re: Reassign Swimlanes NullpointerException
                          brittm

                          Take a look at my complete process creation code and see if it serves your needs.

                          ProcessDefinition pd = graphSession.findLatestProcessDefinition(procDefName);
                           jbpmSession.beginTransaction();
                           ProcessInstance pi = new ProcessInstance(pd);
                          
                           //This ensures that all swimlanes are initialized and available for reporting and reassignment immediately.
                           //If we don't initialize them up front, they won't be created until a task calls for them.
                           Map swimlanes = pd.getTaskMgmtDefinition().getSwimlanes();
                           Iterator itr = swimlanes.keySet().iterator();
                           while(itr.hasNext()) {
                           Swimlane swimlane = (Swimlane)swimlanes.get(itr.next());
                           SwimlaneInstance swi = pi.getTaskMgmtInstance().getInitializedSwimlaneInstance(new ExecutionContext(pi.getRootToken()), swimlane);
                           }
                          
                           //If this process definition defines a startTask in the StartState, we'll have to
                           // explicitly create it--it won't be created otherwise...
                           if (pd.getTaskMgmtDefinition().getStartTask() != null) {
                           //The start task will be assigned to the currently authenticated user (as understood by Jbpm), and that
                           // user WILL overwrite any default swimlane assignment.
                           TaskInstance startTask = pi.getTaskMgmtInstance().createStartTaskInstance();
                           //Unless our application's authentication scheme has been tied into Jbpm's, we
                           // should manually assign the startTask to a user as well as manually set any swimlane
                           // referenced by this task, otherwise both will be null.
                           startTask.setActorId(user);
                           if(startTask.getSwimlaneInstance() != null) {
                           startTask.getSwimlaneInstance().setActorId(user);
                           }
                           }else {
                           //If our new process doesn't have a startTask defined, we may want to get the new process rolling...
                           // Although, some executions may want to set process variables, etc. before
                           // continuing, in which case they should do such and signal for themselves.
                           //pi.signal();
                           }
                           graphSession.saveProcessInstance(pi);
                           jbpmSession.commitTransaction();

                          This is a work in progress, but so far, this code has worked reliably for me. Can anyone find flaws in this process instantiation?

                          Thanks,
                          Britt

                          • 10. Re: Reassign Swimlanes NullpointerException
                            julian_k

                            Would you mind posting your process deifinition as well?

                            Thanks,
                            Julian

                            • 11. Re: Reassign Swimlanes NullpointerException
                              brittm

                              This is a process definition that I use for testing purposes...

                              <?xml version="1.0" encoding="UTF-8"?>
                              <process-definition name="Test">
                               <!-- SWIMLANES -->
                               <swimlane name="testLane1">
                               <assignment class="com.ntc.workflow.GenericAssignmentHandler">
                               <actorId>testActor1</actorId>
                               <groupId>IT Department</groupId>
                               </assignment>
                               </swimlane>
                              
                               <swimlane name="testLane2">
                               <assignment class="com.ntc.workflow.GenericAssignmentHandler">
                               <actorId>testActor2</actorId>
                               <groupId>IT Developers</groupId>
                               </assignment>
                               </swimlane>
                              
                               <!-- NODES -->
                               <start-state name="START" >
                               <task swimlane="testLane1" />
                               <transition to="EMPTY_NODE"/>
                               </start-state>
                              
                               <!-- a simple 'node' should have one associated 'action' element that is responsible for doing
                               whatever and signalling a transition. If no action is present, the node will immediately pass
                               through to its default transition. Think of the simple node by default as a 'milestone'
                               in the workflow -->
                               <node name="EMPTY_NODE">
                               <!--<action class="com.ntc..." />-->
                               <transition to="ASSIGNED_TASK-NODE"/>
                               </node>
                              
                               <!-- a simple 'state' node should have an 'action' element to notify external processes.
                               This external process would be responsible for signalling the state to continue. A simple
                               'state' will not transition automatically.
                               <state name="EMPTY_STATE">
                               <transition to="ASSIGNED_TASK-NODE"/>
                               </state>-->
                              
                               <task-node name="ASSIGNED_TASK-NODE">
                               <task name="assigned_task-node_task" swimlane="testLane1">
                               <!--<assignment class="com.ntc.workflow.GenericAssignmentHandler">
                               <actorId>brittm</actorId>
                               </assignment>-->
                               </task>
                               <transition to="MULTI_TASK_NODE"/>
                               </task-node>
                              
                               <!-- A task-node can automatically create the defined tasks on node enter, or can allow an
                               actionHandler to do it. If an actionHandler is used, tasks can be specified outside of
                               the task node and referenced in the API by name. The 'signal' property defines
                               how/when task status signals the default transition -->
                               <task-node name="MULTI_TASK_NODE" create-tasks="true" signal="never">
                               <task name="MULTI_TASK_NODE_task1" blocking="no" swimlane="testLane1" />
                               <task name="MULTI_TASK_NODE_task2" blocking="yes" swimlane="testLane1" /><!-- blocking means a node transition can't be taken without completion of this task-->
                               <task name="MULTI_TASK_NODE_task3" blocking="yes" swimlane="testLane1" />
                               <transition name="done" to="PRIORITY_TASK_NODE"/>
                               </task-node>
                              
                               <task-node name="PRIORITY_TASK_NODE" create-tasks="true" signal="never">
                               <task name="PRIORITY_TASK_NODE_task1" blocking="no" swimlane="testLane2" priority="2"/>
                               <task name="PRIORITY_TASK_NODE_task2" blocking="yes" swimlane="testLane2" priority="1"/>
                               <task name="PRIORITY_TASK_NODE_task3" blocking="yes" swimlane="testLane2" priority="3"/>
                               <transition name="drop" to="action/DROP"/>
                               <transition name="throw" to="action/THROW"/>
                               </task-node>
                              
                               <super-state name="action">
                               <task-node name="DROP">
                               <task name="drop_task" swimlane="testLane2" />
                               <transition name="done" to="../END"/>
                               </task-node>
                               <task-node name="THROW">
                               <task name="keep_task" swimlane="testLane2" />
                               <transition name="done" to="../END"/>
                               </task-node>
                               </super-state>
                              
                               <!-- END-STATE -->
                               <end-state name="END" />
                              </process-definition>


                              • 12. Re: Reassign Swimlanes NullpointerException
                                brittm

                                I've discoverd that initializing swimlaneInstances at process creation works great as long as you don't want to assign PooledActors. The following code initializes all swimlanes, and while actor assignments are recorded to the database as expected, any configured PooledActors are not persisted to the database.

                                ...
                                Map swimlanes = pd.getTaskMgmtDefinition().getSwimlanes();
                                 Iterator itr = swimlanes.keySet().iterator();
                                 while(itr.hasNext()) {
                                 Swimlane swimlane = (Swimlane)swimlanes.get(itr.next());
                                 SwimlaneInstance swi = pi.getTaskMgmtInstance().getInitializedSwimlaneInstance(new ExecutionContext(pi.getRootToken()), swimlane);
                                //at this point, swi contains both actors and PooledActors that were specified in configuration and populated with an appropriate AssignmentHandler
                                 }
                                graphSession.saveProcessInstance(pi);
                                jbpmSession.commitTransaction();

                                Subsequent peeking into the database reveals that actorId is set for the swimlaneInstance, but no PooledActors were recorded (jbpm_taskactorpool is empty).

                                PooledActors ARE assigned and stored when the above code fragment is not used, and swimlaneInstances are created by default when their first associated task is created.

                                I'm going to try to figure out where to put this in JIRA--there are already a couple of "similar, but not identical" swimlane issue entries.

                                -Britt

                                • 13. Re: Reassign Swimlanes NullpointerException
                                  brittm
                                  • 14. Re: Reassign Swimlanes NullpointerException
                                    brittm

                                    The problem with PooledActors is solved with a hack to my code example.

                                    //This ensures that all swimlanes are initialized and available for reporting and reassignment immediately.
                                     //If we don't initialize them up front, they won't be created until a task calls for them.
                                     Map swimlanes = pd.getTaskMgmtDefinition().getSwimlanes();
                                     Iterator itr = swimlanes.keySet().iterator();
                                     while(itr.hasNext()) {
                                     Swimlane swimlane = (Swimlane)swimlanes.get(itr.next());
                                     SwimlaneInstance swi = pi.getTaskMgmtInstance().getInitializedSwimlaneInstance(new ExecutionContext(pi.getRootToken()), swimlane);
                                    
                                     //We have to do this cause it doesn't automatically happen when we call
                                     // swimlaneInstance.setPooledActors()
                                     Set pooledActors = swi.getPooledActors();
                                     Iterator paItr = pooledActors.iterator();
                                     while(paItr.hasNext()) {
                                     ( (PooledActor)paItr.next() ).setSwimlaneInstance(swi);
                                     }
                                     }


                                    -Britt