/**
   * Update the waiting threads according to the wakeup reason (dispatched element). The
   * corresponding waiting activity/thread is notified.
   */
  public void updateWaitingThreads() {
    if (waittimes != null) {
      //			IClockService clock =
      // (IClockService)interpreter.getServiceProvider().getService(IClockService.class);

      IClockService clock = interpreter.getClockService();
      for (Iterator it = waittimes.keySet().iterator(); it.hasNext(); ) {
        ProcessThread thread = (ProcessThread) it.next();
        if (((Number) waittimes.get(thread)).longValue() <= clock.getTime()) {
          it.remove();
          assert thread.isWaiting();
          BpmnPlanBodyInstance.this.notify(
              thread.getActivity(),
              thread,
              AbstractEventIntermediateTimerActivityHandler.TIMER_EVENT);
        }
      }
    }

    Object dispelem = state.getAttributeValue(rplan, OAVBDIRuntimeModel.plan_has_dispatchedelement);
    //		System.out.println("dispatched: "+dispelem);

    if (dispelem != null) {
      for (Iterator it = context.getAllThreads().iterator(); it.hasNext(); ) {
        ProcessThread thread = (ProcessThread) it.next();
        try {
          if (thread.isWaiting() && thread.getWaitFilter().filter(dispelem)) {
            BpmnPlanBodyInstance.this.notify(thread.getActivity(), thread, getFlyweight(dispelem));
          }
        } catch (Exception e) {
          // just catch filter exceptions.
        }
      }
    }
  }
 /**
  * Get the current timeout of the process, i.e. the remaining time of the closest due intermediate
  * timer event.
  *
  * @return The current timeout or -1 for no timeout.
  */
 public long getTimeout() {
   long mindur = -1;
   if (waittimes != null) {
     String pool = getPool(getLastState());
     if (!POOL_UNDEFINED.equals(pool)) {
       IClockService clock = interpreter.getClockService();
       //				IClockService	clock	=
       // (IClockService)interpreter.getServiceProvider().getService(IClockService.class);
       for (Iterator it = waittimes.keySet().iterator(); it.hasNext(); ) {
         ProcessThread thread = (ProcessThread) it.next();
         if (thread.belongsTo(pool, null)) {
           long time = Math.max(((Number) waittimes.get(thread)).longValue() - clock.getTime(), 0);
           mindur = mindur == -1 ? time : time < mindur ? time : mindur;
         }
       }
     }
   }
   return mindur;
 }
 /**
  * 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);
     }
   }
 }
 /** Must be called on process execution thread! Gets infos about the current threads. */
 protected ProcessThreadInfo[] getThreadInfos() {
   List ret = null;
   ThreadContext tc = instance.getThreadContext();
   Set threads = tc.getAllThreads();
   if (threads != null) {
     ret = new ArrayList();
     for (Iterator it = threads.iterator(); it.hasNext(); ) {
       ProcessThread pt = (ProcessThread) it.next();
       ret.add(
           new ProcessThreadInfo(
               pt.getId(),
               pt.getActivity(), // pt.getLastEdge(),
               pt.getException(),
               pt.isWaiting(),
               pt.getData() == null ? new HashMap() : new HashMap(pt.getData())));
     }
   }
   return (ProcessThreadInfo[]) ret.toArray(new ProcessThreadInfo[ret.size()]);
 }
  /**
   * Get the cumulated wait abstraction for all threads.
   *
   * @return The wait abstraction.
   */
  public Object getWaitAbstraction() {
    Object ret = null;
    String pool = getPool(getLastState());
    if (!POOL_UNDEFINED.equals(pool)) {
      ret = getState().createObject(OAVBDIRuntimeModel.waitabstraction_type);
      boolean empty = true;

      for (Iterator it = context.getAllThreads().iterator(); it.hasNext(); ) {
        ProcessThread pt = (ProcessThread) it.next();
        if (pt.isWaiting() && pt.belongsTo(pool, null)) {
          MActivity act = pt.getActivity();
          if (MBpmnModel.EVENT_INTERMEDIATE_MESSAGE.equals(act.getActivityType())) {
            String type = (String) pt.getWaitInfo();
            if (type == null) throw new RuntimeException("Message type not specified: " + type);
            SFlyweightFunctionality.addMessageEvent(ret, type, state, rcapa);
            empty = false;
          } else if (MBpmnModel.EVENT_INTERMEDIATE_SIGNAL.equals(act.getActivityType())) {
            String type = (String) pt.getWaitInfo();
            if (type == null)
              throw new RuntimeException("Internal event type not specified: " + type);
            SFlyweightFunctionality.addInternalEvent(ret, type, state, rcapa);
            empty = false;
          } else if (MBpmnModel.EVENT_INTERMEDIATE_RULE.equals(act.getActivityType())) {
            String type = (String) pt.getWaitInfo();
            if (type == null) throw new RuntimeException("Rule type not specified: " + type);
            SFlyweightFunctionality.addCondition(ret, type, state, rcapa);
            empty = false;
          } else if (MBpmnModel.EVENT_INTERMEDIATE_MULTIPLE.equals(act.getActivityType())) {
            List edges = pt.getActivity().getOutgoingSequenceEdges();
            Object[] was = (Object[]) pt.getWaitInfo();

            for (int i = 0; i < edges.size(); i++) {
              MSequenceEdge edge = (MSequenceEdge) edges.get(i);
              MActivity nextact = edge.getTarget();
              if (MBpmnModel.EVENT_INTERMEDIATE_MESSAGE.equals(nextact.getActivityType())) {
                String type = (String) was[i];
                if (type == null) throw new RuntimeException("Message type not specified: " + type);
                SFlyweightFunctionality.addMessageEvent(ret, type, state, rcapa);
                empty = false;
              } else if (MBpmnModel.EVENT_INTERMEDIATE_SIGNAL.equals(nextact.getActivityType())) {
                String type = (String) was[i];
                if (type == null)
                  throw new RuntimeException("Internal event type not specified: " + type);
                SFlyweightFunctionality.addInternalEvent(ret, type, state, rcapa);
                empty = false;
              } else if (MBpmnModel.EVENT_INTERMEDIATE_RULE.equals(nextact.getActivityType())) {
                String type = (String) was[i];
                if (type == null) throw new RuntimeException("Rule type not specified: " + type);
                SFlyweightFunctionality.addCondition(ret, type, state, rcapa);
                empty = false;
              } else if (MBpmnModel.EVENT_INTERMEDIATE_TIMER.equals(nextact.getActivityType())) {
                // nothing to do with waitqueue.
              } else {
                throw new RuntimeException("Unknown event: " + nextact);
              }
            }
          }

          // todo: condition wait

          // todo: time wait?!
        }
      }

      if (empty) {
        state.dropObject(ret);
        ret = null;
      }
    }

    return ret;
  }
  /**
   * 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);
    }
  }