From 4d0d0f6e42cfb4b5be59685d8730e96dccdad2d9 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Tue, 19 May 2026 11:30:20 +0200 Subject: [PATCH 1/2] Have a fully JMS 1.0 compatible instrumentation --- .../instrumentation/jms/JMSDecorator.java | 15 ++ .../JMSMessageProducerInstrumentation.java | 4 +- .../jms/SessionInstrumentation.java | 4 +- .../src/test/groovy/JMS1Test.groovy | 33 ++- .../test/java/jms10mock/Jms10Connection.java | 130 ++++++++++ .../jms10mock/Jms10ConnectionFactory.java | 61 +++++ .../java/jms10mock/Jms10QueueReceiver.java | 59 +++++ .../test/java/jms10mock/Jms10QueueSender.java | 124 ++++++++++ .../src/test/java/jms10mock/Jms10Session.java | 229 ++++++++++++++++++ .../java/jms10mock/Jms10TopicPublisher.java | 137 +++++++++++ .../java/jms10mock/Jms10TopicSubscriber.java | 66 +++++ 11 files changed, 847 insertions(+), 15 deletions(-) create mode 100644 dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Connection.java create mode 100644 dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10ConnectionFactory.java create mode 100644 dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueReceiver.java create mode 100644 dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueSender.java create mode 100644 dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Session.java create mode 100644 dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicPublisher.java create mode 100644 dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicSubscriber.java diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java index 87fbfc55fc7..c9dd753b3da 100644 --- a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSDecorator.java @@ -19,10 +19,13 @@ import javax.jms.Destination; import javax.jms.JMSException; import javax.jms.Message; +import javax.jms.MessageProducer; import javax.jms.Queue; +import javax.jms.QueueSender; import javax.jms.TemporaryQueue; import javax.jms.TemporaryTopic; import javax.jms.Topic; +import javax.jms.TopicPublisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -265,6 +268,18 @@ public CharSequence toResourceName(String destinationName, boolean isQueue) { return joiner.apply(destinationName); } + public Destination getDestination(final MessageProducer messageProducer) throws JMSException { + try { + return messageProducer.getDestination(); // >= 1.1 + } catch (AbstractMethodError ignored) { + // <=1.1 getDestination is not available so we need to pay an additional instanceOf + if (messageProducer instanceof QueueSender) { + return ((QueueSender) messageProducer).getQueue(); + } + return ((TopicPublisher) messageProducer).getTopic(); + } + } + public String getDestinationName(Destination destination) { String name = null; try { diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java index 972b4382209..3dbfa0579f5 100644 --- a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/JMSMessageProducerInstrumentation.java @@ -90,10 +90,10 @@ public static AgentScope beforeSend( // fall-back when producer wasn't created via standard Session.createProducer API if (null != producerState) { resourceName = producerState.getResourceName(); - Destination destination = producer.getDestination(); + Destination destination = PRODUCER_DECORATE.getDestination(producer); destinationName = PRODUCER_DECORATE.getDestinationName(destination); } else { - Destination destination = producer.getDestination(); + Destination destination = PRODUCER_DECORATE.getDestination(producer); destinationName = PRODUCER_DECORATE.getDestinationName(destination); boolean isQueue = PRODUCER_DECORATE.isQueue(destination); resourceName = PRODUCER_DECORATE.toResourceName(destinationName, isQueue); diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java index 3f7b095b3b7..8c3ffa48231 100644 --- a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/main/java/datadog/trace/instrumentation/jms/SessionInstrumentation.java @@ -114,7 +114,7 @@ public static void bindProducerState( int ackMode; try { ackMode = session.getAcknowledgeMode(); - } catch (Exception ignored) { + } catch (Throwable ignored) { ackMode = Session.AUTO_ACKNOWLEDGE; } sessionState = @@ -155,7 +155,7 @@ public static void bindConsumerState( int ackMode; try { ackMode = session.getAcknowledgeMode(); - } catch (Exception ignored) { + } catch (Throwable ignored) { ackMode = Session.AUTO_ACKNOWLEDGE; } sessionState = diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy index f059016bc62..f24b7e8f9ff 100644 --- a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/groovy/JMS1Test.groovy @@ -7,32 +7,32 @@ import datadog.trace.agent.test.naming.VersionedNamingTestBase import datadog.trace.api.Config import datadog.trace.api.DDSpanTypes import datadog.trace.api.Trace -import datadog.trace.api.config.TracerConfig import datadog.trace.api.config.TraceInstrumentationConfig +import datadog.trace.api.config.TracerConfig import datadog.trace.bootstrap.instrumentation.api.InstrumentationTags import datadog.trace.bootstrap.instrumentation.api.Tags import datadog.trace.core.DDSpan -import org.apache.activemq.ActiveMQConnectionFactory -import org.apache.activemq.command.ActiveMQTextMessage -import org.apache.activemq.junit.EmbeddedActiveMQBroker -import spock.lang.Shared - +import java.util.concurrent.CountDownLatch +import java.util.concurrent.atomic.AtomicReference import javax.jms.Connection +import javax.jms.ConnectionFactory import javax.jms.Destination import javax.jms.Message import javax.jms.MessageListener +import javax.jms.Queue import javax.jms.QueueConnection import javax.jms.QueueSession import javax.jms.Session import javax.jms.TemporaryQueue import javax.jms.TemporaryTopic -import javax.jms.Queue -import javax.jms.Topic import javax.jms.TextMessage +import javax.jms.Topic import javax.jms.TopicConnection import javax.jms.TopicSession -import java.util.concurrent.CountDownLatch -import java.util.concurrent.atomic.AtomicReference +import jms10mock.Jms10ConnectionFactory +import org.apache.activemq.command.ActiveMQTextMessage +import org.apache.activemq.junit.EmbeddedActiveMQBroker +import spock.lang.Shared abstract class JMS1Test extends VersionedNamingTestBase { @Shared @@ -69,9 +69,13 @@ abstract class JMS1Test extends VersionedNamingTestBase { true } + def createConnectionFactory() { + broker.createConnectionFactory() + } + def setupSpec() { broker.start() - final ActiveMQConnectionFactory connectionFactory = broker.createConnectionFactory() + final ConnectionFactory connectionFactory = createConnectionFactory() connection = connectionFactory.createConnection() connection.start() @@ -1097,3 +1101,10 @@ class JMS1V1ForkedTest extends JMS1Test { "jms.process" } } + +class JMS10Test extends JMS1V0Test { + @Override + def createConnectionFactory() { + new Jms10ConnectionFactory(super.createConnectionFactory()) + } +} diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Connection.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Connection.java new file mode 100644 index 00000000000..0f8721a2b89 --- /dev/null +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Connection.java @@ -0,0 +1,130 @@ +package jms10mock; + +import javax.jms.Connection; +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionMetaData; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueSession; +import javax.jms.ServerSessionPool; +import javax.jms.Session; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; + +/** Wraps a real {@link Connection} but simulates a JMS 1.0 provider. */ +public class Jms10Connection implements QueueConnection, TopicConnection { + private final Connection delegate; + + public Jms10Connection(Connection delegate) { + this.delegate = delegate; + } + + // --- JMS 1.1-only unified Connection method --- + + @Override + public Session createSession(boolean transacted, int acknowledgeMode) throws JMSException { + throw new AbstractMethodError( + "JMS 1.0 provider does not implement createSession(boolean, int) on Connection"); + } + + // --- JMS 1.0 QueueConnection methods --- + + @Override + public QueueSession createQueueSession(boolean transacted, int acknowledgeMode) + throws JMSException { + return new Jms10Session(delegate.createSession(transacted, acknowledgeMode)); + } + + // --- JMS 1.0 TopicConnection methods --- + + @Override + public TopicSession createTopicSession(boolean transacted, int acknowledgeMode) + throws JMSException { + return new Jms10Session(delegate.createSession(transacted, acknowledgeMode)); + } + + // --- Common Connection methods --- + + @Override + public String getClientID() throws JMSException { + return delegate.getClientID(); + } + + @Override + public void setClientID(String clientID) throws JMSException { + delegate.setClientID(clientID); + } + + @Override + public ConnectionMetaData getMetaData() throws JMSException { + return delegate.getMetaData(); + } + + @Override + public ExceptionListener getExceptionListener() throws JMSException { + return delegate.getExceptionListener(); + } + + @Override + public void setExceptionListener(ExceptionListener listener) throws JMSException { + delegate.setExceptionListener(listener); + } + + @Override + public void start() throws JMSException { + delegate.start(); + } + + @Override + public void stop() throws JMSException { + delegate.stop(); + } + + @Override + public void close() throws JMSException { + delegate.close(); + } + + // --- ConnectionConsumer methods — not commonly used, throw for JMS 1.1 unified form --- + + @Override + public ConnectionConsumer createConnectionConsumer( + Destination destination, + String messageSelector, + ServerSessionPool sessionPool, + int maxMessages) + throws JMSException { + throw new AbstractMethodError( + "JMS 1.0 provider does not implement createConnectionConsumer(Destination, ...)"); + } + + @Override + public ConnectionConsumer createConnectionConsumer( + Queue queue, String messageSelector, ServerSessionPool sessionPool, int maxMessages) + throws JMSException { + return delegate.createConnectionConsumer(queue, messageSelector, sessionPool, maxMessages); + } + + @Override + public ConnectionConsumer createConnectionConsumer( + Topic topic, String messageSelector, ServerSessionPool sessionPool, int maxMessages) + throws JMSException { + return delegate.createConnectionConsumer(topic, messageSelector, sessionPool, maxMessages); + } + + @Override + public ConnectionConsumer createDurableConnectionConsumer( + Topic topic, + String subscriptionName, + String messageSelector, + ServerSessionPool sessionPool, + int maxMessages) + throws JMSException { + return delegate.createDurableConnectionConsumer( + topic, subscriptionName, messageSelector, sessionPool, maxMessages); + } +} diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10ConnectionFactory.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10ConnectionFactory.java new file mode 100644 index 00000000000..1660765f731 --- /dev/null +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10ConnectionFactory.java @@ -0,0 +1,61 @@ +package jms10mock; + +import javax.jms.Connection; +import javax.jms.ConnectionFactory; +import javax.jms.JMSException; +import javax.jms.QueueConnection; +import javax.jms.QueueConnectionFactory; +import javax.jms.TopicConnection; +import javax.jms.TopicConnectionFactory; + +/** + * Wraps a real {@link ConnectionFactory} but simulates a JMS 1.0 provider. + * + *

In JMS 1.0, clients used the domain-specific {@link QueueConnectionFactory} and {@link + * TopicConnectionFactory} to obtain connections. The unified {@link ConnectionFactory} and its + * {@code createConnection()} methods are JMS 1.1 additions that this wrapper does not support. + */ +public class Jms10ConnectionFactory implements QueueConnectionFactory, TopicConnectionFactory { + private final ConnectionFactory delegate; + + public Jms10ConnectionFactory(ConnectionFactory delegate) { + this.delegate = delegate; + } + + // --- JMS 1.1-only unified ConnectionFactory methods --- + + @Override + public Connection createConnection() throws JMSException { + return delegate.createConnection(); + } + + @Override + public Connection createConnection(String userName, String password) throws JMSException { + return delegate.createConnection(userName, password); + } + + // --- JMS 1.0 QueueConnectionFactory methods --- + @Override + public QueueConnection createQueueConnection() throws JMSException { + return new Jms10Connection(delegate.createConnection()); + } + + @Override + public QueueConnection createQueueConnection(String userName, String password) + throws JMSException { + return new Jms10Connection(delegate.createConnection(userName, password)); + } + + // --- JMS 1.0 TopicConnectionFactory methods --- + + @Override + public TopicConnection createTopicConnection() throws JMSException { + return new Jms10Connection(delegate.createConnection()); + } + + @Override + public TopicConnection createTopicConnection(String userName, String password) + throws JMSException { + return new Jms10Connection(delegate.createConnection(userName, password)); + } +} diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueReceiver.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueReceiver.java new file mode 100644 index 00000000000..92b8f8ec93b --- /dev/null +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueReceiver.java @@ -0,0 +1,59 @@ +package jms10mock; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Queue; +import javax.jms.QueueReceiver; + +/** Wraps a real {@link MessageConsumer} but simulates a JMS 1.0 provider. */ +public class Jms10QueueReceiver implements QueueReceiver { + private final MessageConsumer delegate; + private final Queue queue; + + public Jms10QueueReceiver(MessageConsumer delegate, Queue queue) { + this.delegate = delegate; + this.queue = queue; + } + + @Override + public Queue getQueue() { + return queue; + } + + @Override + public String getMessageSelector() throws JMSException { + return delegate.getMessageSelector(); + } + + @Override + public MessageListener getMessageListener() throws JMSException { + return delegate.getMessageListener(); + } + + @Override + public void setMessageListener(MessageListener listener) throws JMSException { + delegate.setMessageListener(listener); + } + + @Override + public Message receive() throws JMSException { + return delegate.receive(); + } + + @Override + public Message receive(long timeout) throws JMSException { + return delegate.receive(timeout); + } + + @Override + public Message receiveNoWait() throws JMSException { + return delegate.receiveNoWait(); + } + + @Override + public void close() throws JMSException { + delegate.close(); + } +} diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueSender.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueSender.java new file mode 100644 index 00000000000..1f888203483 --- /dev/null +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueSender.java @@ -0,0 +1,124 @@ +package jms10mock; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueSender; + +/** Wraps a real {@link MessageProducer} but simulates a JMS 1.0 provider. */ +public class Jms10QueueSender implements QueueSender { + private final MessageProducer delegate; + private final Queue queue; + + public Jms10QueueSender(MessageProducer delegate, Queue queue) { + this.delegate = delegate; + this.queue = queue; + } + + // --- JMS 1.1-only methods — not present in JMS 1.0 --- + + @Override + public Destination getDestination() { + throw new AbstractMethodError("JMS 1.0 provider does not implement getDestination()"); + } + + @Override + public void send(Destination destination, Message message) throws JMSException { + delegate.send(destination, message); + } + + @Override + public void send( + Destination destination, Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + delegate.send(destination, message, deliveryMode, priority, timeToLive); + } + + // --- JMS 1.0 QueueSender methods --- + + @Override + public Queue getQueue() { + return queue; + } + + @Override + public void send(Message message) throws JMSException { + delegate.send(message); + } + + @Override + public void send(Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + delegate.send(message, deliveryMode, priority, timeToLive); + } + + @Override + public void send(Queue queue, Message message) throws JMSException { + delegate.send(queue, message); + } + + @Override + public void send(Queue queue, Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + delegate.send(queue, message, deliveryMode, priority, timeToLive); + } + + // --- MessageProducer config methods --- + + @Override + public void close() throws JMSException { + delegate.close(); + } + + @Override + public void setDisableMessageID(boolean value) throws JMSException { + delegate.setDisableMessageID(value); + } + + @Override + public boolean getDisableMessageID() throws JMSException { + return delegate.getDisableMessageID(); + } + + @Override + public void setDisableMessageTimestamp(boolean value) throws JMSException { + delegate.setDisableMessageTimestamp(value); + } + + @Override + public boolean getDisableMessageTimestamp() throws JMSException { + return delegate.getDisableMessageTimestamp(); + } + + @Override + public void setDeliveryMode(int deliveryMode) throws JMSException { + delegate.setDeliveryMode(deliveryMode); + } + + @Override + public int getDeliveryMode() throws JMSException { + return delegate.getDeliveryMode(); + } + + @Override + public void setPriority(int defaultPriority) throws JMSException { + delegate.setPriority(defaultPriority); + } + + @Override + public int getPriority() throws JMSException { + return delegate.getPriority(); + } + + @Override + public void setTimeToLive(long timeToLive) throws JMSException { + delegate.setTimeToLive(timeToLive); + } + + @Override + public long getTimeToLive() throws JMSException { + return delegate.getTimeToLive(); + } +} diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Session.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Session.java new file mode 100644 index 00000000000..65629319237 --- /dev/null +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Session.java @@ -0,0 +1,229 @@ +package jms10mock; + +import java.io.Serializable; +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.Session; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; + +/** Wraps a real {@link Session} but simulates a JMS 1.0 provider. */ +public class Jms10Session implements QueueSession, TopicSession { + private final Session delegate; + + public Jms10Session(Session delegate) { + this.delegate = delegate; + } + + // --- JMS 1.1-only unified Session methods — not present in JMS 1.0 --- + + @Override + public MessageProducer createProducer(Destination destination) throws JMSException { + return delegate.createProducer(destination); + } + + @Override + public MessageConsumer createConsumer(Destination destination) throws JMSException { + return delegate.createConsumer(destination); + } + + @Override + public MessageConsumer createConsumer(Destination destination, String messageSelector) + throws JMSException { + return delegate.createConsumer(destination, messageSelector); + } + + @Override + public MessageConsumer createConsumer( + Destination destination, String messageSelector, boolean noLocal) throws JMSException { + return delegate.createConsumer(destination, messageSelector, noLocal); + } + + // --- JMS 1.0 QueueSession methods --- + + @Override + public Queue createQueue(String queueName) throws JMSException { + return delegate.createQueue(queueName); + } + + @Override + public QueueReceiver createReceiver(Queue queue) throws JMSException { + return new Jms10QueueReceiver(delegate.createConsumer(queue), queue); + } + + @Override + public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException { + return new Jms10QueueReceiver(delegate.createConsumer(queue, messageSelector), queue); + } + + @Override + public QueueSender createSender(Queue queue) throws JMSException { + return new Jms10QueueSender(delegate.createProducer(queue), queue); + } + + @Override + public QueueBrowser createBrowser(Queue queue) throws JMSException { + return delegate.createBrowser(queue); + } + + @Override + public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException { + return delegate.createBrowser(queue, messageSelector); + } + + @Override + public TemporaryQueue createTemporaryQueue() throws JMSException { + return delegate.createTemporaryQueue(); + } + + // --- JMS 1.0 TopicSession methods --- + + @Override + public Topic createTopic(String topicName) throws JMSException { + return delegate.createTopic(topicName); + } + + @Override + public TopicSubscriber createSubscriber(Topic topic) throws JMSException { + return new Jms10TopicSubscriber(delegate.createConsumer(topic), topic, false); + } + + @Override + public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) + throws JMSException { + return new Jms10TopicSubscriber( + delegate.createConsumer(topic, messageSelector, noLocal), topic, noLocal); + } + + @Override + public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { + return new Jms10TopicSubscriber(delegate.createDurableSubscriber(topic, name), topic, false); + } + + @Override + public TopicSubscriber createDurableSubscriber( + Topic topic, String name, String messageSelector, boolean noLocal) throws JMSException { + return new Jms10TopicSubscriber( + delegate.createDurableSubscriber(topic, name, messageSelector, noLocal), topic, noLocal); + } + + @Override + public TopicPublisher createPublisher(Topic topic) throws JMSException { + return new Jms10TopicPublisher(delegate.createProducer(topic), topic); + } + + @Override + public TemporaryTopic createTemporaryTopic() throws JMSException { + return delegate.createTemporaryTopic(); + } + + @Override + public void unsubscribe(String name) throws JMSException { + delegate.unsubscribe(name); + } + + // --- Common Session methods --- + + @Override + public BytesMessage createBytesMessage() throws JMSException { + return delegate.createBytesMessage(); + } + + @Override + public MapMessage createMapMessage() throws JMSException { + return delegate.createMapMessage(); + } + + @Override + public Message createMessage() throws JMSException { + return delegate.createMessage(); + } + + @Override + public ObjectMessage createObjectMessage() throws JMSException { + return delegate.createObjectMessage(); + } + + @Override + public ObjectMessage createObjectMessage(Serializable object) throws JMSException { + return delegate.createObjectMessage(object); + } + + @Override + public StreamMessage createStreamMessage() throws JMSException { + return delegate.createStreamMessage(); + } + + @Override + public TextMessage createTextMessage() throws JMSException { + return delegate.createTextMessage(); + } + + @Override + public TextMessage createTextMessage(String text) throws JMSException { + return delegate.createTextMessage(text); + } + + @Override + public boolean getTransacted() throws JMSException { + return delegate.getTransacted(); + } + + @Override + public int getAcknowledgeMode() { + throw new AbstractMethodError("JMS 1.0 provider does not implement getAcknowledgeMode()"); + } + + @Override + public void commit() throws JMSException { + delegate.commit(); + } + + @Override + public void rollback() throws JMSException { + delegate.rollback(); + } + + @Override + public void close() throws JMSException { + delegate.close(); + } + + @Override + public void recover() throws JMSException { + delegate.recover(); + } + + @Override + public MessageListener getMessageListener() throws JMSException { + return delegate.getMessageListener(); + } + + @Override + public void setMessageListener(MessageListener listener) throws JMSException { + delegate.setMessageListener(listener); + } + + @Override + public void run() { + delegate.run(); + } +} diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicPublisher.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicPublisher.java new file mode 100644 index 00000000000..6f3c1e38663 --- /dev/null +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicPublisher.java @@ -0,0 +1,137 @@ +package jms10mock; + +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageProducer; +import javax.jms.Topic; +import javax.jms.TopicPublisher; + +/** Wraps a real {@link MessageProducer} but simulates a JMS 1.0 provider. */ +public class Jms10TopicPublisher implements TopicPublisher { + private final MessageProducer delegate; + private final Topic topic; + + public Jms10TopicPublisher(MessageProducer delegate, Topic topic) { + this.delegate = delegate; + this.topic = topic; + } + + // --- JMS 1.1-only methods — not present in JMS 1.0 --- + + @Override + public Destination getDestination() { + throw new AbstractMethodError("JMS 1.0 provider does not implement getDestination()"); + } + + @Override + public void send(Destination destination, Message message) throws JMSException { + delegate.send(destination, message); + } + + @Override + public void send( + Destination destination, Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + delegate.send(destination, message, deliveryMode, priority, timeToLive); + } + + // --- JMS 1.0 TopicPublisher methods --- + + @Override + public Topic getTopic() { + return topic; + } + + @Override + public void publish(Message message) throws JMSException { + delegate.send(message); + } + + @Override + public void publish(Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + delegate.send(message, deliveryMode, priority, timeToLive); + } + + @Override + public void publish(Topic topic, Message message) throws JMSException { + delegate.send(topic, message); + } + + @Override + public void publish(Topic topic, Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + delegate.send(topic, message, deliveryMode, priority, timeToLive); + } + + // --- MessageProducer send methods (also available via publish in 1.0) --- + + @Override + public void send(Message message) throws JMSException { + delegate.send(message); + } + + @Override + public void send(Message message, int deliveryMode, int priority, long timeToLive) + throws JMSException { + delegate.send(message, deliveryMode, priority, timeToLive); + } + + // --- MessageProducer config methods --- + + @Override + public void close() throws JMSException { + delegate.close(); + } + + @Override + public void setDisableMessageID(boolean value) throws JMSException { + delegate.setDisableMessageID(value); + } + + @Override + public boolean getDisableMessageID() throws JMSException { + return delegate.getDisableMessageID(); + } + + @Override + public void setDisableMessageTimestamp(boolean value) throws JMSException { + delegate.setDisableMessageTimestamp(value); + } + + @Override + public boolean getDisableMessageTimestamp() throws JMSException { + return delegate.getDisableMessageTimestamp(); + } + + @Override + public void setDeliveryMode(int deliveryMode) throws JMSException { + delegate.setDeliveryMode(deliveryMode); + } + + @Override + public int getDeliveryMode() throws JMSException { + return delegate.getDeliveryMode(); + } + + @Override + public void setPriority(int defaultPriority) throws JMSException { + delegate.setPriority(defaultPriority); + } + + @Override + public int getPriority() throws JMSException { + return delegate.getPriority(); + } + + @Override + public void setTimeToLive(long timeToLive) throws JMSException { + delegate.setTimeToLive(timeToLive); + } + + @Override + public long getTimeToLive() throws JMSException { + return delegate.getTimeToLive(); + } +} diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicSubscriber.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicSubscriber.java new file mode 100644 index 00000000000..97ca5ea2343 --- /dev/null +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicSubscriber.java @@ -0,0 +1,66 @@ +package jms10mock; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.Topic; +import javax.jms.TopicSubscriber; + +/** Wraps a real {@link MessageConsumer} but simulates a JMS 1.0 provider. */ +public class Jms10TopicSubscriber implements TopicSubscriber { + private final MessageConsumer delegate; + private final Topic topic; + private final boolean noLocal; + + public Jms10TopicSubscriber(MessageConsumer delegate, Topic topic, boolean noLocal) { + this.delegate = delegate; + this.topic = topic; + this.noLocal = noLocal; + } + + @Override + public Topic getTopic() { + return topic; + } + + @Override + public boolean getNoLocal() { + return noLocal; + } + + @Override + public String getMessageSelector() throws JMSException { + return delegate.getMessageSelector(); + } + + @Override + public MessageListener getMessageListener() throws JMSException { + return delegate.getMessageListener(); + } + + @Override + public void setMessageListener(MessageListener listener) throws JMSException { + delegate.setMessageListener(listener); + } + + @Override + public Message receive() throws JMSException { + return delegate.receive(); + } + + @Override + public Message receive(long timeout) throws JMSException { + return delegate.receive(timeout); + } + + @Override + public Message receiveNoWait() throws JMSException { + return delegate.receiveNoWait(); + } + + @Override + public void close() throws JMSException { + delegate.close(); + } +} From 04a3a803823d378ec6667f33917068a59dac85c6 Mon Sep 17 00:00:00 2001 From: Andrea Marziali Date: Tue, 19 May 2026 14:00:55 +0200 Subject: [PATCH 2/2] move stubs to test fixture --- dd-java-agent/instrumentation/jms/javax-jms-1.1/build.gradle | 3 +++ .../{test => testFixtures}/java/jms10mock/Jms10Connection.java | 0 .../java/jms10mock/Jms10ConnectionFactory.java | 0 .../java/jms10mock/Jms10QueueReceiver.java | 0 .../java/jms10mock/Jms10QueueSender.java | 0 .../{test => testFixtures}/java/jms10mock/Jms10Session.java | 0 .../java/jms10mock/Jms10TopicPublisher.java | 0 .../java/jms10mock/Jms10TopicSubscriber.java | 0 8 files changed, 3 insertions(+) rename dd-java-agent/instrumentation/jms/javax-jms-1.1/src/{test => testFixtures}/java/jms10mock/Jms10Connection.java (100%) rename dd-java-agent/instrumentation/jms/javax-jms-1.1/src/{test => testFixtures}/java/jms10mock/Jms10ConnectionFactory.java (100%) rename dd-java-agent/instrumentation/jms/javax-jms-1.1/src/{test => testFixtures}/java/jms10mock/Jms10QueueReceiver.java (100%) rename dd-java-agent/instrumentation/jms/javax-jms-1.1/src/{test => testFixtures}/java/jms10mock/Jms10QueueSender.java (100%) rename dd-java-agent/instrumentation/jms/javax-jms-1.1/src/{test => testFixtures}/java/jms10mock/Jms10Session.java (100%) rename dd-java-agent/instrumentation/jms/javax-jms-1.1/src/{test => testFixtures}/java/jms10mock/Jms10TopicPublisher.java (100%) rename dd-java-agent/instrumentation/jms/javax-jms-1.1/src/{test => testFixtures}/java/jms10mock/Jms10TopicSubscriber.java (100%) diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/build.gradle b/dd-java-agent/instrumentation/jms/javax-jms-1.1/build.gradle index 739bc235846..74b9d050a9a 100644 --- a/dd-java-agent/instrumentation/jms/javax-jms-1.1/build.gradle +++ b/dd-java-agent/instrumentation/jms/javax-jms-1.1/build.gradle @@ -14,6 +14,7 @@ muzzle { } apply from: "$rootDir/gradle/java.gradle" +apply plugin: 'java-test-fixtures' repositories { maven { @@ -33,6 +34,8 @@ tasks.named("latestDepTest", Test) { dependencies { compileOnly group: 'javax.jms', name: 'jms-api', version: '1.1-rev-1' + testFixturesCompileOnly group: 'javax.jms', name: 'jms-api', version: '1.1-rev-1' + testImplementation project(':dd-java-agent:instrumentation:datadog:tracing:trace-annotation') testImplementation group: 'org.apache.activemq.tooling', name: 'activemq-junit', version: '5.14.5' testImplementation group: 'org.apache.activemq', name: 'activemq-pool', version: '5.14.5' diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Connection.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10Connection.java similarity index 100% rename from dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Connection.java rename to dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10Connection.java diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10ConnectionFactory.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10ConnectionFactory.java similarity index 100% rename from dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10ConnectionFactory.java rename to dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10ConnectionFactory.java diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueReceiver.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10QueueReceiver.java similarity index 100% rename from dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueReceiver.java rename to dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10QueueReceiver.java diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueSender.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10QueueSender.java similarity index 100% rename from dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10QueueSender.java rename to dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10QueueSender.java diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Session.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10Session.java similarity index 100% rename from dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10Session.java rename to dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10Session.java diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicPublisher.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10TopicPublisher.java similarity index 100% rename from dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicPublisher.java rename to dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10TopicPublisher.java diff --git a/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicSubscriber.java b/dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10TopicSubscriber.java similarity index 100% rename from dd-java-agent/instrumentation/jms/javax-jms-1.1/src/test/java/jms10mock/Jms10TopicSubscriber.java rename to dd-java-agent/instrumentation/jms/javax-jms-1.1/src/testFixtures/java/jms10mock/Jms10TopicSubscriber.java