diff --git a/pom.xml b/pom.xml index d64cff5..c163a8a 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ camel-tooling https://sonarcloud.io - 4.16.0 + 4.18.1 4.0.0 2.3.0 diff --git a/src/main/java/com/github/cameltooling/dap/internal/types/EventMessage.java b/src/main/java/com/github/cameltooling/dap/internal/types/EventMessage.java index 876ea22..42fee33 100644 --- a/src/main/java/com/github/cameltooling/dap/internal/types/EventMessage.java +++ b/src/main/java/com/github/cameltooling/dap/internal/types/EventMessage.java @@ -16,27 +16,60 @@ */ package com.github.cameltooling.dap.internal.types; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Locale; import java.util.Map; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlElement; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.adapters.XmlAdapter; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.apache.camel.spi.BacklogTracerEventMessage; - +@XmlAccessorType(XmlAccessType.NONE) @XmlRootElement(name = "backlogTracerEventMessage") public class EventMessage implements BacklogTracerEventMessage { private static final long serialVersionUID = 2559418843237642923L; + private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern(TIMESTAMP_FORMAT, Locale.ROOT); private long uid; - //TODO: use a real date for the timestamp - //@XmlJavaTypeAdapter(DateAdapter.class) - private long timestamp; + private boolean first; + private boolean last; + private boolean rest; + private boolean template; + @XmlElement(name = "timestamp") + @XmlJavaTypeAdapter(TimestampAdapter.class) + private Long timestamp; + private long elapsed; + private boolean done; + private boolean failed; + private String location; + private String processingThreadName; private String routeId; + private String fromRouteId; private String toNode; + private String toNodeParentId; + private String toNodeParentWhenId; + private String toNodeParentWhenLabel; + private String toNodeShortName; + private String toNodeLabel; + private int toNodeLevel; private String exchangeId; + private String correlationExchangeId; private Message message; + private String endpointUri; + private boolean remoteEndpoint; + private String endpointServiceUrl; + private String endpointServiceProtocol; + private Map endpointServiceMetadata; + private Throwable exception; @XmlElement(name = "uid") public long getUid() { @@ -46,14 +79,86 @@ public void setUid(long uid) { this.uid = uid; } - @XmlElement(name = "timestamp") + @XmlElement(name = "first") + public boolean isFirst() { + return first; + } + public void setFirst(boolean first) { + this.first = first; + } + + @XmlElement(name = "last") + public boolean isLast() { + return last; + } + public void setLast(boolean last) { + this.last = last; + } + + @XmlElement(name = "rest") + public boolean isRest() { + return rest; + } + public void setRest(boolean rest) { + this.rest = rest; + } + + @XmlElement(name = "template") + public boolean isTemplate() { + return template; + } + public void setTemplate(boolean template) { + this.template = template; + } + + @XmlTransient public long getTimestamp() { - return timestamp; + return timestamp != null ? timestamp.longValue() : 0L; } public void setTimestamp(long timestamp) { this.timestamp = timestamp; } + @XmlElement(name = "elapsed") + public long getElapsed() { + return elapsed; + } + public void setElapsed(long elapsed) { + this.elapsed = elapsed; + } + + @XmlElement(name = "done") + public boolean isDone() { + return done; + } + public void setDone(boolean done) { + this.done = done; + } + + @XmlElement(name = "failed") + public boolean isFailed() { + return failed; + } + public void setFailed(boolean failed) { + this.failed = failed; + } + + @XmlElement(name = "location") + public String getLocation() { + return location; + } + public void setLocation(String location) { + this.location = location; + } + + @XmlElement(name = "threadName") + public String getProcessingThreadName() { + return processingThreadName; + } + public void setProcessingThreadName(String processingThreadName) { + this.processingThreadName = processingThreadName; + } + @XmlElement(name = "routeId") public String getRouteId() { return routeId; @@ -62,6 +167,14 @@ public void setRouteId(String routeId) { this.routeId = routeId; } + @XmlElement(name = "fromRouteId") + public String getFromRouteId() { + return fromRouteId; + } + public void setFromRouteId(String fromRouteId) { + this.fromRouteId = fromRouteId; + } + @XmlElement(name = "toNode") public String getToNode() { return toNode; @@ -70,6 +183,54 @@ public void setToNode(String toNode) { this.toNode = toNode; } + @XmlElement(name = "toNodeParentId") + public String getToNodeParentId() { + return toNodeParentId; + } + public void setToNodeParentId(String toNodeParentId) { + this.toNodeParentId = toNodeParentId; + } + + @XmlElement(name = "toNodeParentWhenId") + public String getToNodeParentWhenId() { + return toNodeParentWhenId; + } + public void setToNodeParentWhenId(String toNodeParentWhenId) { + this.toNodeParentWhenId = toNodeParentWhenId; + } + + @XmlElement(name = "toNodeParentWhenLabel") + public String getToNodeParentWhenLabel() { + return toNodeParentWhenLabel; + } + public void setToNodeParentWhenLabel(String toNodeParentWhenLabel) { + this.toNodeParentWhenLabel = toNodeParentWhenLabel; + } + + @XmlElement(name = "toNodeShortName") + public String getToNodeShortName() { + return toNodeShortName; + } + public void setToNodeShortName(String toNodeShortName) { + this.toNodeShortName = toNodeShortName; + } + + @XmlElement(name = "toNodeLabel") + public String getToNodeLabel() { + return toNodeLabel; + } + public void setToNodeLabel(String toNodeLabel) { + this.toNodeLabel = toNodeLabel; + } + + @XmlElement(name = "toNodeLevel") + public int getToNodeLevel() { + return toNodeLevel; + } + public void setToNodeLevel(int toNodeLevel) { + this.toNodeLevel = toNodeLevel; + } + @XmlElement(name = "exchangeId") public String getExchangeId() { return exchangeId; @@ -78,6 +239,14 @@ public void setExchangeId(String exchangeId) { this.exchangeId = exchangeId; } + @XmlElement(name = "correlationExchangeId") + public String getCorrelationExchangeId() { + return correlationExchangeId; + } + public void setCorrelationExchangeId(String correlationExchangeId) { + this.correlationExchangeId = correlationExchangeId; + } + @XmlElement(name = "message") public Message getMessage() { return message; @@ -86,98 +255,90 @@ public void setMessage(Message message) { this.message = message; } - @Override public String getMessageAsXml() { throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); } - @Override public String toXml(int indent) { throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); } - @Override - public boolean isRest() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override - public boolean isTemplate() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override public String getMessageAsJSon() { throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); } - @Override public String toJSon(int indent) { throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); } - @Override public Map asJSon() { throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); } - @Override - public boolean isFirst() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override - public boolean isLast() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override - public String getLocation() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override - public String getProcessingThreadName() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override - public long getElapsed() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override - public boolean isDone() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override - public boolean isFailed() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); - } - @Override public boolean hasException() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return exception != null; } - @Override public String getExceptionAsXml() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return null; } - @Override public String getExceptionAsJSon() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return null; } - @Override public String getEndpointUri() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return endpointUri; + } + public void setEndpointUri(String endpointUri) { + this.endpointUri = endpointUri; } - @Override public boolean isRemoteEndpoint() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return remoteEndpoint; + } + public void setRemoteEndpoint(boolean remoteEndpoint) { + this.remoteEndpoint = remoteEndpoint; } - @Override public String getEndpointServiceUrl() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return endpointServiceUrl; + } + public void setEndpointServiceUrl(String endpointServiceUrl) { + this.endpointServiceUrl = endpointServiceUrl; } - @Override public String getEndpointServiceProtocol() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return endpointServiceProtocol; + } + public void setEndpointServiceProtocol(String endpointServiceProtocol) { + this.endpointServiceProtocol = endpointServiceProtocol; } - @Override public Map getEndpointServiceMetadata() { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + return endpointServiceMetadata; + } + public void setEndpointServiceMetadata(Map endpointServiceMetadata) { + this.endpointServiceMetadata = endpointServiceMetadata; } - @Override public void setException(Throwable cause) { - throw new UnsupportedOperationException("This class is used only to read message sent from Camel server through JMX"); + this.exception = cause; + } + + private static final class TimestampAdapter extends XmlAdapter { + + @Override + public Long unmarshal(String value) { + if (value == null || value.isBlank()) { + return 0L; + } + try { + return Long.valueOf(value); + } catch (NumberFormatException ex) { + try { + return OffsetDateTime.parse(value, TIMESTAMP_FORMATTER).toInstant().toEpochMilli(); + } catch (DateTimeParseException e) { + throw new IllegalArgumentException("Cannot parse backlog tracer timestamp: " + value, e); + } + } + } + + @Override + public String marshal(Long value) { + if (value == null) { + return null; + } + return TIMESTAMP_FORMATTER.format(OffsetDateTime.ofInstant(java.time.Instant.ofEpochMilli(value), java.time.ZoneOffset.UTC)); + } } } diff --git a/src/test/java/com/github/cameltooling/dap/internal/types/UnmarshallerEventMessageTest.java b/src/test/java/com/github/cameltooling/dap/internal/types/UnmarshallerEventMessageTest.java index 38365db..b626800 100644 --- a/src/test/java/com/github/cameltooling/dap/internal/types/UnmarshallerEventMessageTest.java +++ b/src/test/java/com/github/cameltooling/dap/internal/types/UnmarshallerEventMessageTest.java @@ -18,6 +18,10 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + import org.junit.jupiter.api.Test; class UnmarshallerEventMessageTest { @@ -39,8 +43,16 @@ void testGetUnmarshalledEventMessage() { false basic.yaml:23 a-route-id + direct:start testBasicFlow-log-id + choice-parent-id + choice-when-id + when[simple{${body}}] + log + log:foo + 2 7F4C7BF7F3898E7-0000000000000000 + ABC-DEF-123 direct://testSetBreakpoint @@ -60,12 +72,37 @@ void testGetUnmarshalledEventMessage() { """; EventMessage message = new UnmarshallerEventMessage().getUnmarshalledEventMessage(eventMessageGeneratedWithCamel4_2); + long expectedTimestamp = OffsetDateTime.parse("2023-11-20T14:20:26.971+0100", + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ROOT)) + .toInstant() + .toEpochMilli(); assertThat(message).isNotNull(); assertThat(message.getUid()).isEqualTo(1); + assertThat(message.isFirst()).isFalse(); + assertThat(message.isLast()).isFalse(); + assertThat(message.isRest()).isFalse(); + assertThat(message.isTemplate()).isFalse(); + assertThat(message.getTimestamp()).isEqualTo(expectedTimestamp); + assertThat(message.getElapsed()).isEqualTo(1077); + assertThat(message.isDone()).isFalse(); + assertThat(message.isFailed()).isFalse(); + assertThat(message.getLocation()).isEqualTo("basic.yaml:23"); assertThat(message.getMessage().getHeaders()).hasSize(2); assertThat(message.getMessage().getExchangeProperties()).hasSize(3); assertThat(message.getMessage().getExchangeVariables()).hasSize(2); assertThat(message.getMessage().getBody()).isEqualTo("a body for test"); + assertThat(message.getProcessingThreadName()).isEqualTo("Camel (camel-1) thread #2 - ProducerTemplate"); + assertThat(message.getRouteId()).isEqualTo("a-route-id"); + assertThat(message.getFromRouteId()).isEqualTo("direct:start"); + assertThat(message.getToNode()).isEqualTo("testBasicFlow-log-id"); + assertThat(message.getToNodeParentId()).isEqualTo("choice-parent-id"); + assertThat(message.getToNodeParentWhenId()).isEqualTo("choice-when-id"); + assertThat(message.getToNodeParentWhenLabel()).isEqualTo("when[simple{${body}}]"); + assertThat(message.getToNodeShortName()).isEqualTo("log"); + assertThat(message.getToNodeLabel()).isEqualTo("log:foo"); + assertThat(message.getToNodeLevel()).isEqualTo(2); + assertThat(message.getExchangeId()).isEqualTo("7F4C7BF7F3898E7-0000000000000000"); + assertThat(message.getCorrelationExchangeId()).isEqualTo("ABC-DEF-123"); } }