diff --git a/imixs-workflow-core/src/main/java/org/imixs/workflow/ItemCollection.java b/imixs-workflow-core/src/main/java/org/imixs/workflow/ItemCollection.java index fb234ca7..bbbd46c7 100644 --- a/imixs-workflow-core/src/main/java/org/imixs/workflow/ItemCollection.java +++ b/imixs-workflow-core/src/main/java/org/imixs/workflow/ItemCollection.java @@ -230,7 +230,7 @@ public void cloneItem(String itemName, ItemCollection source) { List copy = (List) ois.readObject(); hash.put(itemName, copy); } catch (IOException | ClassNotFoundException e) { - logger.log(Level.WARNING, "Unable to clone values of Item ''{0}'' - {1}", new Object[]{itemName, e}); + logger.log(Level.WARNING, "Unable to clone values of Item ''{0}'' - {1}", new Object[] { itemName, e }); } } @@ -1431,33 +1431,22 @@ public int getEventID() { } /** - * set $eventID + * Set the $eventID for a workitem. * * @param eventID */ public void setEventID(int eventID) { replaceItemValue(WorkflowKernel.EVENTID, eventID); - - // if deprectaed ActivityID exists we must still support it - if (hasItem("$activityid")) { - replaceItemValue("$activityid", eventID); - } } /** - * Set the event id for a workitem. If a event id is already set, the method - * appends the event to the ACTIVITYIDLIST + * Set the $eventID for a workitem. * * @param eventID * @return */ public ItemCollection event(int eventID) { - if (this.getEventID() == 0) { - setEventID(eventID); - } else { - // set - appendItemValue(WorkflowKernel.ACTIVITYIDLIST, eventID); - } + setEventID(eventID); return this; } diff --git a/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowContext.java b/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowContext.java index 04a35634..7ad56a63 100644 --- a/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowContext.java +++ b/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowContext.java @@ -29,10 +29,11 @@ package org.imixs.workflow; /** - * This Interface defines the Context which is used to supply a basic enviroment - * for the exchange between a WorkflowManager an the registered Plugin Moduls. - * Normaly the WorkflowManager Implementation itself implents this Interface to - * provide the Context for the Workflow components. + * This WorkflowContext provides the {@link WorkflowKernel} with a runtime + * environment for the processing live cycle of a workitem. + * A Workflow Engine implements this Interface to provide a {@link ModelManger} + * and the session context to execute {@link Plugin} and {@link Adapter} + * classes. * * @author imixs.com * @version 1.0 @@ -42,16 +43,16 @@ public interface WorkflowContext { /** - * This Methode returns the Runtime enviroment for a workflow Implementation. is - * usesd to initialize the plugin. + * This methode returns the Runtime environment for a workflow Implementation + * used to execute {@link Plugin} and {@link Adapter} classes. * * @return a Session Object */ public Object getSessionContext(); /** - * This method returns an instance of a IModelManager to access model - * information + * This method returns an instance of the {@link ModelManager} to access model + * information form a BPMN model. * * @return ModelManager */ diff --git a/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowKernel.java b/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowKernel.java index 9fdd4076..5ed69019 100644 --- a/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowKernel.java +++ b/imixs-workflow-core/src/main/java/org/imixs/workflow/WorkflowKernel.java @@ -51,13 +51,17 @@ import org.imixs.workflow.util.XMLParser; /** - * The Workflowkernel is the core component of this Framework to control the - * processing of a workitem. A Workflowmanager loads an instance of - * a Workflowkernel and hand over a Model and register - * Plugins for processing one or many workitems. + * The WorkflowKernel is the core component to process a workitem based + * on a BPMN event. The WorkflowKernel expects a {@link WorkflowContext} to + * access the {@link ModelManager} and the runtime environment. + * + * An implementation of the {@link WorkflowManager} typical creates an instance + * of a Workflowkernel and register {@link Plugin} and {@link Adapter} classes + * to be executed during the processing life cycle of one or many workitems. * * @author Ralph Soika - * @version 1.1 + * @version 2.0 + * @see org.imixs.workflow.WorkflowContext * @see org.imixs.workflow.WorkflowManager */ @@ -94,7 +98,6 @@ public class WorkflowKernel { public static final String TASKID = "$taskid"; public static final String EVENTID = "$eventid"; - public static final String ACTIVITYIDLIST = "$activityidlist"; public static final String WORKFLOWGROUP = "$workflowgroup"; public static final String WORKFLOWSTATUS = "$workflowstatus"; public static final String ISVERSION = "$isversion"; @@ -306,7 +309,7 @@ public Map getAdapterRegistry() { * @return updated workitem * @throws PluginException,ModelException */ - public ItemCollection process(final ItemCollection workitem) throws PluginException, ModelException { + public ItemCollection process(ItemCollection workitem) throws PluginException, ModelException { // check document context if (workitem == null) @@ -323,39 +326,36 @@ public ItemCollection process(final ItemCollection workitem) throws PluginExcept throw new ProcessingErrorException(WorkflowKernel.class.getSimpleName(), UNDEFINED_ACTIVITYID, "processing error: $eventID undefined (" + workitem.getEventID() + ")"); - // ItemCollection documentResult = new ItemCollection(workitem); - // we do no longer clone the woritem - Issue #507 - ItemCollection documentResult = workitem; vectorEdgeHistory = new Vector(); // Check if $UniqueID is available if ("".equals(workitem.getItemValueString(UNIQUEID))) { // generating a new one - documentResult.replaceItemValue(UNIQUEID, generateUniqueID()); + workitem.replaceItemValue(UNIQUEID, generateUniqueID()); } // Generate a $TransactionID - documentResult.replaceItemValue(TRANSACTIONID, generateTransactionID()); + workitem.replaceItemValue(TRANSACTIONID, generateTransactionID()); // store last $lastTask - documentResult.replaceItemValue("$lastTask", workitem.getTaskID()); + workitem.replaceItemValue("$lastTask", workitem.getTaskID()); // Check if $WorkItemID is available if ("".equals(workitem.getItemValueString(WorkflowKernel.WORKITEMID))) { - documentResult.replaceItemValue(WorkflowKernel.WORKITEMID, generateUniqueID()); + workitem.replaceItemValue(WorkflowKernel.WORKITEMID, generateUniqueID()); } // clear all existing adapter errors.. - documentResult.removeItem(ADAPTER_ERROR_CONTEXT); - documentResult.removeItem(ADAPTER_ERROR_CODE); - documentResult.removeItem(ADAPTER_ERROR_PARAMS); - documentResult.removeItem(ADAPTER_ERROR_MESSAGE); + workitem.removeItem(ADAPTER_ERROR_CONTEXT); + workitem.removeItem(ADAPTER_ERROR_CODE); + workitem.removeItem(ADAPTER_ERROR_PARAMS); + workitem.removeItem(ADAPTER_ERROR_MESSAGE); // Iterate through all events ItemCollection event = this.ctx.getModelManager().loadEvent(workitem); while (event != null) { // set $lastEventDate - documentResult.replaceItemValue(LASTEVENTDATE, new Date()); + workitem.replaceItemValue(LASTEVENTDATE, new Date()); event = this.ctx.getModelManager().loadEvent(workitem); // invalidate deprecated models! @@ -373,16 +373,17 @@ public ItemCollection process(final ItemCollection workitem) throws PluginExcept "[loadEvent] loop detected " + taskID + "." + eventID + "," + vectorEdgeHistory.toString()); } - documentResult = processEvent(documentResult, event); + workitem = processEvent(workitem, event); // put current edge in history vectorEdgeHistory.addElement( event.getItemValueInteger("numprocessid") + "." + event.getItemValueInteger("numactivityid")); // test if a new model version was assigned by the last event - if (updateModelVersionByEvent(documentResult, event)) { - // reload new Event and start new processing live cycle... + if (updateModelVersionByEvent(workitem, event)) { + // load new Event and start new processing live cycle... event = this.ctx.getModelManager().loadEvent(workitem); + workitem.event(event.getItemValueInteger("numactivityid")); } else { // evaluate next BPMN Element..... ItemCollection nextElement = this.ctx.getModelManager().nextModelElement(workitem); @@ -396,14 +397,15 @@ public ItemCollection process(final ItemCollection workitem) throws PluginExcept event = nextElement; } else { // Update status - Issue #722 - updateWorkflowStatus(documentResult, nextElement); + updateWorkflowStatus(workitem, nextElement); // terminate processing live cycle + workitem.event(0); event = null; } } } - return documentResult; + return workitem; } /** @@ -477,57 +479,59 @@ public List getSplitWorkitems() { * * @throws ModelException **/ - @Deprecated - private ItemCollection updateEventList(final ItemCollection documentContext, final ItemCollection event) - throws ModelException { - ItemCollection documentResult = documentContext; - boolean debug = logger.isLoggable(Level.FINE); - // first clear the eventID - documentResult.setEventID(Integer.valueOf(0)); - - // test if a FollowUp event is defined for the given event (Deprecated)... - String sFollowUp = event.getItemValueString("keyFollowUp"); - int iNextActivityID = event.getItemValueInteger("numNextActivityID"); - if ("1".equals(sFollowUp) && iNextActivityID > 0) { - // append the next event id..... - documentResult = appendActivityID(documentResult, iNextActivityID); - } + // @Deprecated + // private ItemCollection updateEventList(final ItemCollection documentContext, + // final ItemCollection event) + // throws ModelException { + // ItemCollection documentResult = documentContext; + // boolean debug = logger.isLoggable(Level.FINE); + // // first clear the eventID + // documentResult.setEventID(Integer.valueOf(0)); + + // // test if a FollowUp event is defined for the given event (Deprecated)... + // String sFollowUp = event.getItemValueString("keyFollowUp"); + // int iNextActivityID = event.getItemValueInteger("numNextActivityID"); + // if ("1".equals(sFollowUp) && iNextActivityID > 0) { + // // append the next event id..... + // documentResult = appendActivityID(documentResult, iNextActivityID); + // } - // evaluate is $eventid already provided? - if ((documentContext.getEventID() <= 0)) { - // no $eventID provided, so we test for property $ActivityIDList - List vActivityList = documentContext.getItemValue(ACTIVITYIDLIST); + // // evaluate is $eventid already provided? + // if ((documentContext.getEventID() <= 0)) { + // // no $eventID provided, so we test for property $ActivityIDList + // List vActivityList = documentContext.getItemValue(ACTIVITYIDLIST); - // remove 0 values if contained! - while (vActivityList.indexOf(Integer.valueOf(0)) > -1) { - vActivityList.remove(vActivityList.indexOf(Integer.valueOf(0))); - } + // // remove 0 values if contained! + // while (vActivityList.indexOf(Integer.valueOf(0)) > -1) { + // vActivityList.remove(vActivityList.indexOf(Integer.valueOf(0))); + // } - // test if an id is found.... - if (vActivityList.size() > 0) { - // yes - load next ID from activityID List - int iNextID = 0; - Object oA = vActivityList.get(0); - if (oA instanceof Integer) - iNextID = ((Integer) oA).intValue(); - if (oA instanceof Double) - iNextID = ((Double) oA).intValue(); - - if (iNextID > 0) { - // load activity - if (debug) { - logger.log(Level.FINEST, "......processing={0} -> loading next activityID = {1}", - new Object[] { documentContext.getItemValueString(UNIQUEID), iNextID }); - } - vActivityList.remove(0); - // update document context - documentResult.setEventID(Integer.valueOf(iNextID)); - documentResult.replaceItemValue(ACTIVITYIDLIST, vActivityList); - } - } - } - return documentResult; - } + // // test if an id is found.... + // if (vActivityList.size() > 0) { + // // yes - load next ID from activityID List + // int iNextID = 0; + // Object oA = vActivityList.get(0); + // if (oA instanceof Integer) + // iNextID = ((Integer) oA).intValue(); + // if (oA instanceof Double) + // iNextID = ((Double) oA).intValue(); + + // if (iNextID > 0) { + // // load activity + // if (debug) { + // logger.log(Level.FINEST, "......processing={0} -> loading next activityID = + // {1}", + // new Object[] { documentContext.getItemValueString(UNIQUEID), iNextID }); + // } + // vActivityList.remove(0); + // // update document context + // documentResult.setEventID(Integer.valueOf(iNextID)); + // documentResult.replaceItemValue(ACTIVITYIDLIST, vActivityList); + // } + // } + // } + // return documentResult; + // } /** * If the current workflow result of an Event defines a new model tag, this @@ -582,8 +586,7 @@ private boolean updateModelVersionByEvent(final ItemCollection workitem, final I throw new ModelException(ModelException.INVALID_MODEL, sErrorMessage); } // apply new model version and event id - workitem.setModelVersion(version); - workitem.setEventID(Integer.valueOf(iNextEvent)); + workitem.model(version).event(iNextEvent); if (iTask > 0) { // optional workitem.task(iTask); @@ -665,29 +668,29 @@ private ItemCollection processEvent(final ItemCollection workitem, final ItemCol * Helper method to update the items $taskid, $worklfowstatus, $workflowgroup * and type */ - private void updateWorkflowStatus(ItemCollection documentResult, ItemCollection itemColNextTask) { + private void updateWorkflowStatus(ItemCollection workitem, ItemCollection itemColNextTask) { boolean debug = logger.isLoggable(Level.FINE); // Update the attributes $taskID and $WorkflowStatus - documentResult.setTaskID(Integer.valueOf(itemColNextTask.getItemValueInteger("numprocessid"))); + workitem.task(itemColNextTask.getItemValueInteger("numprocessid")); if (debug) { - logger.log(Level.FINEST, "......new $taskID={0}", documentResult.getTaskID()); + logger.log(Level.FINEST, "......new $taskID={0}", workitem.getTaskID()); } - documentResult.replaceItemValue(WORKFLOWSTATUS, itemColNextTask.getItemValueString("txtname")); - documentResult.replaceItemValue(WORKFLOWGROUP, itemColNextTask.getItemValueString("txtworkflowgroup")); + workitem.replaceItemValue(WORKFLOWSTATUS, itemColNextTask.getItemValueString("txtname")); + workitem.replaceItemValue(WORKFLOWGROUP, itemColNextTask.getItemValueString("txtworkflowgroup")); if (debug) { logger.log(Level.FINEST, "......new $workflowStatus={0}", - documentResult.getItemValueString(WORKFLOWSTATUS)); + workitem.getItemValueString(WORKFLOWSTATUS)); } // update deprecated attributes txtworkflowStatus and txtworkflowGroup - documentResult.replaceItemValue("txtworkflowStatus", documentResult.getItemValueString(WORKFLOWSTATUS)); - documentResult.replaceItemValue("txtworkflowGroup", documentResult.getItemValueString(WORKFLOWGROUP)); + workitem.replaceItemValue("txtworkflowStatus", workitem.getItemValueString(WORKFLOWSTATUS)); + workitem.replaceItemValue("txtworkflowGroup", workitem.getItemValueString(WORKFLOWGROUP)); // update the type attribute if defined. // the type attribute can only be overwritten by a plug-in if the type is not // defined by the task! String sType = itemColNextTask.getItemValueString("txttype"); if (!"".equals(sType)) { - documentResult.replaceItemValue(TYPE, sType); + workitem.replaceItemValue(TYPE, sType); } } @@ -1128,30 +1131,33 @@ private ItemCollection createVersion(ItemCollection sourceItemCollection) throws * ($ActivityIDList) The activity list may not contain 0 values. * */ - @SuppressWarnings("unchecked") - private ItemCollection appendActivityID(final ItemCollection documentContext, final int aID) { - boolean debug = logger.isLoggable(Level.FINE); - ItemCollection documentResult = documentContext; - // check if activityidlist is available - List vActivityList = (List) documentContext.getItemValue(ACTIVITYIDLIST); - // clear list? - if ((vActivityList.size() == 1) && ("".equals(vActivityList.get(0).toString()))) - vActivityList = new Vector(); - - vActivityList.add(Integer.valueOf(aID)); - - // remove 0 values if contained! - while (vActivityList.indexOf(Integer.valueOf(0)) > -1) { - vActivityList.remove(vActivityList.indexOf(Integer.valueOf(0))); - } + // @SuppressWarnings("unchecked") + // private ItemCollection appendActivityID(final ItemCollection documentContext, + // final int aID) { + // boolean debug = logger.isLoggable(Level.FINE); + // ItemCollection documentResult = documentContext; + // // check if activityidlist is available + // List vActivityList = (List) + // documentContext.getItemValue(ACTIVITYIDLIST); + // // clear list? + // if ((vActivityList.size() == 1) && + // ("".equals(vActivityList.get(0).toString()))) + // vActivityList = new Vector(); + + // vActivityList.add(Integer.valueOf(aID)); + + // // remove 0 values if contained! + // while (vActivityList.indexOf(Integer.valueOf(0)) > -1) { + // vActivityList.remove(vActivityList.indexOf(Integer.valueOf(0))); + // } - documentResult.replaceItemValue(ACTIVITYIDLIST, vActivityList); - if (debug) { - logger.log(Level.FINEST, "......append new Activity ID={0}", aID); - } + // documentResult.replaceItemValue(ACTIVITYIDLIST, vActivityList); + // if (debug) { + // logger.log(Level.FINEST, "......append new Activity ID={0}", aID); + // } - return documentResult; - } + // return documentResult; + // } /** * This method is responsible for the internal workflow log. The attribute @@ -1214,7 +1220,6 @@ private ItemCollection logEvent(final ItemCollection documentContext, final Item } documentResult.replaceItemValue("$eventlog", logEntries); - documentResult.replaceItemValue("$lastEvent", Integer.valueOf(event.getItemValueInteger("numactivityid"))); // deprecated documentResult.replaceItemValue("numlastactivityid", diff --git a/imixs-workflow-core/src/test/java/org/imixs/workflow/TestWorkflowKernel.java b/imixs-workflow-core/src/test/java/org/imixs/workflow/TestWorkflowKernel.java index 0b811796..bd6037de 100644 --- a/imixs-workflow-core/src/test/java/org/imixs/workflow/TestWorkflowKernel.java +++ b/imixs-workflow-core/src/test/java/org/imixs/workflow/TestWorkflowKernel.java @@ -66,9 +66,12 @@ public void setup() throws PluginException { } + /** + * This test tests the basic behavior of the WorkflowKernel process method. + */ @Test @Category(org.imixs.workflow.WorkflowKernel.class) - public void testProcess() { + public void testSimpleProcessingCycle() { BPMNModel model = null; // Load Model @@ -80,18 +83,17 @@ public void testProcess() { Assert.fail(); } - ItemCollection itemCollectionProcessed = null; - ItemCollection itemCollection = new ItemCollection(); - itemCollection.model(OpenBPMNUtil.getVersion(model)) + ItemCollection workitemProcessed = null; + ItemCollection workItem = new ItemCollection(); + workItem.model(OpenBPMNUtil.getVersion(model)) .task(1000) .event(10); - itemCollection.replaceItemValue("txtTitel", "Hello"); + workItem.replaceItemValue("txtTitel", "Hello"); - Assert.assertEquals(itemCollection.getItemValueString("txttitel"), "Hello"); + Assert.assertEquals(workItem.getItemValueString("txttitel"), "Hello"); try { - itemCollectionProcessed = kernel.process(itemCollection); - + workitemProcessed = kernel.process(workItem); } catch (ModelException e) { Assert.fail(e.getMessage()); e.printStackTrace(); @@ -103,12 +105,29 @@ public void testProcess() { e.printStackTrace(); } - Assert.assertEquals(1, itemCollectionProcessed.getItemValueInteger("runs")); - Assert.assertEquals(1000, itemCollectionProcessed.getTaskID()); + Assert.assertEquals(1, workitemProcessed.getItemValueInteger("runs")); + Assert.assertEquals(1000, workitemProcessed.getTaskID()); // initial and processed workitems should be the same and should be equals! - Assert.assertSame(itemCollection, itemCollectionProcessed); - Assert.assertTrue(itemCollection.equals(itemCollectionProcessed)); + Assert.assertSame(workItem, workitemProcessed); + Assert.assertTrue(workItem.equals(workitemProcessed)); + + // the workitem should not have a $eventid + Assert.assertEquals(0, workItem.getItemValueInteger(WorkflowKernel.EVENTID)); + // a new call of process should throw a ProcessingErrorException + try { + workitemProcessed = kernel.process(workItem); + Assert.fail(); // we expect an Exception here! + } catch (ModelException e) { + Assert.fail(e.getMessage()); + e.printStackTrace(); + } catch (WorkflowException e) { + Assert.fail(); + e.printStackTrace(); + } catch (ProcessingErrorException e) { + // expected Exception! + } + } /** diff --git a/src/site/markdown/core/xml/transform_xml.md b/src/site/markdown/core/xml/transform_xml.md index 804724ec..2677c69e 100644 --- a/src/site/markdown/core/xml/transform_xml.md +++ b/src/site/markdown/core/xml/transform_xml.md @@ -135,12 +135,12 @@ See details about the xml format in the section [XML](../index.html). To transfo - $processid + $taskid 4000 - $activityid + $eventid 20 diff --git a/src/site/markdown/logging.md b/src/site/markdown/logging.md index e37c6590..b65c55f8 100644 --- a/src/site/markdown/logging.md +++ b/src/site/markdown/logging.md @@ -9,7 +9,7 @@ Loglevel INFO shows general information about the WorkflowService. **Example:** [org.imixs.workflow.bpmn.BPMNParser] (default task-3) BPMN Model 'system-en-1.0.0' parsed in 21ms - [org.imixs.workflow.WorkflowKernel] (default task-27) processing=85676103-43d9-4193-a8ce-30c96f0f7b31, MODELVERSION=2.0.0, $processid=1100, $activityid=10 + [org.imixs.workflow.WorkflowKernel] (default task-27) processing=85676103-43d9-4193-a8ce-30c96f0f7b31, MODELVERSION=2.0.0, $taskid=1100, $eventid=10 ## Loglevel FINE