From 862c634b0b63520b8b3a2cdad54cb29c3759fff0 Mon Sep 17 00:00:00 2001 From: Ralph Soika Date: Thu, 4 May 2023 18:42:02 +0200 Subject: [PATCH] impl, added docu Issue #828 --- .../workflow/engine/EventLogService.java | 15 ++- .../engine/plugins/EventLogPlugin.java | 118 ++++++++++++++++++ src/site/markdown/engine/eventlogservice.md | 85 ++++++++----- 3 files changed, 176 insertions(+), 42 deletions(-) create mode 100644 imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/plugins/EventLogPlugin.java diff --git a/imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/EventLogService.java b/imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/EventLogService.java index b108c049c..2844819ef 100644 --- a/imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/EventLogService.java +++ b/imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/EventLogService.java @@ -36,12 +36,11 @@ import java.util.logging.Level; import java.util.logging.Logger; -import jakarta.annotation.security.DeclareRoles; -import jakarta.annotation.security.RolesAllowed; - import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.jpa.EventLog; +import jakarta.annotation.security.DeclareRoles; +import jakarta.annotation.security.RolesAllowed; import jakarta.ejb.Stateless; import jakarta.ejb.TransactionAttribute; import jakarta.ejb.TransactionAttributeType; @@ -81,11 +80,11 @@ */ @DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", - "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", - "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) + "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", + "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) @RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", - "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", - "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) + "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", + "org.imixs.ACCESSLEVEL.MANAGERACCESS" }) @Stateless public class EventLogService { @@ -208,7 +207,7 @@ public List findEventsByTopic(int maxCount, String... topic) { /** * Finds events for one or many given topics within the current timeout. *

- * The attribte 'timeout' is optional. If the timeout is set to a future point + * The attribute 'timeout' is optional. If the timeout is set to a future point * of time, the event will be ignored by this method. * * @param maxCount - maximum count of events to be returned diff --git a/imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/plugins/EventLogPlugin.java b/imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/plugins/EventLogPlugin.java new file mode 100644 index 000000000..4f7a5112a --- /dev/null +++ b/imixs-workflow-engine/src/main/java/org/imixs/workflow/engine/plugins/EventLogPlugin.java @@ -0,0 +1,118 @@ +/* + * Imixs-Workflow + * + * Copyright (C) 2001-2020 Imixs Software Solutions GmbH, + * http://www.imixs.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You can receive a copy of the GNU General Public + * License at http://www.gnu.org/licenses/gpl.html + * + * Project: + * https://www.imixs.org + * https://github.com/imixs/imixs-workflow + * + * Contributors: + * Imixs Software Solutions GmbH - Project Management + * Ralph Soika - Software Developer + */ + +package org.imixs.workflow.engine.plugins; + +import java.util.Calendar; +import java.util.logging.Logger; + +import org.imixs.workflow.ItemCollection; +import org.imixs.workflow.engine.EventLogService; +import org.imixs.workflow.exceptions.PluginException; +import org.imixs.workflow.util.XMLParser; + +import jakarta.inject.Inject; + +/** + * The Imixs EventLog plugin can be used to create a EventLog entry during + * processing an event. The plugin can be configured by the activity + * result : + *

+ * Example: + *

+ * + *

+  {@code
+   
+	$uniqueid
+	60000
+        
+             500.00
+             Finance
+        
+ 
+    }
+ * 
+ *

+ * + * An EventLog entry can be processed by internal or external services. + * See: https://www.imixs.org/doc/engine/eventlogservice.html + * + * @author rsoika + * + */ +public class EventLogPlugin extends AbstractPlugin { + public static final String INVALID_FORMAT = "INVALID_FORMAT"; + + private static Logger logger = Logger.getLogger(EventLogPlugin.class.getName()); + + @Inject + EventLogService eventLogService; + + @Override + public ItemCollection run(ItemCollection documentContext, ItemCollection event) throws PluginException { + + // parse for eventlog definition.... + ItemCollection eventLogConfig = this.getWorkflowService().evalWorkflowResult(event, "eventlog", documentContext, + true); + if (eventLogConfig == null || eventLogConfig.getItemNames().size() == 0) { + // no op - return + return documentContext; + } + + // now iterate over all taxonomy definitions.... + for (String name : eventLogConfig.getItemNames()) { + String xmlDef = eventLogConfig.getItemValueString(name); + ItemCollection eventLogData = XMLParser.parseItemStructure(xmlDef); + + if (eventLogData != null) { + String ref = eventLogData.getItemValueString("ref"); + String documentXML = eventLogData.getItemValueString("document"); + long timeout = eventLogData.getItemValueLong("timeout"); + Calendar cal = null; + // create event log entry... + if (timeout > 0) { + cal = Calendar.getInstance(); + cal.setTimeInMillis(System.currentTimeMillis() + timeout); + } + // do we have a document definition? + if (!documentXML.isEmpty()) { + ItemCollection docData = XMLParser.parseItemStructure(documentXML); + // write eventLog entry... + eventLogService.createEvent(name, ref, docData.getAllItems(), cal); + } else { + // write eventLog entry... + eventLogService.createEvent(name, ref, cal); + } + } + } + + return documentContext; + } + +} diff --git a/src/site/markdown/engine/eventlogservice.md b/src/site/markdown/engine/eventlogservice.md index b35a4663b..d07afd4a7 100644 --- a/src/site/markdown/engine/eventlogservice.md +++ b/src/site/markdown/engine/eventlogservice.md @@ -1,64 +1,81 @@ -# The EventLogService - +# The EventLogService + The EventLogService is used to create and manage event log entries within a running transaction. EventLog entries can be processed by a client in a secure, asynchronous and transactional manner. Only in case the transaction from the source event was committed successfully, the event log entry can be read by a client. For example the LuceneUpdateService uses an event log entry to update the index only if a transaction was successful completed. This is also known as the 'Change Data Capture' design pattern. ## Change Data Capture (CDC) -An event that occurs during an update or a processing function within a transaction becomes a fact when the transaction completes successfully. The EventLogService can be used to create this kind of "Change Data Capture" events. -For example the LuceneUpdateService should update the index of a document only if the document was successfully written to the database. This can be ensured with the help of the EventLogService. +An event that occurs during an update or a processing function within a transaction becomes a fact when the transaction completes successfully. The EventLogService can be used to create this kind of "Change Data Capture" events. +For example the LuceneUpdateService should update the index of a document only if the document was successfully written to the database. This can be ensured with the help of the EventLogService. The service is bound to the current PersistenceContext and stores a EventLog entities directly into the database. An EventLog entry can be queried by clients through this service. - @EJB - EventLogService eventLogService; - .... - // BEGIN Transaction A - eventLogService.createEvent(workitem.getUniqueID(), "MY_TOPIC"); - .... - // END Transaction A - ....... - - - // BEGIN Transaction B - List eventList = eventLogService.findEvents("MY_TOPIC",100); - for (org.imixs.workflow.engine.jpa.EventLog eventLogEntry : eventList) { - // the event created in transaction A is no visible... - .... - } - // END Transaction B + @EJB + EventLogService eventLogService; + .... + // BEGIN Transaction A + eventLogService.createEvent(workitem.getUniqueID(), "MY_TOPIC"); + .... + // END Transaction A + ....... + + + // BEGIN Transaction B + List eventList = eventLogService.findEvents("MY_TOPIC",100); + for (org.imixs.workflow.engine.jpa.EventLog eventLogEntry : eventList) { + // the event created in transaction A is no visible... + .... + } + // END Transaction B Typically a new EventLog entity is created within the same transaction of the main processing or update life cycle. With this mechanism a client can be sure that eventLogEntries returned by the EventLogService are created during a committed Transaction. If the transaction was rolled back for some reason, the EventLog entry will never be written to the database. - - + ## The EventLog Entity The EventLog entity describes a unique event created during the processing life-cycle of a workitem or the update life-cycle of a Document. An EventLog is an immutable entity. The object contains the following properties: - * id - identifier for the event log entry - * ref - the reference id of the corresponding workitem or document entity - * topic - the topic of the eventlog - * created - the creation timestamp - * data - an optional data field - * timeout - an optional timestamp indicated the earliest processing time. +- id - identifier for the event log entry +- ref - the reference id of the corresponding workitem or document entity +- topic - the topic of the eventlog +- created - the creation timestamp +- data - an optional data field +- timeout - an optional timestamp indicated the earliest processing time. The 'data' attribute of an eventLog is optional and can hold any kind of event specific data (e.g. a Mail Message). -**Note:** for the same document reference ($uniqueid) there can exist different eventlog entries. Eventlog entries are unique over there internal ID. You can use the method _findEventsByRef_ to verify if a event log entry for a defined Reference was already created by another transaction. +**Note:** for the same document reference ($uniqueid) there can exist different eventlog entries. Eventlog entries are unique over there internal ID. You can use the method _findEventsByRef_ to verify if a event log entry for a defined Reference was already created by another transaction. +## The EventLogPlugin -## TransactionID +The Imixs Plugin class ` org.imixs.workflow.engine.plugins.EventLogPlugin` can be used to create a EventLog entry during +processing an event. The plugin can be configured by the activity result : -In different to a document entity, a workitem entity hold a *$transactionID* identifying the last processing life-cycle. When an eventLog entry is consumed asynchronous, a client may verify the *$transactionID* item stored in the data field with the last *$transactionID* stored in the workitem. In case the *$transactionID* has changed a client can discard the eventLog entry. This mechanism ensures that an eventLog entity is tied to a specific unique transaction only. +Example: + +``` + + $uniqueid + 60000 + + 500.00 + Finance + + +``` + +This definition will create a new EventLog entry with the topic `MY-TOPIC` pointing to the current workitem. The EventLog entry also includes an optional data structure with the items 'amount' and 'department'. + +## TransactionID -For example an AsyncEvent must only be executed if the *$transactionID* matches the current workitem status. +In different to a document entity, a workitem entity hold a _$transactionID_ identifying the last processing life-cycle. When an eventLog entry is consumed asynchronous, a client may verify the _$transactionID_ item stored in the data field with the last _$transactionID_ stored in the workitem. In case the _$transactionID_ has changed a client can discard the eventLog entry. This mechanism ensures that an eventLog entity is tied to a specific unique transaction only. +For example an AsyncEvent must only be executed if the _$transactionID_ matches the current workitem status. ## Timeout -The optional data attribute *timeout* ..... \ No newline at end of file +The optional data attribute _timeout_ of an EventLog entry can be used to delay its execution. The EventLogService method `findEventsByTimeout` can request only eventLogEntries with a timeout indicator which are in due.