3 Replies Latest reply on Apr 17, 2006 9:27 AM by meltem.yesiltas

    compensation

    tom.baeyens

      on behalf of Meltem:

      "Meltem" wrote:

      | Hi,
      |
      | First of all, you may not be the true address for this issue,
      | if so, sorry for disturbing you.
      |
      | We are developing a project for a bank in Turkey. We aim to
      | make all banking processes to be designed end executed with
      | jbpm. We need a kind of compensation handling. In summary,
      | the following is what we plan to implement:
      |
      | 1. Every node will have an attribute called "compensation-node".
      | 2. We will add the same runtime action to process-end event
      | of every process instance started.
      | 3. This runtime action will decide to execute or not to
      | execute compensation depending on the process state. If
      | compensation is needed, then it will execute compensation
      | nodes of all the executed nodes in reverse order.
      | 4. A compensation node may propagate or stop the compensation
      | depending on the context.
      |
      | We want all types all nodes to have compensation, so we need
      | to change ?org.jbpm.graph.def.Node? class. If you are
      | interested in, we can contribute these changes to jbpm or you
      | may suggest another way to implement compensation. Maybe you
      | already have some related plans. Another question: ?Is there
      | an extension point implemented (or planned to be implemented)
      | for adding this kind of common attributes to all node types
      | dynamically??
      |
      |
      | Thank you,
      |
      | Meltem



        • 1. Re: compensation
          tom.baeyens

          compensation is not yet implemented, but we know how we want it to look like:

          compensation should be based on the logs. logs can have pointers to nodes and actions.

          logs, actions and nodes should be able to implement an Compensatable interface.

          compensation should then be based on reverse playing the logs. compensation should be based on a date. all compensateble logs between now and the given date should be iterated in reverse and the compensate method should be invoked. logs that have references to compensatable nodes or actions can delegate the compensation behaviour to the nodes or actions.

          this way, you can think of adding a kind of user-specific compensation inside of the process logs. e.g. for updates done to your domain model.

          but be warned. compensation is tricky business. transactions are invented to preserve ACID properties. compensation cannot give those guarantees so you have to know what you're doing.

          • 2. Re: compensation
            meltem.yesiltas

            I will explain our requirement with a sample. Think of an EFT process with some approval steps. Before the approval phase, we block required amount of money from the customer account balance. Then if the EFT is rejected or something goes wrong (e.g. task timeout), then we have to unblock the balance and finish the process.

            So, for now, what we need is finishing the process with compensation, rather than going back some state in time. But I will try to cover both.

            Based on your explanation, the following is what we plan to do. Note that the source code is not mature nor tested, it is just to show the idea.

            1. Create an interface

            Compensatable
            with one method:

            public boolean compensate(ExecutionContext context)


            2. Create a base action class called
            CompensatableAction
            .

            public class CompensatableAction extends Action implements Compensatable {
            
             protected String compensateNodeName = null;
             protected String compensateActionName = null;
            
             public boolean compensate(ExecutionContext executionContext){
            
             // Action has the priority, if it is not specified, then we look node.
             if(compensateActionName != null && !compensateActionName.equals("")){
             Action compensateAction = this.getProcessDefinition().getAction(compensateActionName);
             return true;
             }
             if(compensateNodeName != null && !compensateNodeName.equals("")){
             Node compensateNode = this.getProcessDefinition().getNode(compensateNodeName);
             compensateNode.enter(executionContext);
             return true;
             }
            
             }
            
             public void read(Element serviceExecutorElement, JpdlXmlReader jpdlReader) {
             super.read(serviceExecutorElement, jpdlReader);
             // Only one of this attributes must be specified.
             compensateNodeName = serviceExecutorElement.attributeValue("compensate-node");
             compensateActionName = serviceExecutorElement.attributeValue("compensate-action");
             }
            }


            Note that, also new node types can implement Compensatable interface. But here I take actions as sample.

            3. Create our custom actions extending CompensatableAction. The process definition may look like below (or compensate-action can be specified instead of node)

            <myAction ..... compensate-node = "someFlyingNode">
            </myAction>
            
            <node name ="someFlyingNode">
            <!--this node will have no entering or leaving transitions-->
            </node>


            4. Add a new method to ProcessInstance class: compansate(Date toTime)

            This method will search the logs for all compensatable actions and nodes that are executed, call the compansate methods of them.

            If a date parameter is specified, then reverse play will continue till that date, and at the end, process token will be on the node that was active on that date and context also will be rewind in each step. If no date is specified, then all executed nodes and actions will be traversed until the start state.

            5. Create a generic action to call the compansate method of the process instance when necessary (for example when a timeout, reject or cancel occurs)

            <end-state name="reject">
             <event type="node-enter">
             <compansate date="......"/>
             </event>
            </end-state>


            I am not sure how the engine will behave in this scenario. In the end state we rewind to the begining, and then expect the process to finish??? I dont know whether it works or not ?

            • 3. Re: compensation
              meltem.yesiltas

              A forgotten detail about the forth item:

              ProcessInstance::compensate method will stop to call compensatable nodes and actions if any of them return false from compensate method, but it should continue to rewind the context and token.

              This is similar to event comsuming in Java. When compensating a node/action, it can tell the engine not to run compensation anymore.