protected static Object evaluateExpression(
      ActivityNode self, String body, Iterable<? extends InputPin> pins, ActivityInstance context) {
    ExecutionEnvironment env = context.getEnv();

    Object contextValue = context.getOclContext();
    Collection<ContextExtensionPin> extensions = new Vector<ContextExtensionPin>();
    for (InputPin pin : pins) {
      if (pin instanceof ContextExtensionPin) {
        extensions.add((ContextExtensionPin) pin);
      } else if (pin instanceof ContextPin) {
        contextValue = ((PinInstance) context.getPlaces(pin)).getValue();
      } else {
        throw new SemanticException("no non context input pins for expressions allowed.");
      }
    }

    for (ContextExtensionPin pin : extensions) {
      Object contextExtensionValue = ((PinInstance) context.getPlaces(pin)).getValue();
      env.addAdditionalContextAttribute(
          pin.getExtensionName(),
          contextExtensionValue,
          OpaqueActionCustom.getTypeForObject(contextExtensionValue),
          OpaqueActionCustom.getTypeForObject(contextValue));
    }
    Object result =
        env.evaluateInvariant(
            body, OpaqueActionCustom.getTypeForObject(contextValue), contextValue);
    for (ContextExtensionPin pin : extensions) {
      env.removeAdditionalContextAttribute(
          pin.getExtensionName(), OpaqueActionCustom.getTypeForObject(contextValue));
    }
    return result;
  }
  @Before
  public void setup() {
    for (int customerId = 1; customerId <= 5; customerId++) {
      ProcessInstance pi =
          sf.getWorkflowService()
              .startProcess(new QName(MODEL_NAME2, "OrderCreation").toString(), null, true);
      List<ActivityInstance> w = getWorklist();
      Assert.assertEquals("worklist", 1, w.size());
      ActivityInstance ai = w.get(0);
      Assert.assertEquals("process instance", pi.getOID(), ai.getProcessInstanceOID());
      Assert.assertEquals("activity instance", "EnterOrderData", ai.getActivity().getId());
      Map<String, Object> order = CollectionUtils.newMap();
      order.put("date", new Date());
      order.put("customerId", customerId);
      ai =
          complete(
              ai, PredefinedConstants.DEFAULT_CONTEXT, Collections.singletonMap("Order", order));

      try {
        ProcessInstanceStateBarrier.instance().await(pi.getOID(), ProcessInstanceState.Completed);
      } catch (Exception e) {
      }
      w = getWorklist();
    }
  }
 private ActivityInstance complete(ActivityInstance ai, String context, Map<String, ?> data) {
   WorkflowService ws = sf.getWorkflowService();
   if (ai.getState() != ActivityInstanceState.Application) {
     ai = ws.activate(ai.getOID());
   }
   ai = ws.complete(ai.getOID(), context, data);
   return ai;
 }
 private void propagateOutput(Object value, boolean forced, ActivityInstance context) {
   if (self.getOutput().size() != 1) {
     if (forced || self.getOutput().size() > 1) {
       throw new SemanticException("Illegal argument count for expression.");
     }
     return;
   } else {
     OutputPin pin = self.getOutput().get(0);
     for (ActivityEdge edge : pin.getOutgoing()) {
       ActivityNode target = edge.getTarget();
       if (target instanceof ValueNode && "return".equals(((ValueNode) target).getName())) {
         context.setReturn(value);
       }
     }
   }
   for (Place place : self.getOutputPlaces()) {
     if (place instanceof InputPin) {
       ((PinInstance) context.getPlaces(place)).setValue(value);
     }
   }
 }
  @Override
  public void fire(ActivityInstance context) {
    // perform all as semantics actions
    Object contextValue = null;
    MofClassSemantics semantics = null;
    switch (self.getActionKind()) {
      case EXPRESSION:
        DebugInfo.printInfo("eval " + self.getActionBody());
        propagateOutput(
            evaluateExpression(self, self.getActionBody(), self.getInput(), context),
            true,
            context);
        break;
      case CALL:
        DebugInfo.printInfo("call " + self.getActionBody());
        String operationName = getActionBody();
        ;
        contextValue = context.getOclContext();
        ReflectiveSequence<Argument> arguments = new ListImpl<Argument>();
        for (InputPin pin : self.getInput()) {
          if (pin instanceof ContextPin) {
            contextValue = ((PinInstance) context.getPlaces(pin)).getValue();
          } else {
            arguments.add(
                new ArgumentImpl(null, ((PinInstance) context.getPlaces(pin)).getValue()));
          }
        }
        if (contextValue == null) {
          throw new SemanticException("Context must not be null for call " + self.getActionBody());
        }
        semantics =
            MofClassSemantics.createClassClassifierForUmlClass(
                (UmlClass) getTypeForObject(contextValue));

        for (InputPin inputPin : self.getInput()) {
          if (!(inputPin instanceof ContextPin)) {
            operationName +=
                "_" + getTypeOfValue(((PinInstance) context.getPlaces(inputPin)).getValue());
          }
        }
        Operation operation = semantics.getFinalOperation(operationName);
        if (operation == null) {
          throw new SemanticException(
              "The arguments of action "
                  + toString()
                  + "(call "
                  + operationName
                  + ") does not match to an operation in the given context.");
        }
        Object result =
            ((cmof.reflection.Object) contextValue).invokeOperation(operation, arguments);
        propagateOutput(result, false, context);
        DebugInfo.printInfo("end call " + self.getActionBody());
        break;
      case PRINT:
        System.out.println(self.getActionBody());
        break;
      case WRITE_STRUCTURAL_FEATURE:
        DebugInfo.printInfo("set " + self.getActionBody());
        contextValue = context.getOclContext();
        Object featureValue = null;
        Object qualifierValue = null;
        for (InputPin pin : self.getInput()) {
          if (pin instanceof ContextPin) {
            contextValue = ((PinInstance) context.getPlaces(pin)).getValue();
          } else {
            if (featureValue != null) {
              if (qualifierValue != null) {
                throw new SemanticException(
                    "To much input pins for a write structural feature action.");
              }
              qualifierValue = featureValue;
            }
            featureValue = ((PinInstance) context.getPlaces(pin)).getValue();
            if (featureValue instanceof MofClassSemantics) {
              System.out.println("Fehler!!!");
            }
          }
        }
        Property feature = resolveFeature(contextValue);
        if (qualifierValue != null) {
          if (feature.getQualifier() == null) {
            throw new SemanticException(
                "To much input pins for a write structural feature action without qualifier.");
          }
          ((cmof.reflection.Object) contextValue).set(feature, qualifierValue, featureValue);
        } else {
          if (feature.getQualifier() != null) {
            throw new SemanticException(
                "Not enough input pins for a write structural feature action with qualifier.");
          }
          ((cmof.reflection.Object) contextValue).set(feature, featureValue);
        }
        break;
      case WRITE_STRUCTURAL_FEATURE_VALUE:
        DebugInfo.printInfo("add " + self.getActionBody());
        contextValue = context.getOclContext();
        featureValue = null;
        for (InputPin pin : self.getInput()) {
          if (pin instanceof ContextPin) {
            contextValue = ((PinInstance) context.getPlaces(pin)).getValue();
          } else {
            if (featureValue != null) {
              throw new SemanticException(
                  "To much input pins for a write structural feature action.");
            }
            featureValue = ((PinInstance) context.getPlaces(pin)).getValue();
          }
        }
        feature = resolveFeature(contextValue);
        ((ReflectiveSequence) ((cmof.reflection.Object) contextValue).get(feature))
            .add(featureValue);
        break;
      case REMOVE_STRUCTURAL_FEATURE_VALUE:
        DebugInfo.printInfo("remove " + self.getActionBody());
        contextValue = context.getOclContext();
        featureValue = null;
        for (InputPin pin : self.getInput()) {
          if (pin instanceof ContextPin) {
            contextValue = ((PinInstance) context.getPlaces(pin)).getValue();
          } else {
            if (featureValue != null) {
              throw new SemanticException(
                  "To much input pins for a write structural feature action.");
            }
            featureValue = ((PinInstance) context.getPlaces(pin)).getValue();
          }
        }
        feature = resolveFeature(contextValue);
        ((ReflectiveSequence) ((cmof.reflection.Object) contextValue).get(feature))
            .remove(featureValue);
        break;
      case CREATE_OBJECT:
        DebugInfo.printInfo("create " + self.getActionBody());
        boolean hasContextClass = false;
        contextValue = context.getOclContext();
        for (InputPin pin : self.getInput()) {
          if (pin instanceof ContextPin) {
            contextValue = ((PinInstance) context.getPlaces(pin)).getValue();
            hasContextClass = true;
          } else {
            throw new SemanticException(
                "Only a single context pin is allowed for a create action.");
          }
        }
        if (hasContextClass) {
          UmlClass syntaxClass = (UmlClass) getTypeForObject(contextValue);
          String className = self.getActionBody();
          UmlClass runtimeClass = null;
          for (Type classAsType : syntaxClass.getPackage().getOwnedType()) {
            if (classAsType instanceof UmlClass && className.equals(classAsType.getName())) {
              runtimeClass = (UmlClass) classAsType;
            }
          }
          if (runtimeClass == null) {
            throw new SemanticException(
                "Class with name "
                    + className
                    + " does not exist in package "
                    + syntaxClass.getPackage().getQualifiedName()
                    + ".");
          }
          semantics = MofClassSemantics.createClassClassifierForUmlClass(syntaxClass);
          Operation op =
              semantics.getFinalOperation(M1SemanticModel.getCreateOperationName(runtimeClass));
          result =
              ((cmof.reflection.Object) contextValue).invokeOperation(op, new ListImpl<Argument>());
          propagateOutput(result, false, context);
        } else {
          throw new SemanticException("not implemented yet.");
        }
        break;
      case PRINT_EXPRESSION:
        System.out.println(
            evaluateExpression(self, self.getActionBody(), self.getInput(), context));
        break;
      default:
        System.out.println("WARNING: unknown action kind");
    }

    // call the actual petri net semantics perfom method
    ((Transition) getSuper(Transition.class)).fire(context);
  }
  /**
   * The following test case should ensure that
   *
   * <ul>
   *   <li>Any modifications to an attribute of a BOI via API isn't reflected to process data which
   *       are using the BO
   *   <li>Any modifications to an attribute of a BOI via the process data is only reflected to the
   *       BOI which is attached to the synthetic process instance and that it doesn't affect other
   *       BOIs which are used in other processes
   * </ul>
   */
  @Test
  public void checkFilteringOnBusinessObjectAttrChange() {
    // setup
    final int customerIdOffset = 100;
    final int customerCount = 3;
    for (int customerId = 1; customerId <= customerCount; customerId++) {
      ProcessInstance pi =
          sf.getWorkflowService()
              .startProcess(new QName(MODEL_NAME2, "DistributedOrder").toString(), null, true);
      List<ActivityInstance> w = getWorklist(pi);
      Assert.assertEquals("worklist", 1, w.size());
      ActivityInstance ai = w.get(0);
      Assert.assertEquals("activity instance", "CreateOrder", ai.getActivity().getId());
      Map<String, Object> order = CollectionUtils.newMap();
      order.put("date", new Date());
      order.put("customerId", customerIdOffset + customerId);
      order.put("items", "item " + customerId);
      ai =
          complete(
              ai, PredefinedConstants.DEFAULT_CONTEXT, Collections.singletonMap("Order", order));

      try {
        ActivityInstanceStateBarrier.instance().await(ai.getOID(), ActivityInstanceState.Completed);
      } catch (Exception e) {
      }
    }

    // after DistributeCreation activity is completed we have the following state:
    // * 2 asynchronous subprocesses are started: one which copies the data and the
    //   other one which doesn't
    // * 3 synchronous subprocesses are triggered: one with shared data, one with separate
    //   but copied data and the last one with separate data without copying
    // This results into the following state:
    // * Each process has created four business object instances
    //   * One which is attached to a synthetic process instance
    //   * 3 other BOIs which are attached to a real process instance
    String businessObjectQualifiedId = new QName(MODEL_NAME2, "Order").toString();
    BusinessObjectQuery businessObjectQuery =
        BusinessObjectQuery.findForBusinessObject(businessObjectQualifiedId);
    businessObjectQuery
        .getFilter()
        .addAndTerm()
        .add(DataFilter.greaterThan("Order", "customerId", customerIdOffset));
    businessObjectQuery.setPolicy(
        new BusinessObjectQuery.Policy(
            BusinessObjectQuery.Option.WITH_VALUES, BusinessObjectQuery.Option.WITH_DESCRIPTION));
    BusinessObjects bos = sf.getQueryService().getAllBusinessObjects(businessObjectQuery);
    Assert.assertEquals("Only one business object, namely Order, is expected", 1, bos.getSize());
    Assert.assertEquals(
        "Business object instances count isn't the same as started process ergo the count of the synthetic process instances",
        customerCount,
        getTotalSize(bos));

    // Wait that all ShowOrder processes are started (unfortunately we cannot use
    // ProcessInstanceStateBarrier here
    // because of the async processes.
    ProcessInstanceQuery piQuery = ProcessInstanceQuery.findAlive("ShowOrder");
    boolean waitForPIs = true;
    while (waitForPIs) {
      long instanceCount = sf.getQueryService().getProcessInstancesCount(piQuery);
      waitForPIs = instanceCount != (customerCount * 5);

      if (waitForPIs) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
        }
      }
    }

    BusinessObject bo = bos.get(0);
    BusinessObject.Value customer101 = null;
    for (BusinessObject.Value boValue : bo.getValues()) {
      Map<?, ?> boAttr = (Map<?, ?>) boValue.getValue();
      Integer customerId = (Integer) boAttr.get("customerId");
      if (Integer.valueOf(customerIdOffset + 1).equals(customerId)) {
        customer101 = boValue;
      }
    }
    Assert.assertNotNull("Customer " + customerIdOffset + 1 + " not found", customer101);

    // Update BOI via API...
    ((Map) customer101.getValue()).put("items", "newitems");
    sf.getWorkflowService()
        .updateBusinessObjectInstance(businessObjectQualifiedId, customer101.getValue());

    // ...and validate if no process data is modified
    piQuery = ProcessInstanceQuery.findActive();
    FilterTerm filter = piQuery.getFilter().addAndTerm();
    filter.add(
        DataFilter.between(
            "Order", "customerId", customerIdOffset, customerIdOffset + customerCount));
    filter.add(DataFilter.like("Order", "items", "item%"));
    filter.addAndTerm().add(ProcessInstanceHierarchyFilter.ROOT_PROCESS);
    piQuery.setPolicy(SubsetPolicy.UNRESTRICTED);
    ProcessInstances rootPIs = sf.getQueryService().getAllProcessInstances(piQuery);
    // Root process instances are the DistributedOrder processes and the ShowOrder processes which
    // was started
    // as async processes and which had copied the data
    Assert.assertEquals(
        "Changes in BOIs must not be reflected in process instance data",
        customerCount * 2,
        rootPIs.getTotalCount());

    // Update BOI for a given process via data path...
    long piOid = rootPIs.get(0).getOID();
    ((Map) customer101.getValue()).put("items", "newitems1");
    sf.getWorkflowService().setOutDataPath(piOid, "OrderDataPath", (Map) customer101.getValue());

    // ...and validate if the BOI is updated...
    businessObjectQuery =
        BusinessObjectQuery.findWithPrimaryKey(
            businessObjectQualifiedId, ((Map) customer101.getValue()).get("customerId"));
    businessObjectQuery.setPolicy(
        new BusinessObjectQuery.Policy(BusinessObjectQuery.Option.WITH_VALUES));
    bos = sf.getQueryService().getAllBusinessObjects(businessObjectQuery);
    Assert.assertEquals("Only one business object, namely Order, is expected", 1, bos.getSize());
    List<BusinessObject.Value> boValues = bos.get(0).getValues();
    Assert.assertEquals(1, boValues.size());
    Assert.assertEquals("newitems1", ((Map) boValues.get(0).getValue()).get("items"));

    // ...but the other process instance data should be untouched
    piQuery = ProcessInstanceQuery.findActive();
    filter = piQuery.getFilter().addAndTerm();
    filter.add(
        DataFilter.between(
            "Order", "customerId", customerIdOffset, customerIdOffset + customerCount));
    filter.add(DataFilter.like("Order", "items", "item%"));
    filter.addAndTerm().add(ProcessInstanceHierarchyFilter.ROOT_PROCESS);
    piQuery.setPolicy(SubsetPolicy.UNRESTRICTED);
    rootPIs = sf.getQueryService().getAllProcessInstances(piQuery);
    Assert.assertEquals(
        "Changes in BOIs must not be reflected in process instance data",
        (customerCount * 2) - 1,
        rootPIs.getTotalCount());
  }