This is an automated email from the ASF dual-hosted git repository. gnodet pushed a commit to branch fix/jms-security-test-jdk25 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 209c428e973c0ec20ca18c487802730e579fd0cd Author: Guillaume Nodet <[email protected]> AuthorDate: Wed Mar 11 23:14:52 2026 +0100 CAMEL-21917: Fix flaky JMS transacted and security tests - Pre-create custom DLQ queues in Artemis broker config, since Artemis does not auto-create dead letter queues for custom DLQ addresses without autoCreateDeadLetterResources enabled - Use per-class DLQ names to avoid interference between concurrent tests - Set explicit maxDeliveryAttempts (1 or 3) per test for faster and deterministic redelivery behavior - Enforce test method ordering with @TestMethodOrder/@Order to prevent message leakage via shared broker queues (RouteIdTransactedIT, JmsToJmsTransactedIT) - Increase Awaitility/timeout values for CI stability under load - Fix JmsToJmsTransactedSecurityIT to work on JDK 25 by using SASL_PLAINTEXT for Artemis security configuration Co-Authored-By: Claude Opus 4.6 <[email protected]> --- .../jms/JmsProducerWithJMSHeaderTest.java | 2 +- .../jms/JmsRouteWithInOnlyAndMultipleAcksTest.java | 2 +- .../component/jms/JmsRouteWithInOnlyTest.java | 2 +- .../jms/async/AsyncConsumerInOutTwoTest.java | 2 +- .../JmsAddAndRemoveRouteManagementIT.java | 36 ++++++++-- .../JmsDestinationProducedHeaderIT.java | 4 +- .../jms/integration/JmsLoadBalanceFailOverIT.java | 2 +- ...eFailOverWithForceSendOriginalJmsMessageIT.java | 2 +- .../jms/integration/JmsRoutingSlipInOutIT.java | 2 +- .../jms/integration/JmsToDSendDynamicIT.java | 6 +- .../issues/JmsAnotherCustomJMSReplyToIT.java | 2 +- .../polling/JmsPollingHighTimeOutIT.java | 2 +- .../spring/polling/JmsPollingConsumerSpringIT.java | 2 +- .../spring/tx/JmsToJmsTransactedIT.java | 27 +++++-- .../integration/spring/tx/RouteIdTransactedIT.java | 6 ++ .../JMSTransactionIsTransactedRedeliveredIT.java | 2 +- .../tx/security/JmsToJmsTransactedSecurityIT.java | 17 ++++- ...dLetterChannelHandlerRollbackOnExceptionIT.java | 32 ++++++--- ...tterChannelNotHandlerRollbackOnExceptionIT.java | 83 +++++++++++++++++++--- ...TransactedOnExceptionRollbackOnExceptionIT.java | 28 ++++++-- .../camel/component/jms/support/MyCoolBean.java | 4 +- .../services/AbstractArtemisEmbeddedService.java | 12 ++-- 22 files changed, 214 insertions(+), 63 deletions(-) diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsProducerWithJMSHeaderTest.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsProducerWithJMSHeaderTest.java index 254029bb6a5b..0d872aa4e6f4 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsProducerWithJMSHeaderTest.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsProducerWithJMSHeaderTest.java @@ -203,7 +203,7 @@ public class JmsProducerWithJMSHeaderTest extends AbstractJMSTest { assertNotNull(bar, "Should be a message on queue"); template.send("activemq:queue:fooJmsProducerWithJMSHeaderTest?preserveMessageQos=true", bar); - Awaitility.await().atMost(1, TimeUnit.SECONDS) + Awaitility.await().atMost(5, TimeUnit.SECONDS) .untilAsserted(() -> MockEndpoint.assertIsSatisfied(context)); } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyAndMultipleAcksTest.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyAndMultipleAcksTest.java index 17bd37feb858..7379dd92c7fb 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyAndMultipleAcksTest.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyAndMultipleAcksTest.java @@ -36,7 +36,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; -@Timeout(10) +@Timeout(30) @TestInstance(TestInstance.Lifecycle.PER_METHOD) public class JmsRouteWithInOnlyAndMultipleAcksTest extends AbstractJMSTest { diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyTest.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyTest.java index dbcdb93ee772..d8df6a7fc2b9 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyTest.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsRouteWithInOnlyTest.java @@ -40,7 +40,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * Unit test inspired by user forum */ @TestInstance(TestInstance.Lifecycle.PER_METHOD) -@Timeout(10) +@Timeout(30) public class JmsRouteWithInOnlyTest extends AbstractJMSTest { @Order(2) diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/async/AsyncConsumerInOutTwoTest.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/async/AsyncConsumerInOutTwoTest.java index 70aade6e3f0c..2709425f029b 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/async/AsyncConsumerInOutTwoTest.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/async/AsyncConsumerInOutTwoTest.java @@ -32,7 +32,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; -@Timeout(10) +@Timeout(30) public class AsyncConsumerInOutTwoTest extends AbstractJMSTest { @Order(2) diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsAddAndRemoveRouteManagementIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsAddAndRemoveRouteManagementIT.java index 59c57374e83a..42b91af22429 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsAddAndRemoveRouteManagementIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsAddAndRemoveRouteManagementIT.java @@ -16,7 +16,10 @@ */ package org.apache.camel.component.jms.integration; +import java.util.HashSet; import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import javax.management.MBeanServer; import javax.management.ObjectName; @@ -31,6 +34,8 @@ import org.apache.camel.impl.DefaultCamelContext; import org.apache.camel.test.infra.core.CamelContextExtension; import org.apache.camel.test.infra.core.DefaultCamelContextExtension; import org.apache.camel.test.infra.core.annotations.ContextFixture; +import org.awaitility.Awaitility; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; @@ -39,8 +44,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Test that all thread pools are removed when adding and removing a route dynamically. This test manipulates the thread * pools, so it's not a good candidate for running in parallel. @@ -68,8 +71,9 @@ public class JmsAddAndRemoveRouteManagementIT extends AbstractJMSTest { @Test public void testAddAndRemoveRoute() throws Exception { MBeanServer mbeanServer = getMBeanServer(); + ObjectName query = new ObjectName("*:type=threadpools,*"); - Set<ObjectName> before = mbeanServer.queryNames(new ObjectName("*:type=threadpools,*"), null); + Set<ObjectName> before = mbeanServer.queryNames(query, null); getMockEndpoint("mock:result").expectedMessageCount(1); @@ -81,8 +85,20 @@ public class JmsAddAndRemoveRouteManagementIT extends AbstractJMSTest { } }); - Set<ObjectName> during = mbeanServer.queryNames(new ObjectName("*:type=threadpools,*"), null); - assertEquals(before.size() + 1, during.size(), "There should be one more thread pool in JMX"); + // Wait for the new route's thread pool to be registered and identify it + AtomicReference<Set<ObjectName>> duringRef = new AtomicReference<>(); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + Set<ObjectName> during = mbeanServer.queryNames(query, null); + Set<ObjectName> added = new HashSet<>(during); + added.removeAll(before); + Assertions.assertFalse(added.isEmpty(), + "There should be at least one new thread pool in JMX"); + duringRef.set(during); + }); + + // Identify the thread pools added by the new route + Set<ObjectName> addedPools = new HashSet<>(duringRef.get()); + addedPools.removeAll(before); template.sendBody("activemq:queue:JmsAddAndRemoveRouteManagementTest.in", "Hello World"); @@ -92,8 +108,14 @@ public class JmsAddAndRemoveRouteManagementIT extends AbstractJMSTest { context.getRouteController().stopRoute("myNewRoute"); context.removeRoute("myNewRoute"); - Set<ObjectName> after = mbeanServer.queryNames(new ObjectName("*:type=threadpools,*"), null); - assertEquals(before.size(), after.size(), "Should have removed all thread pools from removed route"); + // Verify the thread pools from the removed route are cleaned up + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + Set<ObjectName> after = mbeanServer.queryNames(query, null); + for (ObjectName added : addedPools) { + Assertions.assertFalse(after.contains(added), + "Thread pool from removed route should be cleaned up: " + added); + } + }); } @Override diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsDestinationProducedHeaderIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsDestinationProducedHeaderIT.java index a304b4d98639..9ad99a66cf9d 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsDestinationProducedHeaderIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsDestinationProducedHeaderIT.java @@ -50,9 +50,9 @@ public class JmsDestinationProducedHeaderIT extends AbstractPersistentJMSTest { assertEquals(2, count, "There should only be 1 activemq endpoint"); // and the messages should be in the queues - String out = consumer.receiveBody("activemq:queue:JmsDestinationProducedHeaderTest.bar2", 2000, String.class); + String out = consumer.receiveBody("activemq:queue:JmsDestinationProducedHeaderTest.bar2", 5000, String.class); assertEquals("Hello bar", out); - out = consumer.receiveBody("activemq:queue:JmsDestinationProducedHeaderTest.beer2", 2000, String.class); + out = consumer.receiveBody("activemq:queue:JmsDestinationProducedHeaderTest.beer2", 5000, String.class); assertEquals("Hello beer", out); } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverIT.java index 306f5f60d3bf..20f3b2b872b6 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverIT.java @@ -36,7 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit test for Camel loadbalancer failover with JMS */ -@Timeout(10) +@Timeout(30) public class JmsLoadBalanceFailOverIT extends CamelTestSupport { @RegisterExtension diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverWithForceSendOriginalJmsMessageIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverWithForceSendOriginalJmsMessageIT.java index cd49a3fd0372..e8ec14f16e4a 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverWithForceSendOriginalJmsMessageIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsLoadBalanceFailOverWithForceSendOriginalJmsMessageIT.java @@ -38,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; /** * Unit test for Camel loadbalancer failover with JMS */ -@Timeout(10) +@Timeout(30) public class JmsLoadBalanceFailOverWithForceSendOriginalJmsMessageIT extends AbstractJMSTest { @Order(2) @RegisterExtension diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsRoutingSlipInOutIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsRoutingSlipInOutIT.java index 58a9a4d1838f..323639a5e84c 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsRoutingSlipInOutIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsRoutingSlipInOutIT.java @@ -36,7 +36,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.api.extension.RegisterExtension; -@Timeout(10) +@Timeout(30) public class JmsRoutingSlipInOutIT extends AbstractJMSTest { @Order(2) diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsToDSendDynamicIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsToDSendDynamicIT.java index 09fc1e72d248..71f6b178b5b2 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsToDSendDynamicIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/JmsToDSendDynamicIT.java @@ -34,16 +34,16 @@ public class JmsToDSendDynamicIT extends AbstractPersistentJMSTest { assertEquals(1, count, "There should only be 1 activemq endpoint"); // and the messages should be in the queues - String out = consumer.receiveBody("activemq:queue:JmsToDSendDynamicIT.bar", 2000, String.class); + String out = consumer.receiveBody("activemq:queue:JmsToDSendDynamicIT.bar", 5000, String.class); assertEquals("Hello bar", out); - out = consumer.receiveBody("activemq:queue:JmsToDSendDynamicIT.beer", 2000, String.class); + out = consumer.receiveBody("activemq:queue:JmsToDSendDynamicIT.beer", 5000, String.class); assertEquals("Hello beer", out); } @Test public void testToDSlashed() { template.sendBodyAndHeader("direct:startSlashed", "Hello bar", "where", "JmsToDSendDynamicIT.bar"); - String out = consumer.receiveBody("activemq://JmsToDSendDynamicIT.bar", 2000, String.class); + String out = consumer.receiveBody("activemq://JmsToDSendDynamicIT.bar", 5000, String.class); assertEquals("Hello bar", out); } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/issues/JmsAnotherCustomJMSReplyToIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/issues/JmsAnotherCustomJMSReplyToIT.java index 234b83a604b4..4689932bc0e5 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/issues/JmsAnotherCustomJMSReplyToIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/issues/JmsAnotherCustomJMSReplyToIT.java @@ -69,7 +69,7 @@ public class JmsAnotherCustomJMSReplyToIT extends AbstractJMSTest { // send reply template.sendBody("activemq:" + replyTo.getQueueName(), "My name is Arnio"); - Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> MockEndpoint.assertIsSatisfied(context)); + Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> MockEndpoint.assertIsSatisfied(context)); } @Override diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/polling/JmsPollingHighTimeOutIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/polling/JmsPollingHighTimeOutIT.java index 170320906ef7..948bc092f1bf 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/polling/JmsPollingHighTimeOutIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/polling/JmsPollingHighTimeOutIT.java @@ -33,7 +33,7 @@ class JmsPollingHighTimeOutIT extends JmsPollingConsumerIT { @BeforeEach void setupConsumer() { Executors.newSingleThreadExecutor().execute(() -> { - body = consumer.receiveBody("activemq:queue.JmsPollingConsumerTest.start", 3000, String.class); + body = consumer.receiveBody("activemq:queue.JmsPollingConsumerTest.start", 5000, String.class); template.sendBody("activemq:queue.JmsPollingConsumerTest.foo", body + " Claus"); }); } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/polling/JmsPollingConsumerSpringIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/polling/JmsPollingConsumerSpringIT.java index 8e004e3dbc97..276f6961d9fa 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/polling/JmsPollingConsumerSpringIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/polling/JmsPollingConsumerSpringIT.java @@ -95,7 +95,7 @@ public class JmsPollingConsumerSpringIT extends SpringJMSBasic { StringBuilder result = new StringBuilder(); Exchange exchange; - while ((exchange = consumer.receive("jms:JmsPollingConsumerSpringITQueue", 2000)) != null) { + while ((exchange = consumer.receive("jms:JmsPollingConsumerSpringITQueue", 5000)) != null) { result.append(exchange.getIn().getBody(String.class)); } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/JmsToJmsTransactedIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/JmsToJmsTransactedIT.java index a008a29f0d76..0da4420f14a7 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/JmsToJmsTransactedIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/JmsToJmsTransactedIT.java @@ -16,10 +16,12 @@ */ package org.apache.camel.component.jms.integration.spring.tx; +import org.apache.activemq.artemis.api.core.QueueConfiguration; +import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.mock.MockEndpoint; import org.apache.camel.test.infra.artemis.services.ArtemisService; -import org.apache.camel.test.infra.artemis.services.ArtemisServiceFactory; +import org.apache.camel.test.infra.artemis.services.ArtemisVMService; import org.apache.camel.test.spring.junit6.CamelSpringTestSupport; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; @@ -29,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.parallel.Isolated; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -36,11 +39,25 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @TestInstance(TestInstance.Lifecycle.PER_METHOD) @Tags({ @Tag("not-parallel"), @Tag("spring"), @Tag("tx") }) @TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@Isolated("Uses a shared DLQ queue name and embedded VM broker") public final class JmsToJmsTransactedIT extends CamelSpringTestSupport { + private static final int MAX_DELIVERY_ATTEMPTS = 3; + @Order(0) @RegisterExtension - public static ArtemisService service = ArtemisServiceFactory.createVMService(); + public static ArtemisService service = createArtemisService(); + + static ArtemisService createArtemisService() { + ArtemisVMService svc = new ArtemisVMService(); + svc.customConfiguration(cfg -> { + cfg.getAddressSettings().values() + .forEach(s -> s.setMaxDeliveryAttempts(MAX_DELIVERY_ATTEMPTS)); + cfg.addQueueConfiguration( + QueueConfiguration.of("DLQ").setRoutingType(RoutingType.ANYCAST)); + }); + return svc; + } /** * Used by spring xml configurations @@ -97,7 +114,7 @@ public final class JmsToJmsTransactedIT extends CamelSpringTestSupport { bar.expectedMessageCount(0); MockEndpoint start = getMockEndpoint("mock:start"); - start.expectedMessageCount(7); // default number of redeliveries by AMQ is 6 so we get 6+1 + start.expectedMessageCount(MAX_DELIVERY_ATTEMPTS); template.sendBody("activemq:queue:JmsToJmsTransactedIT", "Hello World"); @@ -125,14 +142,14 @@ public final class JmsToJmsTransactedIT extends CamelSpringTestSupport { bar.expectedMessageCount(0); MockEndpoint start = getMockEndpoint("mock:start"); - start.expectedMessageCount(7); // default number of redeliveries by AMQ is 6 so we get 6+1 + start.expectedMessageCount(MAX_DELIVERY_ATTEMPTS); template.sendBody("activemq:queue:JmsToJmsTransactedIT", "Hello World"); MockEndpoint.assertIsSatisfied(context); // it should be moved to DLQ in JMS broker - Object body = consumer.receiveBody("activemq:queue:DLQ", 2000); + Object body = consumer.receiveBody("activemq:queue:DLQ", 10000); assertEquals("Hello World", body); } } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/RouteIdTransactedIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/RouteIdTransactedIT.java index acca1b2dfdb5..114ea0a20e69 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/RouteIdTransactedIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/RouteIdTransactedIT.java @@ -19,14 +19,18 @@ package org.apache.camel.component.jms.integration.spring.tx; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.jms.integration.spring.AbstractSpringJMSITSupport; import org.apache.camel.component.mock.MockEndpoint; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.springframework.context.support.ClassPathXmlApplicationContext; import static org.junit.jupiter.api.Assertions.assertEquals; @Tags({ @Tag("not-parallel"), @Tag("spring"), @Tag("tx") }) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class RouteIdTransactedIT extends AbstractSpringJMSITSupport { @Override @@ -35,6 +39,7 @@ public class RouteIdTransactedIT extends AbstractSpringJMSITSupport { "/org/apache/camel/component/jms/integration/spring/tx/RouteIdTransactedIT.xml"); } + @Order(1) @Test public void testRouteId() throws Exception { getMockEndpoint("mock:error").expectedMessageCount(0); @@ -48,6 +53,7 @@ public class RouteIdTransactedIT extends AbstractSpringJMSITSupport { assertEquals("myCoolRoute", id); } + @Order(2) @Test public void testRouteIdFailed() throws Exception { getMockEndpoint("mock:error").expectedMessageCount(1); diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/error/JMSTransactionIsTransactedRedeliveredIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/error/JMSTransactionIsTransactedRedeliveredIT.java index d46ce0be2c81..bf53bf8b2e38 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/error/JMSTransactionIsTransactedRedeliveredIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/error/JMSTransactionIsTransactedRedeliveredIT.java @@ -84,7 +84,7 @@ public class JMSTransactionIsTransactedRedeliveredIT extends AbstractSpringJMSIT // check JMX stats // need a little sleep to ensure JMX is updated - final Set<ObjectName> objectNames = Awaitility.await().atMost(1000, TimeUnit.MILLISECONDS) + final Set<ObjectName> objectNames = Awaitility.await().atMost(5, TimeUnit.SECONDS) .until(() -> getMBeanServer() .queryNames(new ObjectName("org.apache.camel:context=camel-*,type=routes,name=\"myRoute\""), null), Matchers.hasSize(1)); diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/security/JmsToJmsTransactedSecurityIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/security/JmsToJmsTransactedSecurityIT.java index 014eb9637690..e44c69f081ce 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/security/JmsToJmsTransactedSecurityIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/spring/tx/security/JmsToJmsTransactedSecurityIT.java @@ -16,6 +16,9 @@ */ package org.apache.camel.component.jms.integration.spring.tx.security; +import org.apache.activemq.artemis.core.config.FileDeploymentManager; +import org.apache.activemq.artemis.core.config.impl.FileConfiguration; +import org.apache.activemq.artemis.core.config.impl.LegacyJMSConfiguration; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; @@ -49,9 +52,17 @@ public class JmsToJmsTransactedSecurityIT extends CamelSpringTestSupport { ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), securityConfig); - activeMQServer = ActiveMQServers.newActiveMQServer( - "org/apache/camel/component/jms/integration/spring/tx/security/artemis-security.xml", null, - securityManager); + FileConfiguration config = new FileConfiguration(); + LegacyJMSConfiguration legacyJMSConfiguration = new LegacyJMSConfiguration(config); + new FileDeploymentManager( + "org/apache/camel/component/jms/integration/spring/tx/security/artemis-security.xml") + .addDeployable(config).addDeployable(legacyJMSConfiguration).readConfiguration(); + + // Use the 4-arg version with enablePersistence=false to honor the XML's persistence-enabled=false. + // The 3-arg version hardcodes enablePersistence=true, which causes the broker to get stuck + // in STARTING state on JDK 25 due to journal initialization issues. + // TODO: revert this workaround once https://github.com/apache/activemq-artemis/pull/6286 is released + activeMQServer = ActiveMQServers.newActiveMQServer(config, null, securityManager, false); activeMQServer.start(); } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT.java index 29470a832c97..ee99afecb2a8 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT.java @@ -18,6 +18,7 @@ package org.apache.camel.component.jms.integration.tx; import jakarta.jms.ConnectionFactory; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.Handler; @@ -26,7 +27,7 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.jms.JmsComponent; import org.apache.camel.test.infra.artemis.common.ConnectionFactoryHelper; import org.apache.camel.test.infra.artemis.services.ArtemisService; -import org.apache.camel.test.infra.artemis.services.ArtemisServiceFactory; +import org.apache.camel.test.infra.artemis.services.ArtemisVMService; import org.apache.camel.test.junit6.CamelTestSupport; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; @@ -36,11 +37,25 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.apache.camel.component.jms.JmsComponent.jmsComponentTransacted; import static org.junit.jupiter.api.Assertions.assertNull; -// This test cannot run in parallel: it reads from the default DLQ and there could be more messages there @Tags({ @Tag("not-parallel"), @Tag("transaction") }) public class JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT extends CamelTestSupport { + + private static final String DLQ_NAME + = "DLQ." + JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT.class.getSimpleName(); + @RegisterExtension - public static ArtemisService service = ArtemisServiceFactory.createVMService(); + public static ArtemisService service = createArtemisService(); + + static ArtemisService createArtemisService() { + ArtemisVMService svc = new ArtemisVMService(); + svc.customConfiguration( + cfg -> cfg.getAddressSettings().values() + .forEach(s -> { + s.setMaxDeliveryAttempts(1); + s.setDeadLetterAddress(SimpleString.of(DLQ_NAME)); + })); + return svc; + } public static class BadErrorHandler { @Handler @@ -51,10 +66,6 @@ public class JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT extends protected final String testingEndpoint = "activemq:test." + getClass().getName(); - protected boolean isHandleNew() { - return true; - } - @Override protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { @@ -63,7 +74,7 @@ public class JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT extends // we use DLC to handle the exception but if it throw a new exception // then the DLC handles that too (the transaction will always commit) errorHandler(deadLetterChannel("bean:" + BadErrorHandler.class.getName()) - .deadLetterHandleNewException(isHandleNew()) + .deadLetterHandleNewException(true) .logNewException(true)); from(testingEndpoint) @@ -78,8 +89,8 @@ public class JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT extends template.sendBody(testingEndpoint, "Hello World"); // as we handle new exception, then the exception is ignored - // and causes the transaction to commit, so there is no message in the ActiveMQ DLQ queue - Object dlqBody = consumer.receiveBody("activemq:DLQ", 2000); + // and causes the transaction to commit, so there is no message in the DLQ queue + Object dlqBody = consumer.receiveBody("activemq:" + DLQ_NAME, 10000); assertNull(dlqBody, "Should not rollback the transaction"); } @@ -87,7 +98,6 @@ public class JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT extends protected CamelContext createCamelContext() throws Exception { CamelContext camelContext = super.createCamelContext(); - // no redeliveries ConnectionFactory connectionFactory = ConnectionFactoryHelper.createConnectionFactory(service, 0); JmsComponent component = jmsComponentTransacted(connectionFactory); diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelNotHandlerRollbackOnExceptionIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelNotHandlerRollbackOnExceptionIT.java index 1ae8f0e8f077..e48d5b895534 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelNotHandlerRollbackOnExceptionIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedDeadLetterChannelNotHandlerRollbackOnExceptionIT.java @@ -16,31 +16,98 @@ */ package org.apache.camel.component.jms.integration.tx; +import jakarta.jms.ConnectionFactory; + +import org.apache.activemq.artemis.api.core.QueueConfiguration; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.camel.CamelContext; +import org.apache.camel.Exchange; +import org.apache.camel.Handler; +import org.apache.camel.RuntimeCamelException; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.jms.JmsComponent; +import org.apache.camel.test.infra.artemis.common.ConnectionFactoryHelper; +import org.apache.camel.test.infra.artemis.services.ArtemisService; +import org.apache.camel.test.infra.artemis.services.ArtemisVMService; +import org.apache.camel.test.junit6.CamelTestSupport; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import static org.apache.camel.component.jms.JmsComponent.jmsComponentTransacted; import static org.junit.jupiter.api.Assertions.assertEquals; -// This test cannot run in parallel: it reads from the default DLQ and there could be more messages there @Tags({ @Tag("not-parallel"), @Tag("transaction") }) -public class JmsTransactedDeadLetterChannelNotHandlerRollbackOnExceptionIT - extends JmsTransactedDeadLetterChannelHandlerRollbackOnExceptionIT { +public class JmsTransactedDeadLetterChannelNotHandlerRollbackOnExceptionIT extends CamelTestSupport { - @Override - protected boolean isHandleNew() { - return false; + private static final String DLQ_NAME + = "DLQ." + JmsTransactedDeadLetterChannelNotHandlerRollbackOnExceptionIT.class.getSimpleName(); + + @RegisterExtension + public static ArtemisService service = createArtemisService(); + + static ArtemisService createArtemisService() { + ArtemisVMService svc = new ArtemisVMService(); + svc.customConfiguration(cfg -> { + cfg.getAddressSettings().values() + .forEach(s -> { + s.setMaxDeliveryAttempts(1); + s.setDeadLetterAddress(SimpleString.of(DLQ_NAME)); + }); + cfg.addQueueConfiguration( + QueueConfiguration.of(DLQ_NAME).setRoutingType(RoutingType.ANYCAST)); + }); + return svc; } + public static class BadErrorHandler { + @Handler + public void onException(Exchange exchange, Exception exception) { + throw new RuntimeCamelException("error in errorhandler"); + } + } + + private final String testingEndpoint = "activemq:test." + getClass().getName(); + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + // we use DLC to handle the exception but if it throw a new exception + // then the DLC does NOT handle that (the transaction will rollback) + errorHandler(deadLetterChannel("bean:" + BadErrorHandler.class.getName()) + .deadLetterHandleNewException(false) + .logNewException(true)); + + from(testingEndpoint) + .log("Incoming JMS message ${body}") + .throwException(new RuntimeCamelException("bad error")); + } + }; + } + @Test public void shouldNotLoseMessagesOnExceptionInErrorHandler() { template.sendBody(testingEndpoint, "Hello World"); // as we do not handle new exception, then the exception propagates back - // and causes the transaction to rollback, and we can find the message in the ActiveMQ DLQ - Object dlqBody = consumer.receiveBody("activemq:DLQ", 2000); + // and causes the transaction to rollback, and we can find the message in the DLQ + Object dlqBody = consumer.receiveBody("activemq:" + DLQ_NAME, 30000); assertEquals("Hello World", dlqBody); } + @Override + protected CamelContext createCamelContext() throws Exception { + CamelContext camelContext = super.createCamelContext(); + + ConnectionFactory connectionFactory = ConnectionFactoryHelper.createConnectionFactory(service, 0); + + JmsComponent component = jmsComponentTransacted(connectionFactory); + camelContext.addComponent("activemq", component); + return camelContext; + } + } diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedOnExceptionRollbackOnExceptionIT.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedOnExceptionRollbackOnExceptionIT.java index 4580a39ba0ec..0b8a70c35b2d 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedOnExceptionRollbackOnExceptionIT.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/tx/JmsTransactedOnExceptionRollbackOnExceptionIT.java @@ -18,6 +18,9 @@ package org.apache.camel.component.jms.integration.tx; import jakarta.jms.ConnectionFactory; +import org.apache.activemq.artemis.api.core.QueueConfiguration; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.api.core.SimpleString; import org.apache.camel.CamelContext; import org.apache.camel.Exchange; import org.apache.camel.Handler; @@ -26,7 +29,7 @@ import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.jms.JmsComponent; import org.apache.camel.test.infra.artemis.common.ConnectionFactoryHelper; import org.apache.camel.test.infra.artemis.services.ArtemisService; -import org.apache.camel.test.infra.artemis.services.ArtemisServiceFactory; +import org.apache.camel.test.infra.artemis.services.ArtemisVMService; import org.apache.camel.test.junit6.CamelTestSupport; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Tags; @@ -36,11 +39,27 @@ import org.junit.jupiter.api.extension.RegisterExtension; import static org.apache.camel.component.jms.JmsComponent.jmsComponentTransacted; import static org.junit.jupiter.api.Assertions.assertEquals; -// This test cannot run in parallel: it reads from the default DLQ and there could be more messages there @Tags({ @Tag("not-parallel"), @Tag("transaction") }) public class JmsTransactedOnExceptionRollbackOnExceptionIT extends CamelTestSupport { + + private static final String DLQ_NAME = "DLQ." + JmsTransactedOnExceptionRollbackOnExceptionIT.class.getSimpleName(); + @RegisterExtension - public static ArtemisService service = ArtemisServiceFactory.createVMService(); + public static ArtemisService service = createArtemisService(); + + static ArtemisService createArtemisService() { + ArtemisVMService svc = new ArtemisVMService(); + svc.customConfiguration(cfg -> { + cfg.getAddressSettings().values() + .forEach(s -> { + s.setMaxDeliveryAttempts(1); + s.setDeadLetterAddress(SimpleString.of(DLQ_NAME)); + }); + cfg.addQueueConfiguration( + QueueConfiguration.of(DLQ_NAME).setRoutingType(RoutingType.ANYCAST)); + }); + return svc; + } public static class BadErrorHandler { @@ -73,7 +92,7 @@ public class JmsTransactedOnExceptionRollbackOnExceptionIT extends CamelTestSupp public void shouldNotLoseMessagesOnExceptionInErrorHandler() throws Exception { template.sendBody(testingEndpoint, "Hello World"); - Object dlqBody = consumer.receiveBody("activemq:DLQ", 2000); + Object dlqBody = consumer.receiveBody("activemq:" + DLQ_NAME, 30000); assertEquals("Hello World", dlqBody); } @@ -82,7 +101,6 @@ public class JmsTransactedOnExceptionRollbackOnExceptionIT extends CamelTestSupp protected CamelContext createCamelContext() throws Exception { CamelContext camelContext = super.createCamelContext(); - // no redeliveries ConnectionFactory connectionFactory = ConnectionFactoryHelper.createConnectionFactory(service, 0); JmsComponent component = jmsComponentTransacted(connectionFactory); diff --git a/components/camel-jms/src/test/java/org/apache/camel/component/jms/support/MyCoolBean.java b/components/camel-jms/src/test/java/org/apache/camel/component/jms/support/MyCoolBean.java index d0fb3df3eff4..76fd0bb13c1b 100644 --- a/components/camel-jms/src/test/java/org/apache/camel/component/jms/support/MyCoolBean.java +++ b/components/camel-jms/src/test/java/org/apache/camel/component/jms/support/MyCoolBean.java @@ -40,9 +40,9 @@ public final class MyCoolBean { public void someBusinessLogic() { // loop to empty queue while (true) { - // receive the message from the queue, wait at most 2 sec + // receive the message from the queue, wait at most 5 sec try { - String msg = consumer.receiveBody("activemq:" + queueName + ".in", 2000, String.class); + String msg = consumer.receiveBody("activemq:" + queueName + ".in", 5000, String.class); if (msg == null) { // no more messages in queue break; diff --git a/test-infra/camel-test-infra-artemis/src/main/java/org/apache/camel/test/infra/artemis/services/AbstractArtemisEmbeddedService.java b/test-infra/camel-test-infra-artemis/src/main/java/org/apache/camel/test/infra/artemis/services/AbstractArtemisEmbeddedService.java index 41bf9b4b0f88..6aab1711c47d 100644 --- a/test-infra/camel-test-infra-artemis/src/main/java/org/apache/camel/test/infra/artemis/services/AbstractArtemisEmbeddedService.java +++ b/test-infra/camel-test-infra-artemis/src/main/java/org/apache/camel/test/infra/artemis/services/AbstractArtemisEmbeddedService.java @@ -19,7 +19,7 @@ package org.apache.camel.test.infra.artemis.services; import java.io.File; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.LongAdder; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import jakarta.jms.ConnectionFactory; @@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory; public abstract class AbstractArtemisEmbeddedService implements ArtemisInfraService, ConnectionFactoryAware { protected static final Logger LOG = LoggerFactory.getLogger(AbstractArtemisEmbeddedService.class); - private static final LongAdder BROKER_COUNT = new LongAdder(); + private static final AtomicInteger BROKER_COUNT = new AtomicInteger(); protected final EmbeddedActiveMQ embeddedBrokerService; private final Configuration artemisConfiguration; @@ -107,9 +107,7 @@ public abstract class AbstractArtemisEmbeddedService implements ArtemisInfraServ * @return the broker ID to use */ protected int computeBrokerId() { - final int brokerId = BROKER_COUNT.intValue(); - BROKER_COUNT.increment(); - return brokerId; + return BROKER_COUNT.getAndIncrement(); } private static File createInstance(int brokerId) { @@ -171,7 +169,9 @@ public abstract class AbstractArtemisEmbeddedService implements ArtemisInfraServ embeddedBrokerService.start(); - embeddedBrokerService.getActiveMQServer().waitForActivation(20, TimeUnit.SECONDS); + if (!embeddedBrokerService.getActiveMQServer().waitForActivation(20, TimeUnit.SECONDS)) { + LOG.warn("Artemis broker did not activate within 20 seconds, proceeding anyway"); + } } } catch (Exception e) { LOG.warn("Unable to start embedded Artemis broker: {}", e.getMessage(), e);
