This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new c91b694e69 issue #1866 (Automated XML serialization of WorkflowMeta)
(#6943)
c91b694e69 is described below
commit c91b694e695ba3c6d0f35575dd007798dc0a8b59
Author: Matt Casters <[email protected]>
AuthorDate: Fri Apr 3 08:31:27 2026 +0200
issue #1866 (Automated XML serialization of WorkflowMeta) (#6943)
* issue #1866 (Automated XML serialization of WorkflowMeta)
* issue #1866 (Automated XML serialization of WorkflowMeta)
---
.../main/java/org/apache/hop/base/BaseHopMeta.java | 14 +-
.../org/apache/hop/pipeline/PipelineHopMeta.java | 11 +
.../hop/pipeline/transforms/missing/Missing.java | 21 +-
.../org/apache/hop/workflow/WorkflowHopMeta.java | 14 +
.../java/org/apache/hop/workflow/WorkflowMeta.java | 739 +++++++--------------
.../org/apache/hop/workflow/action/ActionBase.java | 16 +-
.../org/apache/hop/workflow/action/ActionMeta.java | 29 +-
.../org/apache/hop/workflow/action/IAction.java | 24 +
.../org/apache/hop/workflow/WorkflowMetaTest.java | 117 +++-
.../apache/hop/workflow/actions/ActionFake.java} | 49 +-
.../mdi/files/excelwriter-mdi-test.xlsx | Bin 3311 -> 3310 bytes
plugins/misc/git/pom.xml | 12 +
.../test/java/org/apache/hop/git/HopDiffTest.java | 14 +-
plugins/misc/git/src/test/resources/r1.hwf | 4 +-
plugins/misc/git/src/test/resources/r2.hwf | 4 +-
.../hop/ui/workflow/dialog/WorkflowDialog.java | 41 +-
16 files changed, 485 insertions(+), 624 deletions(-)
diff --git a/engine/src/main/java/org/apache/hop/base/BaseHopMeta.java
b/engine/src/main/java/org/apache/hop/base/BaseHopMeta.java
index e37cd6998a..fe3011bc5d 100644
--- a/engine/src/main/java/org/apache/hop/base/BaseHopMeta.java
+++ b/engine/src/main/java/org/apache/hop/base/BaseHopMeta.java
@@ -35,11 +35,13 @@ public abstract class BaseHopMeta<T> {
protected boolean split = false;
- @HopMetadataProperty(key = "from", storeWithName = true, lookupInList =
"transforms")
- protected T from;
+ protected abstract T getFrom();
- @HopMetadataProperty(key = "to", storeWithName = true, lookupInList =
"transforms")
- protected T to;
+ protected abstract void setFrom(T from);
+
+ protected abstract T getTo();
+
+ protected abstract void setTo(T to);
@HopMetadataProperty(key = "enabled")
protected boolean enabled;
@@ -52,8 +54,8 @@ public abstract class BaseHopMeta<T> {
protected BaseHopMeta(
boolean split, T from, T to, boolean enabled, boolean changed, boolean
errorHop) {
this.split = split;
- this.from = from;
- this.to = to;
+ setFrom(from);
+ setTo(to);
this.enabled = enabled;
this.changed = changed;
this.errorHop = errorHop;
diff --git a/engine/src/main/java/org/apache/hop/pipeline/PipelineHopMeta.java
b/engine/src/main/java/org/apache/hop/pipeline/PipelineHopMeta.java
index 25712058fd..b6be07ee76 100644
--- a/engine/src/main/java/org/apache/hop/pipeline/PipelineHopMeta.java
+++ b/engine/src/main/java/org/apache/hop/pipeline/PipelineHopMeta.java
@@ -19,21 +19,32 @@ package org.apache.hop.pipeline;
import java.util.List;
import java.util.Objects;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.hop.base.BaseHopMeta;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopXmlException;
import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
import org.apache.hop.metadata.serializer.xml.XmlMetadataUtil;
import org.apache.hop.pipeline.transform.TransformMeta;
import org.w3c.dom.Node;
/** Defines a link between 2 transforms in a pipeline */
+@Getter
+@Setter
public class PipelineHopMeta extends BaseHopMeta<TransformMeta>
implements Comparable<PipelineHopMeta>, Cloneable {
private static final Class<?> PKG = Pipeline.class;
public static final String CONST_SPACES = " ";
+ @HopMetadataProperty(key = "from", storeWithName = true, lookupInList =
"transforms")
+ protected TransformMeta from;
+
+ @HopMetadataProperty(key = "to", storeWithName = true, lookupInList =
"transforms")
+ protected TransformMeta to;
+
public PipelineHopMeta(TransformMeta from, TransformMeta to, boolean en) {
this.from = from;
this.to = to;
diff --git
a/engine/src/main/java/org/apache/hop/pipeline/transforms/missing/Missing.java
b/engine/src/main/java/org/apache/hop/pipeline/transforms/missing/Missing.java
index b6522a8c2b..3f50fae925 100644
---
a/engine/src/main/java/org/apache/hop/pipeline/transforms/missing/Missing.java
+++
b/engine/src/main/java/org/apache/hop/pipeline/transforms/missing/Missing.java
@@ -17,10 +17,13 @@
package org.apache.hop.pipeline.transforms.missing;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.hop.pipeline.transform.BaseTransformMeta;
+@Getter
+@Setter
public class Missing extends BaseTransformMeta<MissingTransform, MissingData> {
-
private String transformName;
private String missingPluginId;
@@ -28,20 +31,4 @@ public class Missing extends
BaseTransformMeta<MissingTransform, MissingData> {
this.transformName = transformName;
this.missingPluginId = missingPluginId;
}
-
- public String getTransformName() {
- return transformName;
- }
-
- public void setTransformName(String transformName) {
- this.transformName = transformName;
- }
-
- public String getMissingPluginId() {
- return missingPluginId;
- }
-
- public void setMissingPluginId(String missingPluginId) {
- this.missingPluginId = missingPluginId;
- }
}
diff --git a/engine/src/main/java/org/apache/hop/workflow/WorkflowHopMeta.java
b/engine/src/main/java/org/apache/hop/workflow/WorkflowHopMeta.java
index 0f491c3368..2b33ca3550 100644
--- a/engine/src/main/java/org/apache/hop/workflow/WorkflowHopMeta.java
+++ b/engine/src/main/java/org/apache/hop/workflow/WorkflowHopMeta.java
@@ -18,15 +18,20 @@
package org.apache.hop.workflow;
import java.util.List;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.hop.base.BaseHopMeta;
import org.apache.hop.core.Const;
import org.apache.hop.core.exception.HopXmlException;
import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.i18n.BaseMessages;
+import org.apache.hop.metadata.api.HopMetadataProperty;
import org.apache.hop.workflow.action.ActionMeta;
import org.w3c.dom.Node;
/** This class defines a hop from one action copy to another. */
+@Getter
+@Setter
public class WorkflowHopMeta extends BaseHopMeta<ActionMeta> implements
Cloneable {
private static final Class<?> PKG = WorkflowHopMeta.class;
@@ -34,7 +39,16 @@ public class WorkflowHopMeta extends BaseHopMeta<ActionMeta>
implements Cloneabl
public static final String XML_UNCONDITIONAL_TAG = "unconditional";
private static final String CONST_SPACE = " ";
+ @HopMetadataProperty(key = "from", storeWithName = true, lookupInList =
"workflowActions")
+ protected ActionMeta from;
+
+ @HopMetadataProperty(key = "to", storeWithName = true, lookupInList =
"workflowActions")
+ protected ActionMeta to;
+
+ @HopMetadataProperty(key = "evaluation")
private boolean evaluation;
+
+ @HopMetadataProperty(key = "unconditional")
private boolean unconditional;
public WorkflowHopMeta() {
diff --git a/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
b/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
index 8b08c5a0f9..be0d4c550f 100644
--- a/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
+++ b/engine/src/main/java/org/apache/hop/workflow/WorkflowMeta.java
@@ -31,7 +31,6 @@ import org.apache.commons.vfs2.FileName;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.hop.base.AbstractMeta;
-import org.apache.hop.base.BaseHopMeta;
import org.apache.hop.core.CheckResult;
import org.apache.hop.core.Const;
import org.apache.hop.core.HopVersionProvider;
@@ -40,7 +39,6 @@ import org.apache.hop.core.IProgressMonitor;
import org.apache.hop.core.NotePadMeta;
import org.apache.hop.core.ProgressNullMonitorListener;
import org.apache.hop.core.SqlStatement;
-import org.apache.hop.core.attributes.AttributesUtil;
import org.apache.hop.core.database.DatabaseMeta;
import org.apache.hop.core.exception.HopException;
import org.apache.hop.core.exception.HopFileException;
@@ -52,9 +50,7 @@ import org.apache.hop.core.file.IHasFilename;
import org.apache.hop.core.gui.Point;
import org.apache.hop.core.logging.LogChannel;
import org.apache.hop.core.logging.LoggingObjectType;
-import org.apache.hop.core.parameters.INamedParameters;
import org.apache.hop.core.parameters.NamedParameters;
-import org.apache.hop.core.parameters.UnknownParamException;
import org.apache.hop.core.reflection.StringSearchResult;
import org.apache.hop.core.reflection.StringSearcher;
import org.apache.hop.core.util.StringUtil;
@@ -67,6 +63,7 @@ import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.i18n.BaseMessages;
import org.apache.hop.metadata.api.HopMetadataProperty;
import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.metadata.serializer.xml.XmlMetadataUtil;
import org.apache.hop.resource.IResourceExport;
import org.apache.hop.resource.IResourceNaming;
import org.apache.hop.resource.ResourceDefinition;
@@ -74,15 +71,18 @@ import org.apache.hop.resource.ResourceReference;
import org.apache.hop.workflow.action.ActionMeta;
import org.apache.hop.workflow.action.IAction;
import org.apache.hop.workflow.actions.missing.MissingAction;
+import org.jspecify.annotations.NonNull;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* The definition of a Hop workflow is represented by a WorkflowMeta object.
It is typically loaded
- * from a .hwf file or it is generated dynamically. The declared parameters of
the workflow
+ * from a .hwf file, or it is generated dynamically. The declared parameters
of the workflow
* definition are then queried using listParameters() and assigned values
using calls to
- * setParameterValue(..). WorkflowMeta provides methods to load, save, verify,
etc.
+ * setParameterValue(). WorkflowMeta provides methods to load, save, verify,
etc.
*/
+@Getter
+@Setter
public class WorkflowMeta extends AbstractMeta
implements Cloneable, Comparable<WorkflowMeta>, IXml, IResourceExport,
IHasFilename {
public static final String WORKFLOW_EXTENSION = ".hwf";
@@ -106,19 +106,21 @@ public class WorkflowMeta extends AbstractMeta
private static final String CONST_PARAMETER = "parameter";
private static final String CONST_SPACE = " ";
- /**
- * List of booleans indicating whether or not to remember the size and
position of the different
- * windows...
- */
- public boolean[] max = new boolean[1];
-
@HopMetadataProperty(inline = true)
protected WorkflowMetaInfo info;
+ @HopMetadataProperty(key = "workflow_version")
protected String workflowVersion;
- protected int workflowStatus;
+
+ @HopMetadataProperty(key = "parameters")
+ protected NamedParameters namedParameters;
+
+ @HopMetadataProperty(key = "action", groupKey = "actions")
protected List<ActionMeta> workflowActions;
+
+ @HopMetadataProperty(key = "hop", groupKey = "hops")
protected List<WorkflowHopMeta> workflowHops;
+
protected String[] arguments;
protected boolean changedActions;
protected boolean changedHops;
@@ -130,43 +132,41 @@ public class WorkflowMeta extends AbstractMeta
private List<MissingAction> missingActions;
- @Getter @Setter protected INamedParameters namedParameters;
-
/** Instantiates a new workflow meta. */
public WorkflowMeta() {
clear();
}
/**
- * Instantiates a new workflow meta.
+ * Loads metadata from XML into this object.
*
- * @param fname the fname
- * @throws HopXmlException the hop xml exception
+ * @param filename the filename
+ * @throws HopXmlException In case something goes wrong loading XML content.
*/
- public WorkflowMeta(String fname) throws HopXmlException {
- this(null, fname, null);
+ public WorkflowMeta(String filename) throws HopXmlException {
+ this(null, filename, null);
}
/**
* Load the workflow from the XML file specified
*
- * @param variables
- * @param fname
- * @param metadataProvider
- * @throws HopXmlException
+ * @param variables The variables to use
+ * @param filename The file name
+ * @param metadataProvider The metadata provider
+ * @throws HopXmlException In case something goes wrong loading XML content.
*/
- public WorkflowMeta(IVariables variables, String fname, IHopMetadataProvider
metadataProvider)
+ public WorkflowMeta(IVariables variables, String filename,
IHopMetadataProvider metadataProvider)
throws HopXmlException {
this.metadataProvider = metadataProvider;
- loadXml(variables, fname, metadataProvider);
+ loadXml(variables, filename, metadataProvider);
}
/**
* Instantiates a new workflow meta.
*
* @param inputStream the input stream
- * @param variables
- * @throws HopXmlException the hop xml exception
+ * @param variables The variables to reference for the copyright header.
+ * @throws HopXmlException In case something goes wrong loading XML content.
*/
public WorkflowMeta(
InputStream inputStream, IHopMetadataProvider metadataProvider,
IVariables variables)
@@ -179,11 +179,11 @@ public class WorkflowMeta extends AbstractMeta
}
/**
- * Create a new WorkflowMeta object by loading it from a a DOM node.
+ * Create a new WorkflowMeta object by loading it from a DOM node.
*
* @param workflowNode The node to load from
- * @param variables
- * @throws HopXmlException
+ * @param variables The variables to reference for the copyright header.
+ * @throws HopXmlException In case something goes wrong loading XML content.
*/
public WorkflowMeta(
Node workflowNode, IHopMetadataProvider metadataProvider, IVariables
variables)
@@ -193,7 +193,7 @@ public class WorkflowMeta extends AbstractMeta
loadXml(workflowNode, null, metadataProvider, variables);
}
- /** Clears or reinitializes many of the WorkflowMeta properties. */
+ /** Clears or re-initializes many of the WorkflowMeta properties. */
@Override
public void clear() {
workflowActions = new ArrayList<>();
@@ -206,7 +206,6 @@ public class WorkflowMeta extends AbstractMeta
super.clear();
loopCache = new HashMap<>();
addDefaults();
- workflowStatus = -1;
workflowVersion = null;
}
@@ -252,7 +251,7 @@ public class WorkflowMeta extends AbstractMeta
}
/**
- * Compares this workflow's meta-data to the specified workflow's meta-data.
This method simply
+ * Compares this workflow's metadata to the specified workflow's metadata.
This method simply
* calls compare(this, o)
*
* @param o the o
@@ -261,13 +260,13 @@ public class WorkflowMeta extends AbstractMeta
* @see Comparable#compareTo(Object)
*/
@Override
- public int compareTo(WorkflowMeta o) {
+ public int compareTo(@NonNull WorkflowMeta o) {
return compare(this, o);
}
/**
- * Checks whether this workflow's meta-data object is equal to the specified
object. If the
- * specified object is not an instance of WorkflowMeta, false is returned.
Otherwise the method
+ * Checks whether this workflow's metadata object is equal to the specified
object. If the
+ * specified object is not an instance of WorkflowMeta, false is returned,
otherwise the method
* returns whether a call to compare() indicates equality (i.e. compare(this,
* (WorkflowMeta)obj)==0).
*
@@ -276,6 +275,7 @@ public class WorkflowMeta extends AbstractMeta
* @see #compare(WorkflowMeta, WorkflowMeta)
* @see Object#equals(Object)
*/
+ @Override
public boolean equals(Object obj) {
if (!(obj instanceof WorkflowMeta)) {
return false;
@@ -284,10 +284,15 @@ public class WorkflowMeta extends AbstractMeta
return compare(this, (WorkflowMeta) obj) == 0;
}
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
/**
- * Clones the workflow meta-data object.
+ * Clones the workflow metadata object.
*
- * @return a clone of the workflow meta-data object
+ * @return a clone of the workflow metadata object
* @see Object#clone()
*/
@Override
@@ -296,7 +301,7 @@ public class WorkflowMeta extends AbstractMeta
}
/**
- * Perform a real clone of the workflow meta-data object, including cloning
all lists and copying
+ * Perform a real clone of the workflow metadata object, including cloning
all lists and copying
* all values. If the doClear parameter is true, the clone will be cleared
of ALL values before
* the copy. If false, only the copied fields will be cleared.
*
@@ -369,113 +374,37 @@ public class WorkflowMeta extends AbstractMeta
/**
* Gets the XML representation of this workflow.
*
+ * @param variables The variables to use for checking the license header
inclusion.
* @return the XML representation of this workflow
* @throws HopException if any errors occur during generation of the XML
* @see IXml#getXml(IVariables)
- * @param variables
*/
@Override
public String getXml(IVariables variables) throws HopException {
-
- StringBuilder xml = new StringBuilder(500);
-
- xml.append(XmlHandler.getLicenseHeader(variables));
-
- xml.append(XmlHandler.openTag(XML_TAG)).append(Const.CR);
-
- xml.append(" ")
- .append(
- XmlHandler.addTagValue("name", getName())); // lossy if name is
sync'ed with filename
- xml.append(" ")
- .append(
- XmlHandler.addTagValue(
- "name_sync_with_filename",
info.isNameSynchronizedWithFilename()));
-
- xml.append(" ").append(XmlHandler.addTagValue(CONST_DESCRIPTION,
info.getDescription()));
- xml.append(" ")
- .append(XmlHandler.addTagValue("extended_description",
info.getExtendedDescription()));
- xml.append(" ").append(XmlHandler.addTagValue("workflow_version",
workflowVersion));
- if (workflowStatus >= 0) {
- xml.append(" ").append(XmlHandler.addTagValue("workflow_status",
workflowStatus));
- }
-
- xml.append(" ").append(XmlHandler.addTagValue("created_user",
info.getCreatedUser()));
- xml.append(" ")
- .append(
- XmlHandler.addTagValue("created_date",
XmlHandler.date2string(info.getCreatedDate())));
- xml.append(" ").append(XmlHandler.addTagValue("modified_user",
info.getModifiedUser()));
- xml.append(" ")
- .append(
- XmlHandler.addTagValue(
- "modified_date",
XmlHandler.date2string(info.getModifiedDate())));
-
- xml.append("
").append(XmlHandler.openTag(XML_TAG_PARAMETERS)).append(Const.CR);
- String[] parameters = listParameters();
- for (String parameter : parameters) {
- xml.append("
").append(XmlHandler.openTag(CONST_PARAMETER)).append(Const.CR);
- xml.append(CONST_SPACE).append(XmlHandler.addTagValue("name",
parameter));
- try {
- xml.append(CONST_SPACE)
- .append(XmlHandler.addTagValue("default_value",
getParameterDefault(parameter)));
- xml.append(CONST_SPACE)
- .append(XmlHandler.addTagValue(CONST_DESCRIPTION,
getParameterDescription(parameter)));
- } catch (UnknownParamException e) {
- // skip the default value and/or description. This exception should
never happen because we
- // use listParameters()
- // above.
- }
- xml.append("
").append(XmlHandler.closeTag(CONST_PARAMETER)).append(Const.CR);
- }
- xml.append("
").append(XmlHandler.closeTag(XML_TAG_PARAMETERS)).append(Const.CR);
-
- xml.append("
").append(XmlHandler.openTag(XML_TAG_ACTIONS)).append(Const.CR);
- for (int i = 0; i < nrActions(); i++) {
- ActionMeta jge = getAction(i);
- xml.append(jge.getXml());
- }
- xml.append("
").append(XmlHandler.closeTag(XML_TAG_ACTIONS)).append(Const.CR);
-
- xml.append(" ").append(XmlHandler.openTag(XML_TAG_HOPS)).append(Const.CR);
- for (WorkflowHopMeta hi : workflowHops) {
- // Look at all the hops
- xml.append(hi.getXml());
- }
- xml.append("
").append(XmlHandler.closeTag(XML_TAG_HOPS)).append(Const.CR);
-
- xml.append("
").append(XmlHandler.openTag(XML_TAG_NOTEPADS)).append(Const.CR);
- for (int i = 0; i < nrNotes(); i++) {
- NotePadMeta ni = getNote(i);
- xml.append(ni.getXml());
- }
- xml.append("
").append(XmlHandler.closeTag(XML_TAG_NOTEPADS)).append(Const.CR);
-
- // Also store the attribute groups
- //
- xml.append(AttributesUtil.getAttributesXml(attributesMap));
-
- xml.append(XmlHandler.closeTag(XML_TAG)).append(Const.CR);
-
- return XmlFormatter.format(xml.toString());
+ return XmlHandler.getLicenseHeader(variables)
+ + XmlFormatter.format(
+ XmlHandler.aroundTag(XML_TAG,
XmlMetadataUtil.serializeObjectToXml(this)));
}
- public void loadXml(IVariables variables, String fname, IHopMetadataProvider
metadataProvider)
+ public void loadXml(IVariables variables, String filename,
IHopMetadataProvider metadataProvider)
throws HopXmlException {
try {
// OK, try to load using the VFS stuff...
- Document doc = XmlHandler.loadXmlFile(HopVfs.getFileObject(fname));
+ Document doc = XmlHandler.loadXmlFile(HopVfs.getFileObject(filename));
if (doc != null) {
// The workflowNode
Node workflowNode = XmlHandler.getSubNode(doc, XML_TAG);
- loadXml(workflowNode, fname, metadataProvider, variables);
+ loadXml(workflowNode, filename, metadataProvider, variables);
} else {
throw new HopXmlException(
- BaseMessages.getString(PKG,
"WorkflowMeta.Exception.ErrorReadingFromXMLFile") + fname);
+ BaseMessages.getString(PKG,
"WorkflowMeta.Exception.ErrorReadingFromXMLFile")
+ + filename);
}
} catch (Exception e) {
throw new HopXmlException(
BaseMessages.getString(PKG,
"WorkflowMeta.Exception.UnableToLoadWorkflowFromXMLFile")
- + fname
+ + filename
+ "]",
e);
}
@@ -486,9 +415,9 @@ public class WorkflowMeta extends AbstractMeta
*
* @param workflowNode The node to load from
* @param filename The filename
- * @param metadataProvider the MetaStore to use
- * @param variables
- * @throws HopXmlException
+ * @param metadataProvider the metadata provider to use for referencing
external objects.
+ * @param variables The variables to reference for the copyright header.
+ * @throws HopXmlException In case something goes wrong loading XML content.
*/
public void loadXml(
Node workflowNode,
@@ -502,116 +431,47 @@ public class WorkflowMeta extends AbstractMeta
setFilename(filename);
- // get workflow info:
+ // De-serialize the XML using @HopMetadataProperty annotations
//
- info.setName(XmlHandler.getTagValue(workflowNode, "name"));
+ XmlMetadataUtil.deSerializeFromXml(
+ null, null, workflowNode, WorkflowMeta.class, this,
metadataProvider);
- info.setNameSynchronizedWithFilename(
- "Y".equalsIgnoreCase(XmlHandler.getTagValue(workflowNode,
"name_sync_with_filename")));
-
- // description
- info.setDescription(XmlHandler.getTagValue(workflowNode,
CONST_DESCRIPTION));
-
- // extended description
- info.setExtendedDescription(XmlHandler.getTagValue(workflowNode,
"extended_description"));
-
- // workflow version
- workflowVersion = XmlHandler.getTagValue(workflowNode,
"workflow_version");
-
- // workflow status
- workflowStatus = Const.toInt(XmlHandler.getTagValue(workflowNode,
"workflow_status"), -1);
-
- // Created user/date
- info.setCreatedUser(XmlHandler.getTagValue(workflowNode,
"created_user"));
- String createDate = XmlHandler.getTagValue(workflowNode, "created_date");
-
- if (createDate != null) {
- info.setCreatedDate(XmlHandler.stringToDate(createDate));
- }
-
- // Changed user/date
- info.setModifiedUser(XmlHandler.getTagValue(workflowNode,
"modified_user"));
- String modDate = XmlHandler.getTagValue(workflowNode, "modified_date");
- if (modDate != null) {
- info.setModifiedDate(XmlHandler.stringToDate(modDate));
- }
-
- // Read the named parameters.
- //
- Node paramsNode = XmlHandler.getSubNode(workflowNode,
XML_TAG_PARAMETERS);
- List<Node> paramNodes = XmlHandler.getNodes(paramsNode, CONST_PARAMETER);
- for (Node paramNode : paramNodes) {
- String parameterName = XmlHandler.getTagValue(paramNode, "name");
- String defaultValue = XmlHandler.getTagValue(paramNode,
"default_value");
- String description = XmlHandler.getTagValue(paramNode,
CONST_DESCRIPTION);
-
- addParameterDefinition(parameterName, defaultValue, description);
- }
-
- // read the action metadata
- //
- Node actionsNode = XmlHandler.getSubNode(workflowNode, XML_TAG_ACTIONS);
- List<Node> actionNodes = XmlHandler.getNodes(actionsNode,
ActionMeta.XML_TAG);
- for (Node actionNode : actionNodes) {
- ActionMeta actionMeta = new ActionMeta(actionNode, metadataProvider,
variables);
-
- if (actionMeta.isMissing()) {
- addMissingAction((MissingAction) actionMeta.getAction());
- }
- ActionMeta prev = findAction(actionMeta.getName());
- if (prev != null) {
- // See if the action already exists!
- // Replace previous version with this one: remove it first
- //
- int idx = indexOfAction(prev);
- removeAction(idx);
- }
- // Add the ActionCopy...
- addAction(actionMeta);
- }
-
- Node hopsNode = XmlHandler.getSubNode(workflowNode, XML_TAG_HOPS);
- List<Node> hopNodes = XmlHandler.getNodes(hopsNode,
BaseHopMeta.XML_HOP_TAG);
- for (Node hopNode : hopNodes) {
- WorkflowHopMeta hi = new WorkflowHopMeta(hopNode, this);
- workflowHops.add(hi);
- }
-
- // Read the notes...
- //
- Node notepadsNode = XmlHandler.getSubNode(workflowNode,
XML_TAG_NOTEPADS);
- List<Node> nodepadNodes = XmlHandler.getNodes(notepadsNode,
NotePadMeta.XML_TAG);
- for (Node notepadNode : nodepadNodes) {
- NotePadMeta ni = new NotePadMeta(notepadNode);
- notes.add(ni);
- }
-
- // Load the attribute groups map
- //
- attributesMap =
- AttributesUtil.loadAttributes(
- XmlHandler.getSubNode(workflowNode, AttributesUtil.XML_TAG));
+ lookupReferencesAfterLoading();
clearChanged();
ExtensionPointHandler.callExtensionPoint(
LogChannel.GENERAL, variables,
HopExtensionPoint.WorkflowMetaLoaded.id, this);
-
} catch (Exception e) {
throw new HopXmlException(
BaseMessages.getString(PKG,
"WorkflowMeta.Exception.UnableToLoadWorkflowFromXMLNode"), e);
}
}
+ /**
+ * After loading there can still be some references to other transforms or
indeed this pipeline
+ * that need to be set. This is happening here.
+ */
+ public void lookupReferencesAfterLoading() {
+ for (ActionMeta actionMeta : workflowActions) {
+ IAction action = actionMeta.getAction();
+
+ // Also set the parent workflow to which this action belongs.
+ // This is rarely used, for example in getTableFields.getTableFields()
+ //
+ action.setParentWorkflowMeta(this);
+ }
+ }
+
/**
* Gets the action copy.
*
* @param x the x
* @param y the y
- * @param iconsize the iconsize
+ * @param iconSize the iconSize
* @return the action copy
*/
- public ActionMeta getAction(int x, int y, int iconsize) {
+ public ActionMeta getAction(int x, int y, int iconSize) {
int i;
int s;
s = nrActions();
@@ -620,7 +480,7 @@ public class WorkflowMeta extends AbstractMeta
ActionMeta action = getAction(i);
Point p = action.getLocation();
- if (p != null && x >= p.x && x <= p.x + iconsize && y >= p.y && y <= p.y
+ iconsize) {
+ if (p != null && x >= p.x && x <= p.x + iconSize && y >= p.y && y <= p.y
+ iconSize) {
return action;
}
}
@@ -722,7 +582,6 @@ public class WorkflowMeta extends AbstractMeta
public void removeAction(int index) {
ActionMeta deleted = workflowActions.remove(index);
if (deleted != null) {
- // give transform a chance to cleanup
deleted.setParentWorkflowMeta(null);
if (deleted.getAction() instanceof MissingAction missingAction) {
removeMissingAction(missingAction);
@@ -1147,7 +1006,7 @@ public class WorkflowMeta extends AbstractMeta
hops.add(hop);
}
}
- return hops.toArray(new WorkflowHopMeta[hops.size()]);
+ return hops.toArray(new WorkflowHopMeta[0]);
}
public boolean isPathExist(IAction from, IAction to) {
@@ -1219,47 +1078,6 @@ public class WorkflowMeta extends AbstractMeta
return new Point(maxx + 100, maxy + 100);
}
- /**
- * Get the minimum point on the canvas of a workflow
- *
- * @return Minimum coordinate of a transform in the workflow
- */
- public Point getMinimum() {
- int minx = Integer.MAX_VALUE;
- int miny = Integer.MAX_VALUE;
- for (ActionMeta action : workflowActions) {
- Point loc = action.getLocation();
- if (loc.x < minx) {
- minx = loc.x;
- }
- if (loc.y < miny) {
- miny = loc.y;
- }
- }
- for (NotePadMeta note : getNotes()) {
- Point loc = note.getLocation();
- if (loc.x < minx) {
- minx = loc.x;
- }
- if (loc.y < miny) {
- miny = loc.y;
- }
- }
-
- if (minx > BORDER_INDENT && minx != Integer.MAX_VALUE) {
- minx -= BORDER_INDENT;
- } else {
- minx = 0;
- }
- if (miny > BORDER_INDENT && miny != Integer.MAX_VALUE) {
- miny -= BORDER_INDENT;
- } else {
- miny = 0;
- }
-
- return new Point(minx, miny);
- }
-
/**
* Gets the selected actions locations.
*
@@ -1289,7 +1107,7 @@ public class WorkflowMeta extends AbstractMeta
points.add(new Point(p.x, p.y)); // explicit copy of location
}
- return points.toArray(new Point[points.size()]);
+ return points.toArray(new Point[0]);
}
/**
@@ -1409,120 +1227,131 @@ public class WorkflowMeta extends AbstractMeta
* @return A list of StringSearchResult with strings used in the workflow
*/
public List<StringSearchResult> getStringList(
- boolean searchTransforms, boolean searchDatabases, boolean searchNotes) {
+ boolean searchActions, boolean searchDatabases, boolean searchNotes) {
List<StringSearchResult> stringList = new ArrayList<>();
- if (searchTransforms) {
- // Loop over all transforms in the pipeline and see what the used
- // vars are...
- for (int i = 0; i < nrActions(); i++) {
- ActionMeta actionMeta = getAction(i);
- stringList.add(
- new StringSearchResult(
- actionMeta.getName(),
- actionMeta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.ActionName")));
- if (actionMeta.getDescription() != null) {
- stringList.add(
- new StringSearchResult(
- actionMeta.getDescription(),
- actionMeta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.ActionDescription")));
- }
- IAction action = actionMeta.getAction();
- StringSearcher.findMetaData(action, 1, stringList, actionMeta, this);
- }
+ if (searchActions) {
+ // Loop over all actions in the workflow and see what the used variables
are etc.
+ //
+ getStringListFromActions(stringList);
}
// Loop over all transforms in the pipeline and see what the used vars
// are...
if (searchDatabases) {
- for (DatabaseMeta meta : getDatabases()) {
+ getStringListFromDatabases(stringList);
+ }
+
+ // Loop over all transforms in the pipeline and see what the used vars
+ // are...
+ if (searchNotes) {
+ getStringListFromNotes(stringList);
+ }
+
+ return stringList;
+ }
+
+ private void getStringListFromNotes(List<StringSearchResult> stringList) {
+ for (int i = 0; i < nrNotes(); i++) {
+ NotePadMeta meta = getNote(i);
+ if (meta.getNote() != null) {
stringList.add(
new StringSearchResult(
- meta.getName(),
+ meta.getNote(),
meta,
this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseConnectionName")));
- if (meta.getHostname() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getHostname(),
- meta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseHostName")));
- }
- if (meta.getDatabaseName() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getDatabaseName(),
- meta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseName")));
- }
- if (meta.getUsername() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getUsername(),
- meta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseUsername")));
- }
- if (meta.getPluginId() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getPluginId(),
- meta,
- this,
- BaseMessages.getString(
- PKG,
"WorkflowMeta.SearchMetadata.DatabaseTypeDescription")));
- }
- if (meta.getPort() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getPort(),
- meta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabasePort")));
- }
- if (meta.getServername() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getServername(),
- meta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseServer")));
- }
- if (meta.getPassword() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getPassword(),
- meta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabasePassword")));
- }
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.NotepadText")));
}
}
+ }
- // Loop over all transforms in the pipeline and see what the used vars
- // are...
- if (searchNotes) {
- for (int i = 0; i < nrNotes(); i++) {
- NotePadMeta meta = getNote(i);
- if (meta.getNote() != null) {
- stringList.add(
- new StringSearchResult(
- meta.getNote(),
- meta,
- this,
- BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.NotepadText")));
- }
+ private void getStringListFromDatabases(List<StringSearchResult> stringList)
{
+ for (DatabaseMeta meta : getDatabases()) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getName(),
+ meta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseConnectionName")));
+ if (meta.getHostname() != null) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getHostname(),
+ meta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseHostName")));
+ }
+ if (meta.getDatabaseName() != null) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getDatabaseName(),
+ meta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseName")));
+ }
+ if (meta.getUsername() != null) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getUsername(),
+ meta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseUsername")));
+ }
+ if (meta.getPluginId() != null) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getPluginId(),
+ meta,
+ this,
+ BaseMessages.getString(
+ PKG,
"WorkflowMeta.SearchMetadata.DatabaseTypeDescription")));
+ }
+ if (meta.getPort() != null) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getPort(),
+ meta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabasePort")));
+ }
+ if (meta.getServername() != null) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getServername(),
+ meta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabaseServer")));
+ }
+ if (meta.getPassword() != null) {
+ stringList.add(
+ new StringSearchResult(
+ meta.getPassword(),
+ meta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.DatabasePassword")));
}
}
+ }
- return stringList;
+ private void getStringListFromActions(List<StringSearchResult> stringList) {
+ for (ActionMeta actionMeta : workflowActions) {
+ stringList.add(
+ new StringSearchResult(
+ actionMeta.getName(),
+ actionMeta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.ActionName")));
+ if (actionMeta.getDescription() != null) {
+ stringList.add(
+ new StringSearchResult(
+ actionMeta.getDescription(),
+ actionMeta,
+ this,
+ BaseMessages.getString(PKG,
"WorkflowMeta.SearchMetadata.ActionDescription")));
+ }
+ IAction action = actionMeta.getAction();
+ StringSearcher.findMetaData(action, 1, stringList, actionMeta, this);
+ }
}
/**
@@ -1581,42 +1410,6 @@ public class WorkflowMeta extends AbstractMeta
return false;
}
- /**
- * Gets the version of the workflow.
- *
- * @return The version of the workflow
- */
- public String getWorkflowVersion() {
- return workflowVersion;
- }
-
- /**
- * Set the version of the workflow.
- *
- * @param jobVersion The new version description of the workflow
- */
- public void setWorkflowVersion(String jobVersion) {
- this.workflowVersion = jobVersion;
- }
-
- /**
- * Gets the status of the workflow.
- *
- * @return the status of the workflow
- */
- public int getWorkflowStatus() {
- return workflowStatus;
- }
-
- /**
- * Set the status of the workflow.
- *
- * @param status The new status description of the workflow
- */
- public void setWorkflowStatus(int status) {
- this.workflowStatus = status;
- }
-
/** This method sets various internal hop variables that can be used by the
pipeline. */
@Override
public void setInternalHopVariables(IVariables variables) {
@@ -1722,15 +1515,7 @@ public class WorkflowMeta extends AbstractMeta
BaseMessages.getString(PKG,
"WorkflowMeta.Monitor.VerifyingThisActionTask.Title"),
actions.size());
- // Check for the presence of a start action
- if (findStart() == null) {
- remarks.add(
- new CheckResult(
- ICheckResult.TYPE_RESULT_ERROR,
- BaseMessages.getString(
- PKG,
"WorkflowMeta.CheckResult.StartActionIsMissing.Description"),
- null));
- }
+ checkStartPresent(remarks);
int worked = 1;
for (ActionMeta actionMeta : actions) {
@@ -1740,42 +1525,7 @@ public class WorkflowMeta extends AbstractMeta
IAction action = actionMeta.getAction();
if (action != null) {
- monitor.subTask(
- BaseMessages.getString(
- PKG, "WorkflowMeta.Monitor.VerifyingAction.Title",
action.getName()));
-
- // Check missing action plugin
- if (action instanceof MissingAction missingAction) {
- remarks.add(
- new CheckResult(
- ICheckResult.TYPE_RESULT_ERROR,
- BaseMessages.getString(
- PKG,
-
"WorkflowMeta.CheckResult.ActionPluginNotFound.Description",
- missingAction.getMissingPluginId()),
- action));
- }
-
- // Check deprecated action plugin
- if (actionMeta.isDeprecated()) {
- remarks.add(
- new CheckResult(
- ICheckResult.TYPE_RESULT_WARNING,
- BaseMessages.getString(
- PKG,
"WorkflowMeta.CheckResult.DeprecatedActionPlugin.Description"),
- action));
- }
-
- if (isActionUsedInHops(actionMeta) || getActions().size() == 1) {
- action.check(remarks, this, variables, metadataProvider);
- } else {
- remarks.add(
- new CheckResult(
- ICheckResult.TYPE_RESULT_WARNING,
- BaseMessages.getString(
- PKG,
"WorkflowMeta.CheckResult.ActionIsNotUsed.Description"),
- action));
- }
+ checkAction(remarks, monitor, variables, metadataProvider,
actionMeta, action);
}
// Progress bar...
monitor.worked(worked++);
@@ -1787,6 +1537,62 @@ public class WorkflowMeta extends AbstractMeta
}
}
+ private void checkAction(
+ List<ICheckResult> remarks,
+ IProgressMonitor monitor,
+ IVariables variables,
+ IHopMetadataProvider metadataProvider,
+ ActionMeta actionMeta,
+ IAction action) {
+ monitor.subTask(
+ BaseMessages.getString(
+ PKG, "WorkflowMeta.Monitor.VerifyingAction.Title",
action.getName()));
+
+ // Check missing action plugin
+ if (action instanceof MissingAction missingAction) {
+ remarks.add(
+ new CheckResult(
+ ICheckResult.TYPE_RESULT_ERROR,
+ BaseMessages.getString(
+ PKG,
+ "WorkflowMeta.CheckResult.ActionPluginNotFound.Description",
+ missingAction.getMissingPluginId()),
+ action));
+ }
+
+ // Check deprecated action plugin
+ if (actionMeta.isDeprecated()) {
+ remarks.add(
+ new CheckResult(
+ ICheckResult.TYPE_RESULT_WARNING,
+ BaseMessages.getString(
+ PKG,
"WorkflowMeta.CheckResult.DeprecatedActionPlugin.Description"),
+ action));
+ }
+
+ if (isActionUsedInHops(actionMeta) || getActions().size() == 1) {
+ action.check(remarks, this, variables, metadataProvider);
+ } else {
+ remarks.add(
+ new CheckResult(
+ ICheckResult.TYPE_RESULT_WARNING,
+ BaseMessages.getString(PKG,
"WorkflowMeta.CheckResult.ActionIsNotUsed.Description"),
+ action));
+ }
+ }
+
+ private void checkStartPresent(List<ICheckResult> remarks) {
+ // Check for the presence of a start action
+ if (findStart() == null) {
+ remarks.add(
+ new CheckResult(
+ ICheckResult.TYPE_RESULT_ERROR,
+ BaseMessages.getString(
+ PKG,
"WorkflowMeta.CheckResult.StartActionIsMissing.Description"),
+ null));
+ }
+ }
+
/**
* Gets the resource dependencies.
*
@@ -1919,19 +1725,10 @@ public class WorkflowMeta extends AbstractMeta
return workflowActions;
}
- /**
- * Gets the workflow hops.
- *
- * @return the workflow hops
- */
- public List<WorkflowHopMeta> getWorkflowHops() {
- return workflowHops;
- }
-
/**
* Create a unique list of action interfaces
*
- * @return
+ * @return The list of unique actions
*/
public List<IAction> composeActionList() {
List<IAction> list = new ArrayList<>();
@@ -1967,10 +1764,6 @@ public class WorkflowMeta extends AbstractMeta
return workflowActions.contains(actionMeta);
}
- public List<MissingAction> getMissingActions() {
- return missingActions;
- }
-
public void addMissingAction(MissingAction missingAction) {
if (missingActions == null) {
missingActions = new ArrayList<>();
@@ -1989,44 +1782,10 @@ public class WorkflowMeta extends AbstractMeta
return !Utils.isEmpty(missingActions);
}
- public String getStartActionName() {
- return startActionName;
- }
-
- public void setStartActionName(String name) {
- this.startActionName = name;
- }
-
- public boolean isExpandingRemoteWorkflow() {
- return expandingRemoteWorkflow;
- }
-
- public void setExpandingRemoteWorkflow(boolean expandingRemoteWorkflow) {
- this.expandingRemoteWorkflow = expandingRemoteWorkflow;
- }
-
public boolean isEmpty() {
return nrActions() == 0 && nrNotes() == 0;
}
- /**
- * Gets info
- *
- * @return value of info
- */
- public WorkflowMetaInfo getInfo() {
- return info;
- }
-
- /**
- * Sets info
- *
- * @param info value of info
- */
- public void setInfo(WorkflowMetaInfo info) {
- this.info = info;
- }
-
/**
* Get the name of the workflow. If the name is synchronized with the
filename, we return the base
* filename.
diff --git
a/engine/src/main/java/org/apache/hop/workflow/action/ActionBase.java
b/engine/src/main/java/org/apache/hop/workflow/action/ActionBase.java
index 7357ce93f4..a71e7f1822 100644
--- a/engine/src/main/java/org/apache/hop/workflow/action/ActionBase.java
+++ b/engine/src/main/java/org/apache/hop/workflow/action/ActionBase.java
@@ -23,6 +23,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import lombok.Getter;
+import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.apache.hop.core.Const;
import org.apache.hop.core.IAttributes;
@@ -52,6 +54,7 @@ import org.apache.hop.core.util.Utils;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.core.variables.Variables;
import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.metadata.api.HopMetadataProperty;
import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.metadata.serializer.xml.XmlMetadataUtil;
import org.apache.hop.resource.IResourceHolder;
@@ -68,6 +71,8 @@ import org.w3c.dom.Node;
* does not implement IAction (although it implements most of the same
methods), so individual
* action classes must implement IAction and specifically the
<code>execute()</code> method.
*/
+@Getter
+@Setter
public abstract class ActionBase
implements IAction,
Cloneable,
@@ -78,12 +83,13 @@ public abstract class ActionBase
IResourceHolder {
/** The name of the action */
- private String name;
+ @HopMetadataProperty private String name;
/** The description of the action */
- private String description;
+ @HopMetadataProperty private String description;
/** ID as defined in the xml or annotation. */
+ @HopMetadataProperty(key = "type")
private String pluginId;
/** Whether the action has changed. */
@@ -109,6 +115,12 @@ public abstract class ActionBase
private IHopMetadataProvider metadataProvider;
+ @HopMetadataProperty(
+ key = "group",
+ groupKey = "attributes",
+ mapKeyWrapper = "name",
+ mapValueWrapper = "attribute",
+ mapValueClass = HashMap.class)
protected Map<String, Map<String, String>> attributesMap;
protected Map<String, Object> extensionDataMap;
diff --git
a/engine/src/main/java/org/apache/hop/workflow/action/ActionMeta.java
b/engine/src/main/java/org/apache/hop/workflow/action/ActionMeta.java
index a1eb6a9e67..1cf0e16f70 100644
--- a/engine/src/main/java/org/apache/hop/workflow/action/ActionMeta.java
+++ b/engine/src/main/java/org/apache/hop/workflow/action/ActionMeta.java
@@ -32,6 +32,8 @@ import org.apache.hop.core.plugins.IPlugin;
import org.apache.hop.core.plugins.PluginRegistry;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.metadata.api.HopMetadataProperty;
+import org.apache.hop.metadata.api.IHasName;
import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.pipeline.transform.copy.CopyContext;
import org.apache.hop.workflow.WorkflowMeta;
@@ -43,29 +45,40 @@ import org.w3c.dom.Node;
* This class describes the fact that a single Action can be used multiple
times in the same
* Workflow. Therefore, it contains a link to an Action, a position, a number,
etc.
*/
-public class ActionMeta implements Cloneable, IGuiPosition, IChanged,
IAttributes, IBaseMeta {
+public class ActionMeta
+ implements Cloneable, IGuiPosition, IChanged, IAttributes, IBaseMeta,
IHasName {
public static final String XML_TAG = "action";
private static final String XML_ATTRIBUTE_WORKFLOW_ACTION_COPY =
AttributesUtil.XML_TAG + "_hac";
private static final String CONST_SPACE = " ";
+ @HopMetadataProperty(inline = true)
private IAction action;
+ @HopMetadataProperty(inline = true)
+ private Point location;
+
+ /** Flag to indicate that the actions following this one are launched in
parallel */
+ @HopMetadataProperty(key = "parallel")
+ private boolean launchingInParallel;
+
+ /** protected for easy access by subclasses */
+ @HopMetadataProperty(
+ key = "group",
+ groupKey = XML_ATTRIBUTE_WORKFLOW_ACTION_COPY,
+ mapKeyWrapper = "name",
+ mapValueWrapper = "attribute",
+ mapValueClass = HashMap.class)
+ private Map<String, Map<String, String>> attributesMap;
+
private String suggestion = "";
private boolean selected;
private boolean isDeprecated;
- private Point location;
-
- /** Flag to indicate that the actions following this one are launched in
parallel */
- private boolean launchingInParallel;
-
private WorkflowMeta parentWorkflowMeta;
- private Map<String, Map<String, String>> attributesMap;
-
public ActionMeta() {
clear();
}
diff --git a/engine/src/main/java/org/apache/hop/workflow/action/IAction.java
b/engine/src/main/java/org/apache/hop/workflow/action/IAction.java
index 805992ca6f..91f18e1ead 100644
--- a/engine/src/main/java/org/apache/hop/workflow/action/IAction.java
+++ b/engine/src/main/java/org/apache/hop/workflow/action/IAction.java
@@ -19,6 +19,7 @@ package org.apache.hop.workflow.action;
import java.util.List;
import java.util.Map;
+import java.util.MissingResourceException;
import org.apache.hop.core.ICheckResult;
import org.apache.hop.core.ICheckResultSource;
import org.apache.hop.core.IExtensionData;
@@ -29,7 +30,11 @@ import org.apache.hop.core.exception.HopXmlException;
import org.apache.hop.core.file.IHasFilename;
import org.apache.hop.core.logging.IHasLogChannel;
import org.apache.hop.core.logging.ILogChannel;
+import org.apache.hop.core.plugins.ActionPluginType;
+import org.apache.hop.core.plugins.PluginRegistry;
import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.metadata.api.HopMetadataObject;
+import org.apache.hop.metadata.api.IHopMetadataObjectFactory;
import org.apache.hop.metadata.api.IHopMetadataProvider;
import org.apache.hop.resource.IResourceNaming;
import org.apache.hop.resource.ResourceDefinition;
@@ -127,6 +132,7 @@ import org.w3c.dom.Node;
* Finally, the passed in prevResult object must be returned.
* </ul>
*/
+@HopMetadataObject(xmlKey = "type", objectFactory =
IAction.ActionFactory.class)
public interface IAction extends IVariables, IHasLogChannel,
ICheckResultSource, IExtensionData {
/**
@@ -445,4 +451,22 @@ public interface IAction extends IVariables,
IHasLogChannel, ICheckResultSource,
default boolean supportsDrillDown() {
return false;
}
+
+ final class ActionFactory implements IHopMetadataObjectFactory {
+ @Override
+ public Object createObject(String id, Object parentObject) throws
HopException {
+ IAction action =
+ PluginRegistry.getInstance().loadClass(ActionPluginType.class, id,
IAction.class);
+ if (action == null) {
+ throw new MissingResourceException(
+ "Action plugin with ID '" + id + "' was not found",
IAction.class.getName(), id);
+ }
+ return action;
+ }
+
+ @Override
+ public String getObjectId(Object object) throws HopException {
+ return PluginRegistry.getInstance().getPluginId(ActionPluginType.class,
object);
+ }
+ }
}
diff --git a/engine/src/test/java/org/apache/hop/workflow/WorkflowMetaTest.java
b/engine/src/test/java/org/apache/hop/workflow/WorkflowMetaTest.java
index 9c2a1efac1..3e723c803d 100644
--- a/engine/src/test/java/org/apache/hop/workflow/WorkflowMetaTest.java
+++ b/engine/src/test/java/org/apache/hop/workflow/WorkflowMetaTest.java
@@ -30,15 +30,20 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import org.apache.hop.core.Const;
-import org.apache.hop.core.NotePadMeta;
+import org.apache.hop.core.annotations.Action;
import org.apache.hop.core.exception.HopException;
-import org.apache.hop.core.gui.Point;
+import org.apache.hop.core.exception.HopPluginException;
import org.apache.hop.core.listeners.IContentChangedListener;
+import org.apache.hop.core.plugins.ActionPluginType;
+import org.apache.hop.core.plugins.PluginRegistry;
import org.apache.hop.core.variables.IVariables;
import org.apache.hop.core.variables.Variables;
+import org.apache.hop.core.xml.XmlHandler;
import org.apache.hop.metadata.api.IHopMetadataProvider;
+import org.apache.hop.metadata.serializer.memory.MemoryMetadataProvider;
import org.apache.hop.workflow.action.ActionMeta;
import org.apache.hop.workflow.action.IAction;
+import org.apache.hop.workflow.actions.ActionFake;
import org.apache.hop.workflow.actions.dummy.ActionDummy;
import org.apache.hop.workflow.engine.IWorkflowEngine;
import org.apache.hop.workflow.engines.local.LocalWorkflowEngine;
@@ -56,7 +61,7 @@ class WorkflowMetaTest {
private IContentChangedListener listener;
@BeforeEach
- void setUp() {
+ void setUp() throws HopPluginException {
workflowMeta = new WorkflowMeta();
workflowMeta.setNameSynchronizedWithFilename(false);
// prepare
@@ -65,6 +70,9 @@ class WorkflowMetaTest {
workflowMeta.setName(WORKFLOW_META_NAME);
variables = new Variables();
+
+ PluginRegistry registry = PluginRegistry.getInstance();
+ registry.registerPluginClass(ActionFake.class.getName(),
ActionPluginType.class, Action.class);
}
@Test
@@ -125,35 +133,6 @@ class WorkflowMetaTest {
verifyNoMoreInteractions(listener);
}
- @Test
- void
shouldUseCoordinatesOfItsTransformsAndNotesWhenCalculatingMinimumPoint() {
- Point actionPoint = new Point(500, 500);
- Point notePadMetaPoint = new Point(400, 400);
- ActionMeta actionMeta = mock(ActionMeta.class);
- when(actionMeta.getLocation()).thenReturn(actionPoint);
- NotePadMeta notePadMeta = mock(NotePadMeta.class);
- when(notePadMeta.getLocation()).thenReturn(notePadMetaPoint);
-
- // empty Workflow return 0 coordinate point
- Point point = workflowMeta.getMinimum();
- assertEquals(0, point.x);
- assertEquals(0, point.y);
-
- // when Workflow contains a single transform or note, then workflowMeta
should return
- // coordinates of it, subtracting borders
- workflowMeta.addAction(0, actionMeta);
- Point actualTransformPoint = workflowMeta.getMinimum();
- assertEquals(actionPoint.x - WorkflowMeta.BORDER_INDENT,
actualTransformPoint.x);
- assertEquals(actionPoint.y - WorkflowMeta.BORDER_INDENT,
actualTransformPoint.y);
-
- // when Workflow contains transform or notes, then workflowMeta should
return minimal
- // coordinates of them, subtracting borders
- workflowMeta.addNote(notePadMeta);
- Point transformPoint = workflowMeta.getMinimum();
- assertEquals(notePadMetaPoint.x - WorkflowMeta.BORDER_INDENT,
transformPoint.x);
- assertEquals(notePadMetaPoint.y - WorkflowMeta.BORDER_INDENT,
transformPoint.y);
- }
-
@Test
void testEquals_oneNameNull() {
assertFalse(testEquals(null, null));
@@ -345,4 +324,78 @@ class WorkflowMetaTest {
"Original value defined at run execution",
variables.getVariable(Const.INTERNAL_VARIABLE_ENTRY_CURRENT_FOLDER));
}
+
+ @Test
+ void testXmlSerialization() throws Exception {
+ ActionFake fake1 = new ActionFake();
+ fake1.setSomeProperty("prop1");
+ ActionMeta a1 = new ActionMeta(fake1);
+ a1.setName("a1");
+ workflowMeta.addAction(a1);
+
+ ActionFake fake2 = new ActionFake();
+ fake2.setSomeProperty("prop2");
+ ActionMeta a2 = new ActionMeta(fake2);
+ a2.setName("a2");
+ workflowMeta.addAction(a2);
+
+ ActionFake fake3 = new ActionFake();
+ fake3.setSomeProperty("prop3");
+ ActionMeta a3 = new ActionMeta(fake3);
+ a3.setName("a3");
+ workflowMeta.addAction(a3);
+
+ WorkflowHopMeta a12 = new WorkflowHopMeta(a1, a2);
+ a12.setEvaluation(true);
+ a12.setUnconditional(false);
+ workflowMeta.addWorkflowHop(a12);
+ WorkflowHopMeta a23 = new WorkflowHopMeta(a2, a3);
+ a23.setEvaluation(false);
+ a23.setUnconditional(true);
+ workflowMeta.addWorkflowHop(a23);
+
+ // Serialize to XML
+ String xml = workflowMeta.getXml(new Variables());
+
+ // Re-inflate it
+ //
+ Node node = XmlHandler.loadXmlString(xml, WorkflowMeta.XML_TAG);
+ WorkflowMeta copy = new WorkflowMeta(node, new MemoryMetadataProvider(),
new Variables());
+
+ assertEquals(3, copy.nrActions());
+
+ ActionMeta copy1 = copy.getAction(0);
+ assertEquals("a1", copy1.getName());
+ assertNotNull(copy1.getAction());
+ if (copy1.getAction() instanceof ActionFake action) {
+ assertEquals("prop1", action.getSomeProperty());
+ }
+
+ ActionMeta copy2 = copy.getAction(1);
+ assertEquals("a2", copy2.getName());
+ assertNotNull(copy2.getAction());
+ if (copy2.getAction() instanceof ActionFake action) {
+ assertEquals("prop2", action.getSomeProperty());
+ }
+
+ ActionMeta copy3 = copy.getAction(2);
+ assertEquals("a3", copy3.getName());
+ assertNotNull(copy3.getAction());
+ if (copy3.getAction() instanceof ActionFake action) {
+ assertEquals("prop3", action.getSomeProperty());
+ }
+
+ assertEquals(2, copy.nrWorkflowHops());
+ WorkflowHopMeta copy12 = copy.getWorkflowHop(0);
+ assertTrue(copy12.isEvaluation());
+ assertFalse(copy12.isUnconditional());
+ assertEquals(copy1, copy12.getFrom());
+ assertEquals(copy2, copy12.getTo());
+
+ WorkflowHopMeta copy23 = copy.getWorkflowHop(1);
+ assertFalse(copy23.isEvaluation());
+ assertTrue(copy23.isUnconditional());
+ assertEquals(copy2, copy23.getFrom());
+ assertEquals(copy3, copy23.getTo());
+ }
}
diff --git
a/engine/src/main/java/org/apache/hop/pipeline/transforms/missing/Missing.java
b/engine/src/test/java/org/apache/hop/workflow/actions/ActionFake.java
similarity index 52%
copy from
engine/src/main/java/org/apache/hop/pipeline/transforms/missing/Missing.java
copy to engine/src/test/java/org/apache/hop/workflow/actions/ActionFake.java
index b6522a8c2b..993a55e9f5 100644
---
a/engine/src/main/java/org/apache/hop/pipeline/transforms/missing/Missing.java
+++ b/engine/src/test/java/org/apache/hop/workflow/actions/ActionFake.java
@@ -13,35 +13,32 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
+ *
*/
-package org.apache.hop.pipeline.transforms.missing;
-
-import org.apache.hop.pipeline.transform.BaseTransformMeta;
-
-public class Missing extends BaseTransformMeta<MissingTransform, MissingData> {
-
- private String transformName;
- private String missingPluginId;
-
- public Missing(String transformName, String missingPluginId) {
- this.transformName = transformName;
- this.missingPluginId = missingPluginId;
- }
-
- public String getTransformName() {
- return transformName;
- }
-
- public void setTransformName(String transformName) {
- this.transformName = transformName;
- }
-
- public String getMissingPluginId() {
- return missingPluginId;
+package org.apache.hop.workflow.actions;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.hop.core.Result;
+import org.apache.hop.core.annotations.Action;
+import org.apache.hop.core.exception.HopException;
+import org.apache.hop.metadata.api.HopMetadataProperty;
+import org.apache.hop.workflow.action.ActionBase;
+import org.apache.hop.workflow.action.IAction;
+
+@Action(id = "ActionFake", name = "Fake action")
+@Getter
+@Setter
+public class ActionFake extends ActionBase implements IAction {
+ @HopMetadataProperty private String someProperty;
+
+ public ActionFake() {
+ super("", "", "ActionFake");
}
- public void setMissingPluginId(String missingPluginId) {
- this.missingPluginId = missingPluginId;
+ @Override
+ public Result execute(Result prevResult, int nr) throws HopException {
+ return prevResult;
}
}
diff --git a/integration-tests/mdi/files/excelwriter-mdi-test.xlsx
b/integration-tests/mdi/files/excelwriter-mdi-test.xlsx
index ef8f12feb6..89dc9f1333 100644
Binary files a/integration-tests/mdi/files/excelwriter-mdi-test.xlsx and
b/integration-tests/mdi/files/excelwriter-mdi-test.xlsx differ
diff --git a/plugins/misc/git/pom.xml b/plugins/misc/git/pom.xml
index 7b7a7056b2..00f80f381e 100644
--- a/plugins/misc/git/pom.xml
+++ b/plugins/misc/git/pom.xml
@@ -67,6 +67,18 @@
<version>4.13.2</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.hop</groupId>
+ <artifactId>hop-action-pipeline</artifactId>
+ <version>2.18.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hop</groupId>
+ <artifactId>hop-action-workflow</artifactId>
+ <version>2.18.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.apache.hop</groupId>
<artifactId>hop-transform-calculator</artifactId>
diff --git a/plugins/misc/git/src/test/java/org/apache/hop/git/HopDiffTest.java
b/plugins/misc/git/src/test/java/org/apache/hop/git/HopDiffTest.java
index 19f757ec16..42d6c0226b 100644
--- a/plugins/misc/git/src/test/java/org/apache/hop/git/HopDiffTest.java
+++ b/plugins/misc/git/src/test/java/org/apache/hop/git/HopDiffTest.java
@@ -32,8 +32,10 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import org.apache.hop.core.HopClientEnvironment;
+import org.apache.hop.core.annotations.Action;
import org.apache.hop.core.annotations.Transform;
import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.plugins.ActionPluginType;
import org.apache.hop.core.plugins.PluginRegistry;
import org.apache.hop.core.plugins.TransformPluginType;
import org.apache.hop.core.variables.Variables;
@@ -45,6 +47,9 @@ import
org.apache.hop.pipeline.transforms.checksum.CheckSumMeta;
import org.apache.hop.pipeline.transforms.csvinput.CsvInputMeta;
import org.apache.hop.pipeline.transforms.selectvalues.SelectValuesMeta;
import org.apache.hop.workflow.WorkflowMeta;
+import org.apache.hop.workflow.actions.pipeline.ActionPipeline;
+import org.apache.hop.workflow.actions.start.ActionStart;
+import org.apache.hop.workflow.actions.workflow.ActionWorkflow;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -66,7 +71,14 @@ class HopDiffTest {
PluginRegistry.getInstance()
.registerPluginClass(
CheckSumMeta.class.getName(), TransformPluginType.class,
Transform.class);
- // HopEnvironment.init();
+
+ PluginRegistry.getInstance()
+ .registerPluginClass(ActionStart.class.getName(),
ActionPluginType.class, Action.class);
+ PluginRegistry.getInstance()
+ .registerPluginClass(ActionWorkflow.class.getName(),
ActionPluginType.class, Action.class);
+ PluginRegistry.getInstance()
+ .registerPluginClass(ActionPipeline.class.getName(),
ActionPluginType.class, Action.class);
+
metadataProvider = new MemoryMetadataProvider();
}
diff --git a/plugins/misc/git/src/test/resources/r1.hwf
b/plugins/misc/git/src/test/resources/r1.hwf
index 86a2af45d0..e90a43da69 100644
--- a/plugins/misc/git/src/test/resources/r1.hwf
+++ b/plugins/misc/git/src/test/resources/r1.hwf
@@ -52,7 +52,7 @@ limitations under the License.
<action>
<name>Workflow</name>
<description />
- <type>JOB</type>
+ <type>WORKFLOW</type>
<specification_method>filename</specification_method>
<job_object_id />
<filename />
@@ -84,7 +84,7 @@ limitations under the License.
<action>
<name>Pipeline</name>
<description />
- <type>TRANS</type>
+ <type>PIPELINE</type>
<specification_method>filename</specification_method>
<trans_object_id />
<filename />
diff --git a/plugins/misc/git/src/test/resources/r2.hwf
b/plugins/misc/git/src/test/resources/r2.hwf
index 4d10bc8a11..16c17c1f80 100644
--- a/plugins/misc/git/src/test/resources/r2.hwf
+++ b/plugins/misc/git/src/test/resources/r2.hwf
@@ -55,7 +55,7 @@ limitations under the License.
<action>
<name>Workflow</name>
<description />
- <type>JOB</type>
+ <type>WORKFLOW</type>
<specification_method>filename</specification_method>
<job_object_id />
<filename />
@@ -84,7 +84,7 @@ limitations under the License.
<action>
<name>Workflow 2</name>
<description />
- <type>JOB</type>
+ <type>WORKFLOW</type>
<specification_method>filename</specification_method>
<job_object_id />
<filename />
diff --git
a/ui/src/main/java/org/apache/hop/ui/workflow/dialog/WorkflowDialog.java
b/ui/src/main/java/org/apache/hop/ui/workflow/dialog/WorkflowDialog.java
index 184edc288e..b187f8c312 100644
--- a/ui/src/main/java/org/apache/hop/ui/workflow/dialog/WorkflowDialog.java
+++ b/ui/src/main/java/org/apache/hop/ui/workflow/dialog/WorkflowDialog.java
@@ -54,7 +54,6 @@ import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
@@ -96,8 +95,6 @@ public class WorkflowDialog extends Dialog {
private Text wExtendedDescription;
- private Combo wWorkflowStatus;
-
// Workflow version
private Text wVersion;
@@ -313,30 +310,6 @@ public class WorkflowDialog extends Dialog {
fdExtendedDescription.bottom = new FormAttachment(50, -margin);
wExtendedDescription.setLayoutData(fdExtendedDescription);
- // Workflow Status
- Label wlWorkflowStatus = new Label(wWorkflowComp, SWT.RIGHT);
- wlWorkflowStatus.setText(BaseMessages.getString(PKG,
"WorkflowDialog.WorkflowStatus.Label"));
- PropsUi.setLook(wlWorkflowStatus);
- FormData fdlWorkflowStatus = new FormData();
- fdlWorkflowStatus.left = new FormAttachment(0, 0);
- fdlWorkflowStatus.right = new FormAttachment(middle, -margin);
- fdlWorkflowStatus.top = new FormAttachment(wExtendedDescription, margin);
- wlWorkflowStatus.setLayoutData(fdlWorkflowStatus);
- wWorkflowStatus = new Combo(wWorkflowComp, SWT.SINGLE | SWT.READ_ONLY |
SWT.BORDER);
- wWorkflowStatus.add(BaseMessages.getString(PKG,
"WorkflowDialog.Draft_WorkflowStatus.Label"));
- wWorkflowStatus.add(
- BaseMessages.getString(PKG,
"WorkflowDialog.Production_WorkflowStatus.Label"));
- wWorkflowStatus.add("");
- // +1: starts at -1
- wWorkflowStatus.select(-1);
-
- PropsUi.setLook(wWorkflowStatus);
- FormData fdWorkflowStatus = new FormData();
- fdWorkflowStatus.left = new FormAttachment(middle, 0);
- fdWorkflowStatus.top = new FormAttachment(wExtendedDescription, margin);
- fdWorkflowStatus.right = new FormAttachment(100, 0);
- wWorkflowStatus.setLayoutData(fdWorkflowStatus);
-
// Workflow version:
Label wlVersion = new Label(wWorkflowComp, SWT.RIGHT);
wlVersion.setText(BaseMessages.getString(PKG,
"WorkflowDialog.Version.Label"));
@@ -344,14 +317,14 @@ public class WorkflowDialog extends Dialog {
FormData fdlVersion = new FormData();
fdlVersion.left = new FormAttachment(0, 0);
fdlVersion.right = new FormAttachment(middle, -margin);
- fdlVersion.top = new FormAttachment(wWorkflowStatus, margin);
+ fdlVersion.top = new FormAttachment(wExtendedDescription, margin);
wlVersion.setLayoutData(fdlVersion);
wVersion = new Text(wWorkflowComp, SWT.SINGLE | SWT.LEFT | SWT.BORDER);
PropsUi.setLook(wVersion);
wVersion.addModifyListener(lsMod);
FormData fdVersion = new FormData();
fdVersion.left = new FormAttachment(middle, 0);
- fdVersion.top = new FormAttachment(wWorkflowStatus, margin);
+ fdVersion.top = new FormAttachment(wExtendedDescription, margin);
fdVersion.right = new FormAttachment(100, 0);
wVersion.setLayoutData(fdVersion);
@@ -539,7 +512,7 @@ public class WorkflowDialog extends Dialog {
shell.dispose();
}
- /** Copy information from the meta-data input to the dialog fields. */
+ /** Copy information from the metadata input to the dialog fields. */
public void getData() {
wWorkflowName.setText(Const.NVL(workflowMeta.getName(), ""));
wNameFilenameSync.setSelection(workflowMeta.isNameSynchronizedWithFilename());
@@ -548,7 +521,6 @@ public class WorkflowDialog extends Dialog {
wDescription.setText(Const.NVL(workflowMeta.getDescription(), ""));
wExtendedDescription.setText(Const.NVL(workflowMeta.getExtendedDescription(),
""));
wVersion.setText(Const.NVL(workflowMeta.getWorkflowVersion(), ""));
- wWorkflowStatus.select(workflowMeta.getWorkflowStatus() - 1);
if (workflowMeta.getCreatedUser() != null) {
wCreateUser.setText(workflowMeta.getCreatedUser());
@@ -609,13 +581,6 @@ public class WorkflowDialog extends Dialog {
workflowMeta.setDescription(wDescription.getText());
workflowMeta.setExtendedDescription(wExtendedDescription.getText());
workflowMeta.setWorkflowVersion(wVersion.getText());
- if (wWorkflowStatus.getSelectionIndex() != 2) {
- // Saving the index as meta data is in fact pretty bad, but since
- // it was already in ...
- workflowMeta.setWorkflowStatus(wWorkflowStatus.getSelectionIndex() + 1);
- } else {
- workflowMeta.setWorkflowStatus(-1);
- }
// Clear and add parameters
workflowMeta.removeAllParameters();