This is an automated email from the ASF dual-hosted git repository.
gnodet 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 ee335421b7d0 CAMEL-23902: Fix flaky UndertowWsConsumerRouteTest
(#24421)
ee335421b7d0 is described below
commit ee335421b7d0faf049b09361fc089b08daf3a9d3
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Jul 6 11:26:47 2026 +0200
CAMEL-23902: Fix flaky UndertowWsConsumerRouteTest (#24421)
* CAMEL-23902: Fix flaky UndertowWsConsumerRouteTest
The WebSocket tests in UndertowWsConsumerRouteTest fail intermittently on
JDK 17 because WebSocket.sendText() returns a CompletableFuture that must
complete before the next send. When two messages are sent in rapid
succession, the second send can fail silently with IllegalStateException.
Replace CountDownLatch-based assertTrue(await(10)) with Awaitility
polling in echo(), echoMulti(), sendToAll() and connectionKeyList().
For tests that send multiple messages on the same connection (echo,
connectionKeyList), wait for the echo of the first message before
sending the next to avoid the JDK WebSocket pending-send conflict.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* CAMEL-23902: Avoid ConcurrentModificationException during Awaitility
polling
Address Copilot review: getReceived(String.class) iterates over
WebsocketTestClient's non-thread-safe ArrayList via for-each, risking
ConcurrentModificationException while the listener thread adds messages.
Switch Awaitility polling to use getReceived().size() (index-based, no
iterator). Content assertions via getReceived(String.class) are only
called after the size check confirms all expected messages arrived.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
---
components/camel-undertow/pom.xml | 6 +++
.../undertow/ws/UndertowWsConsumerRouteTest.java | 45 ++++++++++++++--------
2 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/components/camel-undertow/pom.xml
b/components/camel-undertow/pom.xml
index 5da11c33f5ef..5ed99a6b734e 100644
--- a/components/camel-undertow/pom.xml
+++ b/components/camel-undertow/pom.xml
@@ -127,6 +127,12 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <version>${awaitility-version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.eclipse.jetty.http2</groupId>
<artifactId>jetty-http2-client</artifactId>
diff --git
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
index 84752761c891..4fd6fc7a8e26 100644
---
a/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
+++
b/components/camel-undertow/src/test/java/org/apache/camel/component/undertow/ws/UndertowWsConsumerRouteTest.java
@@ -46,6 +46,7 @@ import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -168,9 +169,15 @@ public class UndertowWsConsumerRouteTest extends
BaseUndertowTest {
wsclient1.connect();
wsclient1.sendTextMessage("Test1");
- wsclient1.sendTextMessage("Test2");
+ // Wait for the first echo before sending the next message to avoid
+ // IllegalStateException from JDK WebSocket when a send is still
pending.
+ // Poll using size() to avoid iterating the non-thread-safe received
list.
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient1.getReceived().size() >= 1);
- assertTrue(wsclient1.await(10));
+ wsclient1.sendTextMessage("Test2");
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient1.getReceived().size() >= 2);
assertEquals(Arrays.asList("Test1", "Test2"),
wsclient1.getReceived(String.class));
@@ -187,8 +194,9 @@ public class UndertowWsConsumerRouteTest extends
BaseUndertowTest {
wsclient1.sendTextMessage("Gambas");
wsclient2.sendTextMessage("Calamares");
- assertTrue(wsclient1.await(10));
- assertTrue(wsclient2.await(10));
+ // Poll using size() to avoid iterating the non-thread-safe received
list
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient1.getReceived().size() >= 1 &&
wsclient2.getReceived().size() >= 1);
assertEquals(List.of("Gambas"), wsclient1.getReceived(String.class));
assertEquals(List.of("Calamares"),
wsclient2.getReceived(String.class));
@@ -207,12 +215,12 @@ public class UndertowWsConsumerRouteTest extends
BaseUndertowTest {
wsclient1.sendTextMessage("Gambas");
wsclient2.sendTextMessage("Calamares");
- assertTrue(wsclient1.await(10));
- assertTrue(wsclient2.await(10));
+ // Poll using size() to avoid iterating the non-thread-safe received
list
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient1.getReceived().size() >= 2 &&
wsclient2.getReceived().size() >= 2);
List<String> received1 = wsclient1.getReceived(String.class);
assertEquals(2, received1.size());
-
assertTrue(received1.contains("Gambas"));
assertTrue(received1.contains("Calamares"));
@@ -292,12 +300,16 @@ public class UndertowWsConsumerRouteTest extends
BaseUndertowTest {
wsclient2.connect();
wsclient3.connect();
- wsclient1.await(10);
+ // Poll using size() to avoid iterating the non-thread-safe received
list
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient1.getReceived().size() >= 1);
final String connectionKey1 = assertConnected(wsclient1);
assertNotNull(connectionKey1);
- wsclient2.await(10);
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient2.getReceived().size() >= 1);
final String connectionKey2 = assertConnected(wsclient2);
- wsclient3.await(10);
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient3.getReceived().size() >= 1);
final String connectionKey3 = assertConnected(wsclient3);
wsclient1.reset(1);
@@ -305,13 +317,16 @@ public class UndertowWsConsumerRouteTest extends
BaseUndertowTest {
wsclient3.reset(1);
final String broadcastMsg = BROADCAST_MESSAGE_PREFIX + connectionKey2
+ " " + connectionKey3;
wsclient1.sendTextMessage(broadcastMsg); // this one should go to
wsclient2 and wsclient3
- wsclient1.sendTextMessage("private"); // this one should go to
wsclient1 only
-
- wsclient2.await(10);
+ // Wait for broadcast delivery before sending the next message to avoid
+ // IllegalStateException from JDK WebSocket when a send is still
pending
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient2.getReceived().size() >= 1 &&
wsclient3.getReceived().size() >= 1);
assertEquals(broadcastMsg, wsclient2.getReceived(String.class).get(0));
- wsclient3.await(10);
assertEquals(broadcastMsg, wsclient3.getReceived(String.class).get(0));
- wsclient1.await(10);
+
+ wsclient1.sendTextMessage("private"); // this one should go to
wsclient1 only
+ await().atMost(10, TimeUnit.SECONDS)
+ .until(() -> wsclient1.getReceived().size() >= 1);
assertEquals("private", wsclient1.getReceived(String.class).get(0));
wsclient1.close();