/**
  * Delegate synchronization to agent. / public void invokeLater(final Runnable action) { //
  * Redirect to component instance. interpreter.invokeLater(action);
  *
  * <p>// Called from outside (e.g. workflow client) // when task is finished // Check if plan
  * should be set to ready (Hack!!!) // interpreter.invokeLater(new Runnable() // { // public void
  * run() // { // // The DefaultActivityHandler.step() may rethrow an exception that // // occurred
  * within task execution. // // It is catched an stored in the plan to be rethrown from the // //
  * BPMNPlanExecutor.executeStep() method. // // The plan must always be set to ready to transfer
  * the state // // of the process thread to the plan. // try // { // action.run(); // } //
  * catch(Throwable t) // { // state.setAttributeValue(rplan,
  * OAVBDIRuntimeModel.plan_has_exception, t); // } // state.setAttributeValue(rplan,
  * OAVBDIRuntimeModel.plan_has_processingstate, OAVBDIRuntimeModel.PLANPROCESSINGTATE_READY); //
  * //// String lane = getLane(getLastState()); //// if(isReady(null, lane)) //// { ////
  * state.setAttributeValue(rplan, OAVBDIRuntimeModel.plan_has_processingstate,
  * OAVBDIRuntimeModel.PLANPROCESSINGTATE_READY); //// } // } // }); }
  *
  * <p>/** Method that should be called, when an activity is finished and the following activity
  * should be scheduled. Can safely be called from external threads.
  *
  * @param activity The timing event activity.
  * @param instance The process instance.
  * @param thread The process thread.
  * @param event The event that has occurred, if any.
  */
 public void notify(final MActivity activity, final ProcessThread thread, final Object event) {
   if (isExternalThread()) {
     getComponentAdapter()
         .invokeLater(
             new Runnable() {
               public void run() {
                 if (isCurrentActivity(activity, thread)) {
                   try {
                     getStepHandler(activity)
                         .step(activity, BpmnPlanBodyInstance.this, thread, event);
                   } catch (Throwable t) {
                     if (state.containsObject(rplan))
                       state.setAttributeValue(rplan, OAVBDIRuntimeModel.plan_has_exception, t);
                   }
                   thread.setNonWaiting();
                   if (state.containsObject(rplan)) {
                     //							System.out.println("Ready: "+activity+" "+thread);
                     state.setAttributeValue(
                         rplan,
                         OAVBDIRuntimeModel.plan_has_processingstate,
                         OAVBDIRuntimeModel.PLANPROCESSINGTATE_READY);
                   }
                 } else {
                   System.out.println("Nop, due to outdated notify: " + thread + " " + activity);
                 }
               }
             });
   } else {
     if (isCurrentActivity(activity, thread)) {
       try {
         getStepHandler(activity).step(activity, BpmnPlanBodyInstance.this, thread, event);
       } catch (Throwable t) {
         if (state.containsObject(rplan))
           state.setAttributeValue(rplan, OAVBDIRuntimeModel.plan_has_exception, t);
       }
       thread.setNonWaiting();
       if (state.containsObject(rplan)) {
         //					System.out.println("Ready: "+activity+" "+thread);
         state.setAttributeValue(
             rplan,
             OAVBDIRuntimeModel.plan_has_processingstate,
             OAVBDIRuntimeModel.PLANPROCESSINGTATE_READY);
       }
     } else {
       System.out.println("Nop, due to outdated notify: " + thread + " " + activity);
     }
   }
 }
  /**
   * Make a process step, i.e. find the next edge or activity for a just executed thread.
   *
   * @param activity The activity to execute.
   * @param instance The process instance.
   * @param thread The process thread.
   */
  public void step(
      MActivity activity, BpmnInterpreter instance, ProcessThread thread, Object event) {
    //		System.out.println(instance.getComponentIdentifier().getLocalName()+": step "+activity+",
    // data "+thread.getData());

    // Hack!!! Should be in interpreter/thread?
    thread.updateParametersAfterStep(activity, instance);

    MNamedIdElement next = null;
    ThreadContext remove = null; // Context that needs to be removed (if any).
    Exception ex = thread.getException();

    // Store event (if any).
    if (event != null) {
      thread.setParameterValue("$event", event);
      //			System.out.println("Event: "+activity+" "+thread+" "+event);
    }

    // Timer occurred flow
    if (AbstractEventIntermediateTimerActivityHandler.TIMER_EVENT.equals(event)) {
      // Cancel subflows.
      remove = thread.getThreadContext().getSubcontext(thread);

      // Continue with timer edge.
      List outedges = activity.getOutgoingSequenceEdges();
      if (outedges != null && outedges.size() == 1) {
        next = (MSequenceEdge) outedges.get(0);
      } else if (outedges != null && outedges.size() > 1) {
        throw new RuntimeException("Cannot determine outgoing edge: " + activity);
      }
    }

    // Find next element and context(s) to be removed.
    else {
      boolean outside = false;
      ThreadContext context = thread.getThreadContext();
      while (next == null && !outside) {
        // Normal flow
        if (ex == null) {
          List outgoing = activity.getOutgoingSequenceEdges();
          if (outgoing != null && outgoing.size() == 1) {
            next = (MSequenceEdge) outgoing.get(0);
          } else if (outgoing != null && outgoing.size() > 1) {
            throw new UnsupportedOperationException(
                "Activity has more than one one outgoing edge. Please overridge step() for disambiguation: "
                    + activity);
          }
          // else no outgoing edge -> check parent context, if any.
        }

        // Exception flow.
        else {
          List handlers = activity.getEventHandlers();
          for (int i = 0; handlers != null && next == null && i < handlers.size(); i++) {
            MActivity handler = (MActivity) handlers.get(i);
            if (handler.getActivityType().equals("EventIntermediateError")) {
              // Todo: match exception types.
              //						Class	clazz	= handler.getName()!=null ? SReflect.findClass0(clname, imports,
              // classloader);
              next = handler;
            }
          }
        }

        outside = context.getParent() == null;
        if (next == null && !outside) {
          // When last thread or exception, mark current context for removal.
          if (context.getThreads().size() == 1 || ex != null) {
            activity = (MActivity) context.getModelElement();
            remove = context;
            context = context.getParent();

            // Cancel subprocess handlers.
            if (activity instanceof MSubProcess) {
              List handlers = activity.getEventHandlers();
              for (int i = 0; handlers != null && i < handlers.size(); i++) {
                MActivity handler = (MActivity) handlers.get(i);
                instance
                    .getActivityHandler(handler)
                    .cancel(handler, instance, remove.getInitiator());
              }
            }
          }

          // If more threads are available in current context just exit loop.
          else if (context.getThreads().size() > 1) {
            outside = true;
          }
        }
      }
    }

    // Remove inner context(s), if any.
    if (remove != null) {
      thread = remove.getInitiator();
      thread.setNonWaiting();
      // Todo: Callbacks for aborted threads (to abort external activities)
      thread.getThreadContext().removeSubcontext(remove);
    }

    if (next != null) {

      // Todo: store exception as parameter!?
      if (ex != null) thread.setException(null);
    } else if (ex != null) {
      if (ex instanceof RuntimeException) throw (RuntimeException) ex;
      else throw new RuntimeException("Unhandled exception in process: " + activity, ex);
    }

    // Perform step settings, i.e. set next edge/activity or remove thread.
    if (next instanceof MSequenceEdge) {
      thread.setLastEdge((MSequenceEdge) next);
    } else if (next instanceof MActivity) {
      thread.setActivity((MActivity) next);
    } else if (next == null) {
      thread.getThreadContext().removeThread(thread);
    } else {
      throw new UnsupportedOperationException("Unknown outgoing element type: " + next);
    }
  }