@Override
  public void execute(ActivityExecution execution) throws Exception {

    // find cancel boundary event:
    ActivityImpl cancelBoundaryEvent =
        ScopeUtil.findInParentScopesByBehaviorType(
            (ActivityImpl) execution.getActivity(), CancelBoundaryEventActivityBehavior.class);

    if (cancelBoundaryEvent == null) {
      throw new ActivitiException(
          "Could not find cancel boundary event for cancel end event " + execution.getActivity());
    }

    ActivityExecution scopeExecution =
        ScopeUtil.findScopeExecutionForScope(
            (ExecutionEntity) execution, cancelBoundaryEvent.getParentActivity());

    // end all executions and process instances in the scope of the transaction
    scopeExecution.destroyScope("cancel end event fired");

    // the scope execution executes the boundary event
    InterpretableExecution outgoingExecution = (InterpretableExecution) scopeExecution;
    outgoingExecution.setActivity(cancelBoundaryEvent);
    outgoingExecution.setActive(true);

    // execute the boundary
    cancelBoundaryEvent.getActivityBehavior().execute(outgoingExecution);
  }
  public void execute(
      JobEntity job,
      String configuration,
      ExecutionEntity execution,
      CommandContext commandContext) {
    ActivityImpl borderEventActivity = execution.getProcessDefinition().findActivity(configuration);

    if (borderEventActivity == null) {
      throw new ActivitiException(
          "Error while firing timer: border event activity " + configuration + " not found");
    }

    try {

      borderEventActivity.getActivityBehavior().execute(execution);

      if (commandContext.getEventDispatcher().isEnabled()) {
        commandContext
            .getEventDispatcher()
            .dispatchEvent(
                ActivitiEventBuilder.createEntityEvent(ActivitiEventType.TIMER_FIRED, job));
      }

    } catch (RuntimeException e) {
      log.error("exception during timer execution", e);
      throw e;

    } catch (Exception e) {
      log.error("exception during timer execution", e);
      throw new ActivitiException("exception during timer execution: " + e.getMessage(), e);
    }
  }
  /**
   * 封装输出信息,包括:当前节点的X、Y坐标、变量信息、任务类型、任务描述
   *
   * @param activity
   * @param processInstance
   * @param currentActiviti
   * @return
   */
  private Map<String, Object> packageSingleActivitiInfo(
      ActivityImpl activity, ProcessInstance processInstance, boolean currentActiviti)
      throws Exception {
    Map<String, Object> vars = new HashMap<String, Object>();
    Map<String, Object> activityInfo = new HashMap<String, Object>();
    activityInfo.put("currentActiviti", currentActiviti);
    setPosition(activity, activityInfo);
    setWidthAndHeight(activity, activityInfo);

    Map<String, Object> properties = activity.getProperties();
    vars.put("任务类型", WorkflowUtils.parseToZhType(properties.get("type").toString()));

    ActivityBehavior activityBehavior = activity.getActivityBehavior();
    logger.debug("activityBehavior={}", activityBehavior);
    if (activityBehavior instanceof UserTaskActivityBehavior) {

      Task currentTask = null;

      /*
       * 当前节点的task
       */
      if (currentActiviti) {
        currentTask = getCurrentTaskInfo(processInstance);
      }

      /*
       * 当前任务的分配角色
       */
      UserTaskActivityBehavior userTaskActivityBehavior =
          (UserTaskActivityBehavior) activityBehavior;
      TaskDefinition taskDefinition = userTaskActivityBehavior.getTaskDefinition();
      Set<Expression> candidateGroupIdExpressions = taskDefinition.getCandidateGroupIdExpressions();
      if (!candidateGroupIdExpressions.isEmpty()) {

        // 任务的处理角色
        setTaskGroup(vars, candidateGroupIdExpressions);

        // 当前处理人
        if (currentTask != null) {
          setCurrentTaskAssignee(vars, currentTask);
        }
      }
    }

    vars.put("节点说明", properties.get("documentation"));

    String description = activity.getProcessDefinition().getDescription();
    vars.put("描述", description);

    logger.debug("trace variables: {}", vars);
    activityInfo.put("vars", vars);
    return activityInfo;
  }
  @SuppressWarnings("unchecked")
  @Override
  protected void eventNotificationsCompleted(InterpretableExecution execution) {
    ActivityImpl activity = (ActivityImpl) execution.getActivity();
    ActivityImpl parentActivity = activity.getParentActivity();

    // if the execution is a single path of execution inside the process definition scope
    if ((parentActivity != null) && (!parentActivity.isScope())) {
      execution.setActivity(parentActivity);
      execution.performOperation(ACTIVITY_END);

    } else if (execution.isProcessInstance()) {
      execution.performOperation(PROCESS_END);

    } else if (execution.isScope()) {

      ActivityBehavior parentActivityBehavior =
          (parentActivity != null ? parentActivity.getActivityBehavior() : null);
      if (parentActivityBehavior instanceof CompositeActivityBehavior) {
        CompositeActivityBehavior compositeActivityBehavior =
            (CompositeActivityBehavior) parentActivity.getActivityBehavior();

        if (activity.isScope() && activity.getOutgoingTransitions().isEmpty()) {
          // there is no transition destroying the scope
          InterpretableExecution parentScopeExecution =
              (InterpretableExecution) execution.getParent();
          execution.destroy();
          execution.remove();
          parentScopeExecution.setActivity(parentActivity);
          compositeActivityBehavior.lastExecutionEnded(parentScopeExecution);
        } else {
          execution.setActivity(parentActivity);
          compositeActivityBehavior.lastExecutionEnded(execution);
        }
      } else {
        // default destroy scope behavior
        InterpretableExecution parentScopeExecution =
            (InterpretableExecution) execution.getParent();
        execution.destroy();
        execution.remove();
        // if we are a scope under the process instance
        // and have no outgoing transitions: end the process instance here
        if (activity.getParent() == activity.getProcessDefinition()
            && activity.getOutgoingTransitions().isEmpty()) {
          parentScopeExecution.setActivity(activity);
          // we call end() because it sets isEnded on the execution
          parentScopeExecution.end();
        } else {
          parentScopeExecution.setActivity(parentActivity);
          parentScopeExecution.performOperation(ACTIVITY_END);
        }
      }

    } else { // execution.isConcurrent() && !execution.isScope()

      execution.remove();

      // prune if necessary
      InterpretableExecution concurrentRoot = (InterpretableExecution) execution.getParent();
      if (concurrentRoot.getExecutions().size() == 1) {
        InterpretableExecution lastConcurrent =
            (InterpretableExecution) concurrentRoot.getExecutions().get(0);
        if (!lastConcurrent.isScope()) {
          concurrentRoot.setActivity((ActivityImpl) lastConcurrent.getActivity());
          lastConcurrent.setReplacedBy(concurrentRoot);

          // Move children of lastConcurrent one level up
          if (lastConcurrent.getExecutions().size() > 0) {
            concurrentRoot.getExecutions().clear();
            for (ActivityExecution childExecution : lastConcurrent.getExecutions()) {
              InterpretableExecution childInterpretableExecution =
                  (InterpretableExecution) childExecution;
              ((List) concurrentRoot.getExecutions())
                  .add(childExecution); // casting ... damn generics
              childInterpretableExecution.setParent(concurrentRoot);
            }
            lastConcurrent.getExecutions().clear();
          }

          // Copy execution-local variables of lastConcurrent
          concurrentRoot.setVariablesLocal(lastConcurrent.getVariablesLocal());

          lastConcurrent.remove();
        } else {
          lastConcurrent.setConcurrent(false);
        }
      }
    }
  }