diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/DatadogPluginAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/DatadogPluginAction.java index 88cb56657..277070956 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/DatadogPluginAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/DatadogPluginAction.java @@ -2,9 +2,10 @@ import hudson.model.InvisibleAction; import java.io.Serializable; +import org.jenkinsci.plugins.workflow.actions.PersistentAction; /** * Marker interface for all actions that are added by the plugin */ -public abstract class DatadogPluginAction extends InvisibleAction implements Serializable { +public abstract class DatadogPluginAction extends InvisibleAction implements PersistentAction, Serializable { } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitCommitAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitCommitAction.java index 1046a6bd9..6997a377e 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitCommitAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitCommitAction.java @@ -6,7 +6,8 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import java.util.Objects; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; /** * Keeps the Git commit related information. @@ -140,18 +141,22 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return GitCommitAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - GitCommitAction action = (GitCommitAction) source; + public void marshal(GitCommitAction action, HierarchicalStreamWriter writer, MarshallingContext context) { if (action.tag != null) { writeField("tag", action.tag, writer, context); } @@ -182,7 +187,7 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public GitCommitAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String tag = null; String commit = null; String message = null; diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java index d9c978936..048217a53 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/GitRepositoryAction.java @@ -7,7 +7,8 @@ import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import java.util.Objects; import javax.annotation.Nullable; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; /** * Keeps the Git repository related information. @@ -78,18 +79,22 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return GitRepositoryAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - GitRepositoryAction action = (GitRepositoryAction) source; + public void marshal(GitRepositoryAction action, HierarchicalStreamWriter writer, MarshallingContext context) { if (action.repositoryURL != null) { writeField("repositoryURL", action.repositoryURL, writer, context); } @@ -102,7 +107,7 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public GitRepositoryAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { GitRepositoryAction gitRepositoryAction = new GitRepositoryAction(); while (reader.hasMoreChildren()) { reader.moveDown(); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineNodeInfoAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineNodeInfoAction.java index abafd8697..b598c11da 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineNodeInfoAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineNodeInfoAction.java @@ -8,7 +8,8 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; public class PipelineNodeInfoAction extends DatadogPluginAction { @@ -63,18 +64,22 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return PipelineNodeInfoAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - PipelineNodeInfoAction action = (PipelineNodeInfoAction) source; + public void marshal(PipelineNodeInfoAction action, HierarchicalStreamWriter writer, MarshallingContext context) { if (action.nodeName != null) { writeField("nodeName", action.nodeName, writer, context); } @@ -90,7 +95,7 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public PipelineNodeInfoAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String nodeName = null; String nodeHostname = null; Set nodeLabels = Collections.emptySet(); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineQueueInfoAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineQueueInfoAction.java index c7f79e7f5..2c753c930 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineQueueInfoAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/PipelineQueueInfoAction.java @@ -6,7 +6,8 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import java.util.Objects; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; public class PipelineQueueInfoAction extends DatadogPluginAction { @@ -61,18 +62,22 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return PipelineQueueInfoAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - PipelineQueueInfoAction action = (PipelineQueueInfoAction) source; + public void marshal(PipelineQueueInfoAction action, HierarchicalStreamWriter writer, MarshallingContext context) { if (action.queueTimeMillis != -1) { writeField("queueTimeMillis", action.queueTimeMillis, writer, context); } @@ -82,7 +87,7 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public PipelineQueueInfoAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { long queueTimeMillis = -1; long propagatedQueueTimeMillis = -1; diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/TraceInfoAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/TraceInfoAction.java index 989f13803..141e29d18 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/TraceInfoAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/TraceInfoAction.java @@ -11,7 +11,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.datadog.jenkins.plugins.datadog.traces.IdGenerator; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; /** * This action stores mapping between IDs of {@link org.jenkinsci.plugins.workflow.graph.FlowNode} @@ -78,23 +79,27 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return TraceInfoAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - TraceInfoAction action = (TraceInfoAction) source; + public void marshal(TraceInfoAction action, HierarchicalStreamWriter writer, MarshallingContext context) { writeField("infoByFlowNodeId", action.spanIdByNodeId, writer, context); } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public TraceInfoAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Map infoByFlowNodeId = readField(reader, context, Map.class); return new TraceInfoAction(infoByFlowNodeId); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/DequeueAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/DequeueAction.java index dbd2aa832..7bd3a45fb 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/DequeueAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/DequeueAction.java @@ -6,7 +6,8 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import java.util.Objects; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; public class DequeueAction extends QueueInfoAction { @@ -40,24 +41,28 @@ public String toString() { return "DequeueAction{queueTimeNanos=" + queueTimeNanos + '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return DequeueAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - DequeueAction action = (DequeueAction) source; + public void marshal(DequeueAction action, HierarchicalStreamWriter writer, MarshallingContext context) { writeField("queueTimeNanos", action.queueTimeNanos, writer, context); context.convertAnother(action.queueTimeNanos); } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public DequeueAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { long queueTimeNanos = readField(reader, context, long.class); return new DequeueAction(queueTimeNanos); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/EnqueueAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/EnqueueAction.java index 433c22c33..f1f4af466 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/EnqueueAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/EnqueueAction.java @@ -6,7 +6,8 @@ import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import java.util.Objects; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; public class EnqueueAction extends QueueInfoAction { @@ -40,23 +41,27 @@ public String toString() { return "EnqueueAction{timestampNanos=" + timestampNanos + '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return EnqueueAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - EnqueueAction action = (EnqueueAction) source; + public void marshal(EnqueueAction action, HierarchicalStreamWriter writer, MarshallingContext context) { writeField("timestampNanos", action.timestampNanos, writer, context); } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public EnqueueAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { long timestampNanos = readField(reader, context, long.class); return new EnqueueAction(timestampNanos); } diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/NodeInfoAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/NodeInfoAction.java index 9eabab814..8aed8ddcc 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/NodeInfoAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/NodeInfoAction.java @@ -9,7 +9,8 @@ import java.util.Objects; import java.util.Set; import org.datadog.jenkins.plugins.datadog.model.DatadogPluginAction; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; public class NodeInfoAction extends DatadogPluginAction { @@ -66,18 +67,22 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return NodeInfoAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - NodeInfoAction action = (NodeInfoAction) source; + public void marshal(NodeInfoAction action, HierarchicalStreamWriter writer, MarshallingContext context) { if (action.nodeName != null) { writeField("nodeName", action.nodeName, writer, context); } @@ -93,7 +98,7 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public NodeInfoAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { String nodeName = null; String nodeHostname = null; Set nodeLabels = Collections.emptySet(); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/StatusAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/StatusAction.java index cad240997..0a254463e 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/StatusAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/model/node/StatusAction.java @@ -8,7 +8,8 @@ import java.util.Objects; import org.datadog.jenkins.plugins.datadog.model.DatadogPluginAction; import org.datadog.jenkins.plugins.datadog.model.Status; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; public class StatusAction extends DatadogPluginAction { @@ -51,24 +52,28 @@ public int hashCode() { return Objects.hash(status, propagate); } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return StatusAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - StatusAction action = (StatusAction) source; + public void marshal(StatusAction action, HierarchicalStreamWriter writer, MarshallingContext context) { writeField("status", action.status, writer, context); writeField("propagate", action.propagate, writer, context); } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public StatusAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Status status = readField(reader, context, Status.class); boolean propagate = readField(reader, context, boolean.class); return new StatusAction(status, propagate); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/BuildSpanAction.java b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/BuildSpanAction.java index fd5a35285..ea99115a8 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/BuildSpanAction.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/BuildSpanAction.java @@ -8,7 +8,8 @@ import java.util.Objects; import org.datadog.jenkins.plugins.datadog.model.DatadogPluginAction; import org.datadog.jenkins.plugins.datadog.traces.message.TraceSpan; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; /** * Keeps build span propagation @@ -63,18 +64,22 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return BuildSpanAction.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - BuildSpanAction action = (BuildSpanAction) source; + public void marshal(BuildSpanAction action, HierarchicalStreamWriter writer, MarshallingContext context) { writeField("spanContext", action.buildSpanContext, writer, context); if (action.buildUrl != null) { writeField("buildUrl", action.buildUrl, writer, context); @@ -82,7 +87,7 @@ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingC } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public BuildSpanAction unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { TraceSpan.TraceSpanContext spanContext = readField(reader, context, TraceSpan.TraceSpanContext.class); String buildUrl = null; diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/message/TraceSpan.java b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/message/TraceSpan.java index b35bc04d0..26ee310c4 100644 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/traces/message/TraceSpan.java +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/traces/message/TraceSpan.java @@ -10,7 +10,8 @@ import java.util.Map; import java.util.Objects; import org.datadog.jenkins.plugins.datadog.traces.IdGenerator; -import org.datadog.jenkins.plugins.datadog.util.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.DatadogActionConverter; +import org.datadog.jenkins.plugins.datadog.util.conversion.VersionedConverter; public class TraceSpan { @@ -181,25 +182,29 @@ public String toString() { '}'; } - public static final class ConverterImpl extends DatadogActionConverter { + public static final class ConverterImpl extends DatadogActionConverter { public ConverterImpl(XStream xs) { + super(new ConverterV1()); } + } - @Override - public boolean canConvert(Class type) { - return TraceSpanContext.class == type; + public static final class ConverterV1 extends VersionedConverter { + + private static final int VERSION = 1; + + public ConverterV1() { + super(VERSION); } @Override - public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { - TraceSpanContext traceSpanContext = (TraceSpanContext) source; + public void marshal(TraceSpanContext traceSpanContext, HierarchicalStreamWriter writer, MarshallingContext context) { writeField("traceId", traceSpanContext.traceId, writer, context); writeField("parentId", traceSpanContext.parentId, writer, context); writeField("spanId", traceSpanContext.spanId, writer, context); } @Override - public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + public TraceSpanContext unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { long traceId = readField(reader, context, long.class); long parentId = readField(reader, context, long.class); long spanId = readField(reader, context, long.class); diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/util/DatadogActionConverter.java b/src/main/java/org/datadog/jenkins/plugins/datadog/util/DatadogActionConverter.java deleted file mode 100644 index fb5951794..000000000 --- a/src/main/java/org/datadog/jenkins/plugins/datadog/util/DatadogActionConverter.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.datadog.jenkins.plugins.datadog.util; - -import com.thoughtworks.xstream.converters.Converter; -import com.thoughtworks.xstream.converters.MarshallingContext; -import com.thoughtworks.xstream.converters.UnmarshallingContext; -import com.thoughtworks.xstream.io.HierarchicalStreamReader; -import com.thoughtworks.xstream.io.HierarchicalStreamWriter; - -public abstract class DatadogActionConverter implements Converter { - protected void writeField(String name, Object value, HierarchicalStreamWriter writer, MarshallingContext context) { - writer.startNode(name); - context.convertAnother(value); - writer.endNode(); - } - - protected T readField(HierarchicalStreamReader reader, UnmarshallingContext context, Class type) { - reader.moveDown(); - T value = (T) context.convertAnother(null, type); - reader.moveUp(); - return value; - } -} diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/DatadogActionConverter.java b/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/DatadogActionConverter.java new file mode 100644 index 000000000..0e45a34ca --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/DatadogActionConverter.java @@ -0,0 +1,115 @@ +package org.datadog.jenkins.plugins.datadog.util.conversion; + +import com.thoughtworks.xstream.converters.Converter; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Converter that supports data versioning. + * Different data version = different serialization format for objects of the given type. + * Each version is handled by a child {@link VersionedConverter} that encapsulates serialization/deserialization logic for that specific version. + *

+ * Serialized data contains version in an attribute. + * When data is deserialized, the version is used to determine which child converter should handle deserialization. + *

+ * If you want to change serialization format for a specific type, + * leave the previous {@link VersionedConverter} instances in place (they will be used to deserialize older data) + * and add a new one with a higher version number. + * + * @param The type of converted objects (should be as narrow as possible, so that different converters do not conflict) + */ +public abstract class DatadogActionConverter implements Converter { + + private static final String VERSION_ATTRIBUTE = "v"; + + private final VersionedConverter writeConverter; + private final Map> readConverters; + private final Class convertedType; + + /** + * @param converters The list of versioned converters. + * Writing is always done with the most up-to-date converter (the one with the maximum version). + * Reading is done with the appropriate converter (the one that has version that matches the data version). + * If converter with the specified version does not exist, or data has no version attribute (written by an old version of the plugin), + * no deserialization is performed. + */ + @SafeVarargs + protected DatadogActionConverter(@Nonnull VersionedConverter... converters) { + if (converters.length == 0) { + throw new IllegalArgumentException("At least one converter is needed"); + } + + readConverters = new HashMap<>(); + for (VersionedConverter converter : converters) { + VersionedConverter existingConverter = readConverters.put(converter.getVersion(), converter); + if (existingConverter != null) { + throw new IllegalArgumentException(String.format("Two converters have the same version: %s (%d), %s (%d)", + converter, converter.getVersion(), existingConverter, existingConverter.getVersion())); + } + } + + writeConverter = readConverters.values() + .stream() + .max(Comparator.comparingInt(VersionedConverter::getVersion)) + // this shouldn't be possible since readConverters must be non-empty + .orElseThrow(() -> new IllegalArgumentException("Cannot find converter with max version")); + + // only direct children are supported, to keep logic simple + if (!getClass().getSuperclass().equals(DatadogActionConverter.class)) { + throw new IllegalArgumentException(getClass().getName() + " is not a direct descendant of " + DatadogActionConverter.class.getName()); + } + + ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); + Type[] actualTypeArguments = genericSuperclass.getActualTypeArguments(); + convertedType = (Class) actualTypeArguments[0]; + } + + @Override + public boolean canConvert(Class type) { + return type.equals(convertedType); + } + + @Override + public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { + writer.addAttribute(VERSION_ATTRIBUTE, String.valueOf(writeConverter.getVersion())); + writeConverter.marshal((T) source, writer, context); + } + + @Nullable + @Override + public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + VersionedConverter readConverter = getReadConverter(reader); + if (readConverter != null) { + return readConverter.unmarshal(reader, context); + } else { + // no matching converter found, data is too old or corrupted + return null; + } + } + + @Nullable + private VersionedConverter getReadConverter(HierarchicalStreamReader reader) { + try { + String versionString = reader.getAttribute(VERSION_ATTRIBUTE); + if (versionString == null) { + // no attribute, data was written by an old version of the plugin + return null; + } + int version = Integer.parseInt(versionString); + return readConverters.get(version); + + } catch (NumberFormatException e) { + // version is malformed + return null; + } + } +} diff --git a/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/VersionedConverter.java b/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/VersionedConverter.java new file mode 100644 index 000000000..f38c0065f --- /dev/null +++ b/src/main/java/org/datadog/jenkins/plugins/datadog/util/conversion/VersionedConverter.java @@ -0,0 +1,35 @@ +package org.datadog.jenkins.plugins.datadog.util.conversion; + +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; + +public abstract class VersionedConverter { + private final int version; + + protected VersionedConverter(int version) { + this.version = version; + } + + public int getVersion() { + return version; + } + + public abstract void marshal(T source, HierarchicalStreamWriter writer, MarshallingContext context); + + public abstract T unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context); + + protected void writeField(String name, Object value, HierarchicalStreamWriter writer, MarshallingContext context) { + writer.startNode(name); + context.convertAnother(value); + writer.endNode(); + } + + protected F readField(HierarchicalStreamReader reader, UnmarshallingContext context, Class type) { + reader.moveDown(); + F value = (F) context.convertAnother(null, type); + reader.moveUp(); + return value; + } +} diff --git a/src/test/java/org/datadog/jenkins/plugins/datadog/util/conversion/DatadogActionConverterTest.java b/src/test/java/org/datadog/jenkins/plugins/datadog/util/conversion/DatadogActionConverterTest.java new file mode 100644 index 000000000..9ae1ad01c --- /dev/null +++ b/src/test/java/org/datadog/jenkins/plugins/datadog/util/conversion/DatadogActionConverterTest.java @@ -0,0 +1,101 @@ +package org.datadog.jenkins.plugins.datadog.util.conversion; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.converters.MarshallingContext; +import com.thoughtworks.xstream.converters.UnmarshallingContext; +import com.thoughtworks.xstream.io.HierarchicalStreamReader; +import com.thoughtworks.xstream.io.HierarchicalStreamWriter; +import hudson.util.XStream2; +import java.io.StringWriter; +import org.junit.BeforeClass; +import org.junit.Test; + +public class DatadogActionConverterTest { + + private static final XStream XSTREAM = new XStream2(XStream2.getDefaultDriver()); + + @BeforeClass + public static void setUp() { + XSTREAM.registerConverter(new MyConverter()); + } + + @Test + public void testUsesMostRecentConverterVersionWhenSerializing() { + StringWriter stringWriter = new StringWriter(); + MyClass myClass = new MyClass(); + XSTREAM.toXML(myClass, stringWriter); + String serializedMyClass = stringWriter.getBuffer().toString(); + assertTrue(serializedMyClass.contains("v=\"3\"")); // this is the meta attribute set by parent converter + assertTrue(serializedMyClass.contains("written-with-version-3")); // this is the serialized data written by child converter + } + + @Test + public void testUsesAppropriateConverterVersionWhenDeserializing() { + String serializedWithV3 = "written-with-version-3"; + MyClass deserializedWithV3 = (MyClass) XSTREAM.fromXML(serializedWithV3); + assertEquals(3, deserializedWithV3.deserializedWithVersion); + + String serializedWithV2 = "written-with-version-2"; + MyClass deserializedWithV2 = (MyClass) XSTREAM.fromXML(serializedWithV2); + assertEquals(2, deserializedWithV2.deserializedWithVersion); + + String serializedWithV1 = "written-with-version-1"; + MyClass deserializedWithV1 = (MyClass) XSTREAM.fromXML(serializedWithV1); + assertEquals(1, deserializedWithV1.deserializedWithVersion); + } + + @Test + public void testDoesNotDeserializeUnversionedData() { + String serializedUnversioned = "written-without-version"; + MyClass deserializedUnversioned = (MyClass) XSTREAM.fromXML(serializedUnversioned); + assertNull(deserializedUnversioned); + } + + @Test + public void testDoesNotDeserializeDataWithUnsupportedVersion() { + String serializedWithV0 = "written-with-version-0"; + MyClass deserializedWithV0 = (MyClass) XSTREAM.fromXML(serializedWithV0); + assertNull(deserializedWithV0); + } + + @Test + public void testDoesNotDeserializeDataWithMalformedVersion() { + String serializedWithCorruptedVersion = "written-with-corrupted-version"; + MyClass deserializedWithCorruptedVersion = (MyClass) XSTREAM.fromXML(serializedWithCorruptedVersion); + assertNull(deserializedWithCorruptedVersion); + } + + private static final class MyClass { + private int deserializedWithVersion; + } + + private static final class MyConverter extends DatadogActionConverter { + public MyConverter() { + super(new MyVersionedConverter(1), new MyVersionedConverter(2), new MyVersionedConverter(3)); + } + } + + private static final class MyVersionedConverter extends VersionedConverter { + public MyVersionedConverter(int version) { + super(version); + } + + @Override + public void marshal(MyClass source, HierarchicalStreamWriter writer, MarshallingContext context) { + writer.setValue("written-with-version-" + getVersion()); + } + + @Override + public MyClass unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { + MyClass myClass = new MyClass(); + myClass.deserializedWithVersion = getVersion(); + return myClass; + } + } + + +} \ No newline at end of file