11 Replies Latest reply on Jan 24, 2006 11:30 AM by jakajaksic

    RuntimeAction? What is it for?

    dharraj

      Hello,

      I am trying to capture events (e.g., node enter). Is RuntimeAction the correct class to use. Please let me know how I can do this.

      Thanks
      Raj

        • 1. Re: RuntimeAction? What is it for?
          aguizar

          Runtime actions associate some event of a graph element with an action, for a particular process instance.
          Given the following process definition:

          <process-definition>
           <action name='gotocheetahs' class='com.secret.LetsDoItSneeky'/>
          <start-state name="start">
           <transition name="tr1" to="node1"></transition>
           </start-state>
           <end-state name="end"></end-state>
           <node name="node1">
           <event type="node-enter"/>
           <transition name="tr1" to="end"/>
           </node>
          </process-definition>

          You can dinamically associate the gotocheetahs action with the node-enter event of node1 as follows:
          ProcessInstance processInstance = ...;
          ProcessDefinition processDefinition = processInstance.getProcessDefinition();
          Node node1 = processDefinition.getNode("node1");
          Event event = node1.getEvent("node-enter");
          Action action = processDefinition.getAction("gotocheetahs");
          processInstance.addRuntimeAction(new RuntimeAction(event, action));

          Note that both the event and the action are specified in the process definition. Only the association can be added at runtime, and then for a specific process instance.

          • 2. Re: RuntimeAction? What is it for?
            dharraj

            Hello Alejandro,

            Thanks for your help. Well, I am trying to write a generic (specific to our need) class that will execute a given process definition. However, before starting the process definition, it needs to add some actions to the process instance (or definition), such as an action that is to be performed for each node-enter event. It will be a lot for our analyst to have to add these into the processdefinition.xml. Also, we may have to add other actions later on and this will mean changing all process definitions, which as you can imagine will be a lot of work.

            From you reply it seems that the action has to be part of the original process definition file. What is a good way to manipulate the process definition (note that the modified process definition shall not overwrite original process definition).

            Again, thanks for you help.
            Raj

            • 3. Re: RuntimeAction? What is it for?
              dharraj

              Hello,

              Why does the following not work:

              ProcessDefinition pd = ..
              Node node=pd.getNode("start"); // root node.
              String [] events = node.getSupportedEventTypes(); // has node-leave event in here
              java.util.Map Events = node.getEvents(); // returns null!!!!

              What I am trying to do is add a new action to ProcessInstance and than add a new runtime action with that action on the root node's node-leave event. Is this prossible to do?

              Raj


              • 4. Re: RuntimeAction? What is it for?
                aguizar

                You have to declare the events of your interest in the corresponding graph elements.

                <node name="node1">
                 <!-- this event is interesting, capture it -->
                 <event type="node-enter"/>
                 <transition name="tr1" to="end"/>
                </node>

                This may seem weird, but it makes some sense when you consider it prevents the creation of unnecessary instances of class org.jbpm.graph.def.Event.

                • 5. Re: RuntimeAction? What is it for?
                  aguizar

                   

                  What is a good way to manipulate the process definition

                  The versioning mechanism. I'm thinking of another technique which doesn't involve messing around with the process definition. Try creating runtime actions like this:
                  RuntimeAction createRuntimeAction(GraphElement graphElement,
                   String eventType, String actionHandlerClassName) {
                   Event event = graphElement.getEvent(eventType);
                   Action action = new Action(new Delegation(actionHandlerClassName));
                   return new RuntimeAction(event, action);
                   }

                  Here, the action isn't defined in the process definition. You truly create it at runtime. I'm anticipating that you might run into persistence problems. Actions are supposed to be definition objects and, as such, the RuntimeAction doesn't have to cascade persistence operations to it. Anyway, the worst thing that can happen is that you have to save the Action explicitly. Let me know.

                  • 6. Re: RuntimeAction? What is it for?
                    ralfoeldi

                    Hi Alex,

                    you are right about the persistence problems.

                    What I get is

                    11:21:43,602 ERROR [jbpm.db.GraphSession] org.hibernate.HibernateException: instance not of expected entity type: org.jbpm.graph.def.Action


                    when I try to persist a ProcessInstance that I added a RuntimeAction to

                    
                     public final void execute( ExecutionContext executionContext )
                     throws Exception
                    {
                     ...
                    
                     Action action = new ActorWriteAction();
                    
                     RuntimeAction runtimeAction = new RuntimeAction(leaveNodeEvent, action);
                    
                     executionContext.getProcessInstance().addRuntimeAction( runtimeAction );
                    
                     ....
                    
                     // this is where the application spec execute() is called and
                     // ultimately token.signal()
                     actorExecute(executionContext);
                    }
                    


                    As some other people have mentioned here real runtime, i.e. non persisted Actions would be a big benefit.

                    What I am trying to do is build a framework on top of JBPM that other, more application oriented developers, can use to build very specific actions. This framework takes care of some dataaccess and transaction management 'magic' that I want to hide from the application oriented developers, e.g. I have a AbstractAsyncAction class that can be extended and does all the JMS and tx plumbing.

                    There are situations where I have to intercept leaving a node after the application specific execute() method has finished. (e.g. saving data that was changed by the Action) The only way of doing this is by registering a callback by way of adding a Action to the leaving node event.

                    What I don't want to do is realy on all developers registering all these details in every workflow definition - it just wouldn't work. The intention is basically the same as yours for business consultants that shouldn't see the implementation details in a graph rep of the workflow.

                    With the same reasoning I would like to keep all the nitty gritty details from the developers.

                    Any comments / suggestions?

                    Cheerfull greetings from snow white (and cooold) Switzerland

                    Rainer

                    • 7. Re: RuntimeAction? What is it for?
                      ralfoeldi

                      I even get a

                      13:32:43,764 ERROR [jbpm.db.LoggingSession] org.hibernate.HibernateException: instance not of expected entity type: org.jbpm.graph.def.Action


                      when I remove the RuntimeAction from the processInstance?!

                      
                       public final void execute( ExecutionContext executionContext )
                       throws Exception
                       {
                       RuntimeAction runtimeAction = null;
                      
                       try
                       {
                       Node node = executionContext.getNode();
                      
                       Event leaveNodeEvent = node.getEvent(Event.EVENTTYPE_NODE_LEAVE);
                      
                       if (leaveNodeEvent == null)
                       {
                       leaveNodeEvent = new Event(node, Event.EVENTTYPE_NODE_LEAVE);
                      
                       node.addEvent(leaveNodeEvent);
                       }
                      
                       Action action = new ActorWriteAction();
                      
                       runtimeAction = new RuntimeAction(leaveNodeEvent, action);
                      
                       executionContext.getProcessInstance().addRuntimeAction(runtimeAction);
                      
                       // TODO We still need persistence of written values if the node waits...
                       // i.e. no signal and no node leave event
                      
                       actorExecute(executionContext);
                       }
                       finally
                       {
                       if (runtimeAction != null)
                       {
                       executionContext.getProcessInstance()
                       .removeRuntimeAction(runtimeAction);
                       }
                       }
                       }
                      


                      Rainer

                      • 8. Re: RuntimeAction? What is it for?
                        ralfoeldi

                        well... it does work. Kind of. If you kick out the ActionLog before persisting you can add a transient RuntimeAction, but this results in code like:

                        finally
                         {
                         ProcessInstance processInstance = executionContext.getProcessInstance();
                         LoggingInstance loggingInstance = processInstance.getLoggingInstance();
                        
                         if (runtimeAction != null)
                         {
                         Action action = runtimeAction.getAction();
                        
                         processInstance.removeRuntimeAction(runtimeAction);
                        
                         List logs = loggingInstance.getLogs();
                        
                         List filteredLogs = LoggingInstance.getLogs( logs, ActionLog.class );
                        
                         for ( int i = 0; i < filteredLogs.size(); i++ )
                         {
                         ActionLog actionLog = ( ActionLog ) filteredLogs.get( i );
                        
                         if ( actionLog.getAction().equals( action ) )
                         {
                         logs.remove( actionLog );
                         }
                         }
                         }
                         }
                        


                        So I can get my callbacks this way, but I'm still interested if JBPM couldn't support this natively.

                        Anybody else need something like this?

                        Rainer

                        • 9. Re: RuntimeAction? What is it for?
                          aguizar

                          You are getting the "instance not of expected entity type" error because you are using an instance of an unmapped subclass. You might prefer to use a plain Action with a delegation, as shown in my Oct 13 post.

                          I understand that the bottomline of your question is the option to have transient runtime actions. I can't get my mind around that, tough. You'd need to register them again every time you resume the execution of a process instance. Is that what you really want?

                          • 10. Re: RuntimeAction? What is it for?
                            ralfoeldi

                            Hi Alex,

                            thanks for the reply (on a weekend).

                            The persistence problems are solved and ok. As JBPM expects that everything exists in the db it was sure to crash somewhere. It took just it little bit of tracing to figure out that I have to remove the logs that reference the Action - it just wasn't the obvious thing to look for.

                            The bottom-bottom line is what Raj mentioned in the initial post of this thread: capturing events.

                            I want to be able to write a base class that can "do magic" before and after the node executes. (Why I want to do this is mentioned in previous posts, basically low level data access / tx management hidden from other developers.)

                            The "do magic after" should execute
                            - if the node is left by way of transition (before the next node is reached)
                            - if the node is a wait state and the thread just returns without signalling

                            The only way I see to achieve this is by registering Actions on the Node-Leave event, remembering if a Node-Leave was fired and checking this when the Thread returns (see code).

                            From my point of view the "TransientActions" are very lightweight object with a very short lifetime. Should JBPM implement them it would be even more lightweight than in my implementation :-)

                            My solution works for me and I guess JBPM won't have TransientActions till we go live here (if at all), but I do have the impression I'm not the only one that could use TransientActions.

                            Rainer

                            P.S.: By the way: great project. I just finished a big project for another client using JBPM 2.0.

                            public abstract class AbstractActorAction implements ActionHandler
                            {
                            
                            ...
                            
                             public final void execute( ExecutionContext executionContext )
                             throws Exception
                             {
                             _executionContext = executionContext;
                            
                             try
                             {
                             addTransientRuntimeAction(new RemeberLeaveSignalAction(),
                             EVENT_TYPE_LEAVE_OR_WAIT);
                            
                             // pseudo
                             // addTransientRuntimeAction(new DoAfterMagic(),
                             // EVENT_TYPE_LEAVE_OR_WAIT);
                            
                             // "do before MAGIC MAGIC MAGIC MAGIC MAGIC MAGIC"
                            
                             actorExecute(executionContext);
                            
                             // "do after MAGIC MAGIC MAGIC MAGIC MAGIC MAGIC"
                             // RemeberLeaveSignalAction will have set this to null if already executed
                             if ((_leaveOrWaitActions != null) && (!_leaveOrWaitActions.isEmpty()))
                             {
                             Iterator leaveOrWaitActions = _leaveOrWaitActions.iterator();
                            
                             while (leaveOrWaitActions.hasNext())
                             {
                             RuntimeAction runtimeAction = (RuntimeAction) leaveOrWaitActions
                             .next();
                            
                             runtimeAction.getAction().execute(executionContext);
                             }
                             }
                             }
                             finally
                             {
                             cleanupTransientRuntimeActions();
                             }
                             }
                            
                            ...
                            
                            }
                            


                            • 11. Re: RuntimeAction? What is it for?
                              jakajaksic

                              Yes, something like that would definitely be very useful. Not necessarily in the form of transient runtime actions (which would have to be registered every time before triggering process execution), it could also be in the form of process events with general, application scope.

                              For example, we would like to notify each user of every task they receive, by email. Currently every task in every process definition needs to include an on-create event with the notify action, which is an unnecessary complication of process definitions and a waste of database and server resources.

                              We would also like to implement monitoring of process execution, for statistics and to prevent process cycles and such. For that, we would again need to manually include the monitoring action in every process node. This is all unnecessary work, which business analysts definitely should not be bothered with.

                              A possible solution would be to have a declaration of general events somewhere alongside JBPM configuration files, so that they would only need to be configured in one place and then automatically executed each time an event of the specified type is triggered.

                              Or is there another possible solution for those problems?