11package org .testcontainers .containers .wait .strategy ;
22
3+ import lombok .SneakyThrows ;
34import lombok .extern .slf4j .Slf4j ;
4- import org .rnorth .ducttape .TimeoutException ;
5- import org .rnorth .ducttape .unreliables .Unreliables ;
5+ import org .awaitility .Awaitility ;
66import org .testcontainers .containers .ContainerLaunchException ;
77import org .testcontainers .containers .wait .internal .ExternalPortListeningCheck ;
88import org .testcontainers .containers .wait .internal .InternalCommandPortListeningCheck ;
99
10+ import java .time .Duration ;
11+ import java .time .Instant ;
12+ import java .util .Arrays ;
1013import java .util .List ;
1114import java .util .Set ;
1215import java .util .concurrent .Callable ;
16+ import java .util .concurrent .CancellationException ;
17+ import java .util .concurrent .ExecutionException ;
18+ import java .util .concurrent .Future ;
1319import java .util .concurrent .TimeUnit ;
20+ import java .util .concurrent .TimeoutException ;
1421import java .util .stream .Collectors ;
1522
1623/**
2229public class HostPortWaitStrategy extends AbstractWaitStrategy {
2330
2431 @ Override
32+ @ SneakyThrows (InterruptedException .class )
2533 protected void waitUntilReady () {
2634 final Set <Integer > externalLivenessCheckPorts = getLivenessCheckPorts ();
2735 if (externalLivenessCheckPorts .isEmpty ()) {
@@ -31,7 +39,6 @@ protected void waitUntilReady() {
3139 return ;
3240 }
3341
34- @ SuppressWarnings ("unchecked" )
3542 List <Integer > exposedPorts = waitStrategyTarget .getExposedPorts ();
3643
3744 final Set <Integer > internalPorts = getInternalPorts (externalLivenessCheckPorts , exposedPorts );
@@ -41,10 +48,43 @@ protected void waitUntilReady() {
4148 Callable <Boolean > externalCheck = new ExternalPortListeningCheck (waitStrategyTarget , externalLivenessCheckPorts );
4249
4350 try {
44- Unreliables .retryUntilTrue ((int ) startupTimeout .getSeconds (), TimeUnit .SECONDS ,
45- () -> getRateLimiter ().getWhenReady (() -> internalCheck .call () && externalCheck .call ()));
51+ List <Future <Boolean >> futures = EXECUTOR .invokeAll (Arrays .asList (
52+ // Blocking
53+ () -> {
54+ Instant now = Instant .now ();
55+ Boolean result = internalCheck .call ();
56+ log .debug (
57+ "Internal port check {} for {} in {}" ,
58+ Boolean .TRUE .equals (result ) ? "passed" : "failed" ,
59+ internalPorts ,
60+ Duration .between (now , Instant .now ())
61+ );
62+ return result ;
63+ },
64+ // Polling
65+ () -> {
66+ Instant now = Instant .now ();
67+ Awaitility .await ()
68+ .pollInSameThread ()
69+ .pollInterval (Duration .ofMillis (100 ))
70+ .pollDelay (Duration .ZERO )
71+ .forever ()
72+ .until (externalCheck );
4673
47- } catch (TimeoutException e ) {
74+ log .debug (
75+ "External port check passed for {} mapped as {} in {}" ,
76+ internalPorts ,
77+ externalLivenessCheckPorts ,
78+ Duration .between (now , Instant .now ())
79+ );
80+ return true ;
81+ }
82+ ), startupTimeout .getSeconds (), TimeUnit .SECONDS );
83+
84+ for (Future <Boolean > future : futures ) {
85+ future .get (0 , TimeUnit .SECONDS );
86+ }
87+ } catch (CancellationException | ExecutionException | TimeoutException e ) {
4888 throw new ContainerLaunchException ("Timed out waiting for container port to open (" +
4989 waitStrategyTarget .getHost () +
5090 " ports: " +
0 commit comments