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()); }