This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 6f6325683c90 CAMEL-21438: Fix flaky JMS consumer startup races in IBM 
MQ and Artemis tests (#24390)
6f6325683c90 is described below

commit 6f6325683c90c11c83fef20039ffb7e184063322
Author: Adriano Machado <[email protected]>
AuthorDate: Fri Jul 3 16:34:58 2026 -0400

    CAMEL-21438: Fix flaky JMS consumer startup races in IBM MQ and Artemis 
tests (#24390)
    
    * CAMEL-21438: Improve IBM MQ test infra readiness before JMS tests
    
    Wait for the listener port and a successful JMS connection after the
    container starts so tests do not race against a broker that is not yet
    accepting clients.
    
    Co-authored-by: Cursor <[email protected]>
    
    * CAMEL-21438: Fix flaky JMS consumer startup races in tests
    
    Add a shared waitForJmsConsumerRoutes helper with a 100ms route uptime
    threshold and use it in IBM MQ and Artemis consumer tests that were
    sending before listeners were ready.
    
    Co-authored-by: Cursor <[email protected]>
    
    * CAMEL-21438: Address review feedback on JMS test race fixes
    
    - IBM MQ infra: combine the listener-port and log-message waits via
      WaitAllStrategy (chained waitingFor() replaces, not combines). Use
      WITH_INDIVIDUAL_TIMEOUTS_ONLY so each strategy keeps its own timeout
      instead of the 30s outer cap the default mode imposes.
    - AbstractJMSTest.waitForJmsConsumerRoutes: null-check the route so a
      missing/removed id times out clearly instead of throwing NPE; reword
      Javadoc to state uptime is a heuristic and document parameters.
    - AbstractPersistentJMSTest: add a minUptimeMillis overload.
    - SingleMessageSameTopicIT: restore the 200ms uptime threshold for topic
      consumers via a named constant at all call sites.
    - MultipleMessagesSameTopicIT: migrate the inline uptime waits to the
      shared helper (200ms).
    - JmsComponentIbmMQTest: wait only on the JMS consumer route and drop the
      unused producer routeId.
    
    Co-authored-by: Cursor <[email protected]>
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
    
    * CAMEL-21438: Address review feedback on JMS test helper placement
    
    Extract the JMS consumer-route wait helpers into a standalone
    JmsTestHelper so the same static API is reachable identically from all
    three JMS test lineages (AbstractJMSTest, AbstractPersistentJMSTest and
    plain CamelTestSupport tests), removing the dual static/instance-wrapper
    surface. Migrate all call sites accordingly.
    
    Replace the TestUtils.waitFor sleep-retry loop in the IBM MQ infra
    service with Awaitility for consistency with the rest of the change and
    clearer timeout diagnostics.
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
    
    * CAMEL-21438: Merge SingleMessageSameTopicIT @BeforeEach to fix ordering
    
    JUnit Jupiter does not honor @Order on @BeforeEach lifecycle methods and
    does not guarantee the execution order of multiple @BeforeEach methods in
    a single class. The split waitForConnections()/prepare() setup could
    therefore still send the message before the consumer routes were ready,
    preserving the race the PR set out to fix. Consolidate both into a single
    @BeforeEach so the wait always precedes the send.
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
    
    ---------
    
    Co-authored-by: Cursor <[email protected]>
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../camel/component/jms/JmsComponentIbmMQTest.java |  7 ++-
 .../apache/camel/component/jms/JmsTestHelper.java  | 72 ++++++++++++++++++++++
 .../consumers/MultipleMessagesSameTopicIT.java     |  8 ++-
 .../consumers/SingleMessageSameTopicIT.java        | 20 +++---
 .../consumers/TwoConsumerOnSameQueueIT.java        |  5 ++
 .../component/jms/issues/JmsReplyToIbmMQTest.java  |  6 +-
 .../jms/issues/JmsReplyToLoopIssueTest.java        | 11 ++--
 .../jms/temp/TemporaryQueueRouteTest.java          |  5 +-
 test-infra/camel-test-infra-ibmmq/pom.xml          |  6 ++
 .../services/IbmMQLocalContainerInfraService.java  | 34 +++++++++-
 10 files changed, 154 insertions(+), 20 deletions(-)

diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsComponentIbmMQTest.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsComponentIbmMQTest.java
index dca3c8acbb51..64249742852f 100644
--- 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsComponentIbmMQTest.java
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsComponentIbmMQTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.component.jms;
 
+import java.util.concurrent.TimeUnit;
+
 import jakarta.jms.ConnectionFactory;
 
 import org.apache.camel.CamelContext;
@@ -42,9 +44,11 @@ public class JmsComponentIbmMQTest extends CamelTestSupport {
         resultEndpoint.message(0).header("cheese").isEqualTo(123);
         resultEndpoint.message(0).body().isEqualTo("Hello there!");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, "consumer");
+
         template.sendBodyAndHeader("direct:start", "Hello world", "cheese", 
123);
 
-        MockEndpoint.assertIsSatisfied(context);
+        MockEndpoint.assertIsSatisfied(context, 20, TimeUnit.SECONDS);
     }
 
     @Override
@@ -56,6 +60,7 @@ public class JmsComponentIbmMQTest extends CamelTestSupport {
                         .to("jms:queue:DEV.QUEUE.1");
 
                 from("jms:queue:DEV.QUEUE.1")
+                        .routeId("consumer")
                         .process(exchange -> exchange.getIn().setBody("Hello 
there!"))
                         .to("mock:result");
             }
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsTestHelper.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsTestHelper.java
new file mode 100644
index 000000000000..f178d9db1754
--- /dev/null
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/JmsTestHelper.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.camel.component.jms;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.Route;
+import org.awaitility.Awaitility;
+
+/**
+ * Shared helpers for JMS tests. Kept as a standalone utility so the same 
static API is reachable from every JMS test
+ * lineage ({@link AbstractJMSTest}, {@link AbstractPersistentJMSTest} and 
plain {@code CamelTestSupport} tests) without
+ * forcing them into a common base class.
+ */
+public final class JmsTestHelper {
+
+    public static final long JMS_CONSUMER_ROUTE_UPTIME_MILLIS = 100;
+    public static final long JMS_CONSUMER_ROUTE_WAIT_AT_MOST_MILLIS = 30_000;
+
+    private JmsTestHelper() {
+    }
+
+    /**
+     * Wait until the given routes have been running for {@link 
#JMS_CONSUMER_ROUTE_UPTIME_MILLIS} milliseconds. Route
+     * uptime is only a heuristic for JMS listener registration on the broker: 
once a consumer route has been up for a
+     * short while its listener is expected to be subscribed, so tests can 
send without racing an unregistered listener.
+     *
+     * @param context  the Camel context owning the routes
+     * @param routeIds the ids of the consumer routes to wait for
+     */
+    public static void waitForJmsConsumerRoutes(CamelContext context, 
String... routeIds) {
+        waitForJmsConsumerRoutes(context, JMS_CONSUMER_ROUTE_UPTIME_MILLIS, 
routeIds);
+    }
+
+    /**
+     * Wait until the given routes have been running for at least {@code 
minUptimeMillis} milliseconds, giving up after
+     * {@link #JMS_CONSUMER_ROUTE_WAIT_AT_MOST_MILLIS} milliseconds. A missing 
route (unknown id, or one already removed
+     * from the context) is treated as not-yet-ready rather than throwing, so 
a misconfigured route id surfaces as a
+     * clear timeout instead of a {@link NullPointerException}.
+     *
+     * @param context         the Camel context owning the routes
+     * @param minUptimeMillis the minimum route uptime, in milliseconds, 
before a route is considered ready
+     * @param routeIds        the ids of the consumer routes to wait for
+     */
+    public static void waitForJmsConsumerRoutes(CamelContext context, long 
minUptimeMillis, String... routeIds) {
+        Awaitility.await().atMost(JMS_CONSUMER_ROUTE_WAIT_AT_MOST_MILLIS, 
TimeUnit.MILLISECONDS).until(() -> {
+            for (String routeId : routeIds) {
+                Route route = context.getRoute(routeId);
+                if (route == null || route.getUptimeMillis() <= 
minUptimeMillis) {
+                    return false;
+                }
+            }
+            return true;
+        });
+    }
+}
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/MultipleMessagesSameTopicIT.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/MultipleMessagesSameTopicIT.java
index 2ff59fe3de79..8b88ef431a24 100644
--- 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/MultipleMessagesSameTopicIT.java
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/MultipleMessagesSameTopicIT.java
@@ -19,8 +19,8 @@ package org.apache.camel.component.jms.integration.consumers;
 
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.jms.AbstractPersistentJMSTest;
+import org.apache.camel.component.jms.JmsTestHelper;
 import org.apache.camel.component.mock.MockEndpoint;
-import org.awaitility.Awaitility;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -29,6 +29,9 @@ import org.junit.jupiter.api.Test;
  */
 public class MultipleMessagesSameTopicIT extends AbstractPersistentJMSTest {
 
+    // topic subscriptions take a little longer to register than queue 
consumers, so keep the longer 200ms threshold
+    private static final long TOPIC_ROUTE_UPTIME_MILLIS = 200;
+
     @Test
     public void testMultipleMessagesOnSameTopic() throws Exception {
         getMockEndpoint("mock:a").expectedBodiesReceived("Hello Camel 1", 
"Hello Camel 2", "Hello Camel 3",
@@ -46,8 +49,7 @@ public class MultipleMessagesSameTopicIT extends 
AbstractPersistentJMSTest {
 
     @BeforeEach
     void waitForConnections() {
-        Awaitility.await().until(() -> context.getRoute("a").getUptimeMillis() 
> 200);
-        Awaitility.await().until(() -> context.getRoute("b").getUptimeMillis() 
> 200);
+        JmsTestHelper.waitForJmsConsumerRoutes(context, 
TOPIC_ROUTE_UPTIME_MILLIS, "a", "b");
     }
 
     @Override
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/SingleMessageSameTopicIT.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/SingleMessageSameTopicIT.java
index d7cfc3468bcc..72056ac3eb51 100644
--- 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/SingleMessageSameTopicIT.java
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/SingleMessageSameTopicIT.java
@@ -18,8 +18,8 @@ package org.apache.camel.component.jms.integration.consumers;
 
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.jms.AbstractPersistentJMSTest;
+import org.apache.camel.component.jms.JmsTestHelper;
 import org.apache.camel.component.mock.MockEndpoint;
-import org.awaitility.Awaitility;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.MethodOrderer;
 import org.junit.jupiter.api.Order;
@@ -31,8 +31,15 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
 public class SingleMessageSameTopicIT extends AbstractPersistentJMSTest {
 
+    // topic subscriptions take a little longer to register than queue 
consumers, so keep the longer 200ms threshold
+    private static final long TOPIC_ROUTE_UPTIME_MILLIS = 200;
+
     @BeforeEach
-    void prepare() {
+    void waitAndPrepare() {
+        // JUnit does not honor @Order on @BeforeEach methods, so the wait and 
the send must live in a single
+        // @BeforeEach to guarantee the consumer routes are ready before the 
message is published.
+        JmsTestHelper.waitForJmsConsumerRoutes(context, 
TOPIC_ROUTE_UPTIME_MILLIS, "a", "b");
+
         getMockEndpoint("mock:a").expectedBodiesReceived("Hello World");
         getMockEndpoint("mock:b").expectedBodiesReceived("Hello World");
 
@@ -59,6 +66,7 @@ public class SingleMessageSameTopicIT extends 
AbstractPersistentJMSTest {
         getMockEndpoint("mock:a").expectedMessageCount(0);
         getMockEndpoint("mock:b").expectedBodiesReceived("Bye World");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, 
TOPIC_ROUTE_UPTIME_MILLIS, "b");
         template.sendBody("activemq:topic:SingleMessageSameTopicIT", "Bye 
World");
 
         MockEndpoint.assertIsSatisfied(context);
@@ -72,6 +80,7 @@ public class SingleMessageSameTopicIT extends 
AbstractPersistentJMSTest {
         getMockEndpoint("mock:a").expectedBodiesReceived("Hello World");
         getMockEndpoint("mock:b").expectedBodiesReceived("Hello World");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, 
TOPIC_ROUTE_UPTIME_MILLIS, "a", "b");
         template.sendBody("activemq:topic:SingleMessageSameTopicIT", "Hello 
World");
     }
 
@@ -90,17 +99,12 @@ public class SingleMessageSameTopicIT extends 
AbstractPersistentJMSTest {
         getMockEndpoint("mock:a").expectedMessageCount(0);
         getMockEndpoint("mock:b").expectedBodiesReceived("Bye World");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, 
TOPIC_ROUTE_UPTIME_MILLIS, "b");
         template.sendBody("activemq:topic:SingleMessageSameTopicIT", "Bye 
World");
 
         MockEndpoint.assertIsSatisfied(context);
     }
 
-    @BeforeEach
-    void waitForConnections() {
-        Awaitility.await().until(() -> context.getRoute("a").getUptimeMillis() 
> 200);
-        Awaitility.await().until(() -> context.getRoute("b").getUptimeMillis() 
> 200);
-    }
-
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/TwoConsumerOnSameQueueIT.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/TwoConsumerOnSameQueueIT.java
index 9e62f8202fee..71c185abbbc5 100644
--- 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/TwoConsumerOnSameQueueIT.java
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/integration/consumers/TwoConsumerOnSameQueueIT.java
@@ -23,6 +23,7 @@ import jakarta.jms.ConnectionFactory;
 import org.apache.camel.CamelContext;
 import org.apache.camel.Exchange;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jms.JmsTestHelper;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.test.infra.artemis.common.ConnectionFactoryHelper;
 import org.apache.camel.test.infra.artemis.services.ArtemisService;
@@ -80,6 +81,7 @@ public class TwoConsumerOnSameQueueIT extends 
CamelTestSupport {
         getMockEndpoint("mock:a").expectedMessageCount(0);
         getMockEndpoint("mock:b").expectedBodiesReceived("Bye World", "Bye 
World");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, "b");
         template.sendBody("activemq:queue:TwoConsumerOnSameQueueTest", "Bye 
World");
         template.sendBody("activemq:queue:TwoConsumerOnSameQueueTest", "Bye 
World");
 
@@ -108,6 +110,7 @@ public class TwoConsumerOnSameQueueIT extends 
CamelTestSupport {
         getMockEndpoint("mock:a").expectedMessageCount(0);
         getMockEndpoint("mock:b").expectedBodiesReceived("Bye World", "Bye 
World");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, "b");
         template.sendBody("activemq:queue:TwoConsumerOnSameQueueTest", "Bye 
World");
         template.sendBody("activemq:queue:TwoConsumerOnSameQueueTest", "Bye 
World");
 
@@ -118,6 +121,8 @@ public class TwoConsumerOnSameQueueIT extends 
CamelTestSupport {
         final MockEndpoint mockB = getMockEndpoint("mock:b");
         final MockEndpoint mockA = getMockEndpoint("mock:a");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, "a", "b");
+
         template.sendBody("activemq:queue:TwoConsumerOnSameQueueTest", "Hello 
World");
         template.sendBody("activemq:queue:TwoConsumerOnSameQueueTest", "Hello 
World");
 
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToIbmMQTest.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToIbmMQTest.java
index 18bfa06b1358..d57e95c2f5b9 100644
--- 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToIbmMQTest.java
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToIbmMQTest.java
@@ -20,6 +20,7 @@ import jakarta.jms.ConnectionFactory;
 
 import org.apache.camel.CamelContext;
 import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jms.JmsTestHelper;
 import org.apache.camel.test.infra.ibmmq.common.ConnectionFactoryHelper;
 import org.apache.camel.test.infra.ibmmq.services.IbmMQService;
 import org.apache.camel.test.infra.ibmmq.services.IbmMQServiceFactory;
@@ -37,10 +38,12 @@ public class JmsReplyToIbmMQTest extends CamelTestSupport {
 
     @Test
     public void testCustomJMSReplyToInOut() {
+        JmsTestHelper.waitForJmsConsumerRoutes(context, "request");
+
         template.sendBody("jms:queue:DEV.QUEUE.1", "What is your name?");
 
         String reply
-                = consumer.receiveBody("jms:queue:DEV.QUEUE.2", 5000, 
String.class);
+                = consumer.receiveBody("jms:queue:DEV.QUEUE.2", 20000, 
String.class);
         assertEquals("My name is Camel", reply);
     }
 
@@ -50,6 +53,7 @@ public class JmsReplyToIbmMQTest extends CamelTestSupport {
             @Override
             public void configure() {
                 from("jms:queue:DEV.QUEUE.1?replyTo=queue:DEV.QUEUE.2")
+                        .routeId("request")
                         .to("log:hello")
                         .transform(constant("My name is Camel"));
             }
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToLoopIssueTest.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToLoopIssueTest.java
index 0967f5a91fb5..6880056322bc 100644
--- 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToLoopIssueTest.java
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/issues/JmsReplyToLoopIssueTest.java
@@ -23,6 +23,7 @@ import org.apache.camel.ConsumerTemplate;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.jms.AbstractJMSTest;
+import org.apache.camel.component.jms.JmsTestHelper;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.test.infra.core.CamelContextExtension;
 import org.apache.camel.test.infra.core.DefaultCamelContextExtension;
@@ -44,14 +45,16 @@ public class JmsReplyToLoopIssueTest extends 
AbstractJMSTest {
     protected ConsumerTemplate consumer;
 
     @Test
-    public void testReplyToLoopIssue() {
+    public void testReplyToLoopIssue() throws Exception {
         getMockEndpoint("mock:foo").expectedBodiesReceived("World");
         getMockEndpoint("mock:bar").expectedBodiesReceived("Bye World");
         getMockEndpoint("mock:done").expectedBodiesReceived("World");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, "foo", "bar");
+
         template.sendBodyAndHeader("direct:start", "World", "JMSReplyTo", 
"queue:JmsReplyToLoopIssueTest.bar");
 
-        // sleep a little to ensure we do not do endless loop
+        // fail fast if an endless reply loop keeps producing messages
         Awaitility.await().atMost(250, TimeUnit.MILLISECONDS).untilAsserted(() 
-> MockEndpoint.assertIsSatisfied(context));
     }
 
@@ -70,11 +73,11 @@ public class JmsReplyToLoopIssueTest extends 
AbstractJMSTest {
                         
.to("activemq:queue:JmsReplyToLoopIssueTest.foo?preserveMessageQos=true")
                         .to("mock:done");
 
-                from("activemq:queue:JmsReplyToLoopIssueTest.foo")
+                
from("activemq:queue:JmsReplyToLoopIssueTest.foo").routeId("foo")
                         .to("log:foo?showAll=true", "mock:foo")
                         .transform(body().prepend("Bye "));
 
-                from("activemq:queue:JmsReplyToLoopIssueTest.bar")
+                
from("activemq:queue:JmsReplyToLoopIssueTest.bar").routeId("bar")
                         .to("log:bar?showAll=true", "mock:bar");
             }
         };
diff --git 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/temp/TemporaryQueueRouteTest.java
 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/temp/TemporaryQueueRouteTest.java
index b9ac5418c466..c546bd78bc56 100644
--- 
a/components/camel-jms/src/test/java/org/apache/camel/component/jms/temp/TemporaryQueueRouteTest.java
+++ 
b/components/camel-jms/src/test/java/org/apache/camel/component/jms/temp/TemporaryQueueRouteTest.java
@@ -21,6 +21,7 @@ import org.apache.camel.ConsumerTemplate;
 import org.apache.camel.ProducerTemplate;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.component.jms.AbstractJMSTest;
+import org.apache.camel.component.jms.JmsTestHelper;
 import org.apache.camel.component.mock.MockEndpoint;
 import org.apache.camel.test.infra.core.CamelContextExtension;
 import org.apache.camel.test.infra.core.TransientCamelContextExtension;
@@ -43,6 +44,8 @@ public class TemporaryQueueRouteTest extends AbstractJMSTest {
         MockEndpoint endpoint = getMockEndpoint("mock:result");
         endpoint.expectedBodiesReceived("Hello World");
 
+        JmsTestHelper.waitForJmsConsumerRoutes(context, "consumer");
+
         template.sendBody(endpointUri, "Hello World");
 
         endpoint.assertIsSatisfied();
@@ -57,7 +60,7 @@ public class TemporaryQueueRouteTest extends AbstractJMSTest {
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
             public void configure() {
-                from(endpointUri).to("mock:result");
+                from(endpointUri).routeId("consumer").to("mock:result");
             }
         };
     }
diff --git a/test-infra/camel-test-infra-ibmmq/pom.xml 
b/test-infra/camel-test-infra-ibmmq/pom.xml
index b3811674cf50..55b7f4727f32 100644
--- a/test-infra/camel-test-infra-ibmmq/pom.xml
+++ b/test-infra/camel-test-infra-ibmmq/pom.xml
@@ -46,6 +46,12 @@
             <artifactId>com.ibm.mq.jakarta.client</artifactId>
             <version>${com-ibm-mq-jakarta-client-version}</version>
         </dependency>
+
+        <dependency>
+            <groupId>org.awaitility</groupId>
+            <artifactId>awaitility</artifactId>
+            <version>${awaitility-version}</version>
+        </dependency>
     </dependencies>
 
 </project>
diff --git 
a/test-infra/camel-test-infra-ibmmq/src/main/java/org/apache/camel/test/infra/ibmmq/services/IbmMQLocalContainerInfraService.java
 
b/test-infra/camel-test-infra-ibmmq/src/main/java/org/apache/camel/test/infra/ibmmq/services/IbmMQLocalContainerInfraService.java
index ccc9cd313463..00d32805b8a9 100644
--- 
a/test-infra/camel-test-infra-ibmmq/src/main/java/org/apache/camel/test/infra/ibmmq/services/IbmMQLocalContainerInfraService.java
+++ 
b/test-infra/camel-test-infra-ibmmq/src/main/java/org/apache/camel/test/infra/ibmmq/services/IbmMQLocalContainerInfraService.java
@@ -16,16 +16,25 @@
  */
 package org.apache.camel.test.infra.ibmmq.services;
 
+import java.util.concurrent.TimeUnit;
+
+import jakarta.jms.Connection;
+import jakarta.jms.ConnectionFactory;
+import jakarta.jms.JMSException;
+
 import org.apache.camel.spi.annotations.InfraService;
 import org.apache.camel.test.infra.common.LocalPropertyResolver;
 import org.apache.camel.test.infra.common.services.ContainerEnvironmentUtil;
 import org.apache.camel.test.infra.common.services.ContainerService;
+import org.apache.camel.test.infra.ibmmq.common.ConnectionFactoryHelper;
 import org.apache.camel.test.infra.ibmmq.common.IbmMQProperties;
+import org.awaitility.Awaitility;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.testcontainers.containers.GenericContainer;
 import org.testcontainers.containers.output.Slf4jLogConsumer;
 import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.containers.wait.strategy.WaitAllStrategy;
 import org.testcontainers.utility.DockerImageName;
 
 @InfraService(service = IbmMQInfraService.class,
@@ -61,8 +70,13 @@ public class IbmMQLocalContainerInfraService implements 
IbmMQInfraService, Conta
                         .withEnv("MQ_QMGR_NAME", 
IbmMQProperties.DEFAULT_QMGR_NAME)
                         .withEnv("MQ_APP_PASSWORD", 
IbmMQProperties.DEFAULT_APP_PASSWORD)
                         .withLogConsumer(new Slf4jLogConsumer(LOG))
-                        .waitingFor(Wait.forLogMessage(
-                                ".*Queued Publish/Subscribe Daemon started for 
queue manager.*", 1));
+                        // AND the listener-port and log-message checks; a 
plain chained waitingFor() would replace,
+                        // not combine, the strategies. 
WITH_INDIVIDUAL_TIMEOUTS_ONLY keeps each strategy's own timeout
+                        // instead of the 30s outer cap the default 
WaitAllStrategy mode would impose.
+                        .waitingFor(new 
WaitAllStrategy(WaitAllStrategy.Mode.WITH_INDIVIDUAL_TIMEOUTS_ONLY)
+                                .withStrategy(Wait.forListeningPort())
+                                .withStrategy(Wait.forLogMessage(
+                                        ".*Queued Publish/Subscribe Daemon 
started for queue manager.*", 1)));
 
                 ContainerEnvironmentUtil.configurePorts(this,
                         
ContainerEnvironmentUtil.isFixedPort(IbmMQLocalContainerInfraService.class),
@@ -88,6 +102,22 @@ public class IbmMQLocalContainerInfraService implements 
IbmMQInfraService, Conta
         // also starts admin console on https://localhost:9443/ibmmq/console, 
user: admin, password: passw0rd
         container.start();
         registerProperties();
+        waitForJmsConnection();
+    }
+
+    private void waitForJmsConnection() {
+        Awaitility.await("IBM MQ accepting JMS connections")
+                .atMost(30, TimeUnit.SECONDS)
+                .pollInterval(1, TimeUnit.SECONDS)
+                .ignoreExceptionsInstanceOf(JMSException.class)
+                .until(() -> {
+                    ConnectionFactory connectionFactory = 
ConnectionFactoryHelper.createConnectionFactory(
+                            queueManager(), channel(), listenerPort());
+                    try (Connection connection = 
connectionFactory.createConnection()) {
+                        connection.start();
+                    }
+                    return true;
+                });
     }
 
     @Override

Reply via email to