Skip to content

Commit f31de8d

Browse files
authored
Java tests -- retry NettyAppServer start (#10170)
1 parent e893ce2 commit f31de8d

5 files changed

Lines changed: 110 additions & 51 deletions

File tree

.github/workflows/java.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,11 @@ jobs:
205205
- name: Start XVFB
206206
run: Xvfb :99 &
207207
- name: Run browser tests in Firefox
208-
uses: ./.github/actions/bazel-test
208+
uses: nick-invision/retry@v2
209209
with:
210-
query: attr(tags, rc, tests(//java/...))
210+
timeout_minutes: 20
211+
max_attempts: 3
212+
command: bazel query "attr(tags, rc, tests(//java/...))" | xargs bazel test
211213
env:
212214
DISPLAY: :99
213215

java/src/org/openqa/selenium/netty/server/NettyServer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public NettyServer start() {
166166
if (e instanceof BindException) {
167167
String errorMessage = String.format(
168168
"Could not bind to address or port is already in use. Host %s, Port %s", host, port);
169-
throw new UncheckedIOException(new IOException(errorMessage, e));
169+
throw new ServerBindException(errorMessage, (BindException) e);
170170
}
171171
throw e;
172172
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.openqa.selenium.netty.server;
19+
20+
import java.io.IOException;
21+
import java.io.UncheckedIOException;
22+
23+
public class ServerBindException extends UncheckedIOException {
24+
25+
public ServerBindException(String message, IOException cause) {
26+
super(message, cause);
27+
}
28+
29+
}

java/test/org/openqa/selenium/environment/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ java_library(
5656
"//java/src/org/openqa/selenium/remote/http",
5757
"//java/test/org/openqa/selenium/build",
5858
artifact("com.google.guava:guava"),
59+
artifact("net.jodah:failsafe"),
5960
artifact("org.assertj:assertj-core"),
6061
],
6162
)

java/test/org/openqa/selenium/environment/webserver/NettyAppServer.java

Lines changed: 75 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,16 @@
1717

1818
package org.openqa.selenium.environment.webserver;
1919

20+
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
21+
import static java.util.Collections.singletonMap;
22+
import static org.openqa.selenium.json.Json.JSON_UTF_8;
23+
import static org.openqa.selenium.remote.http.Contents.string;
24+
2025
import com.google.common.collect.ImmutableMap;
26+
27+
import net.jodah.failsafe.Failsafe;
28+
import net.jodah.failsafe.RetryPolicy;
29+
2130
import org.openqa.selenium.grid.config.CompoundConfig;
2231
import org.openqa.selenium.grid.config.Config;
2332
import org.openqa.selenium.grid.config.MapConfig;
@@ -28,6 +37,7 @@
2837
import org.openqa.selenium.io.TemporaryFilesystem;
2938
import org.openqa.selenium.net.PortProber;
3039
import org.openqa.selenium.netty.server.NettyServer;
40+
import org.openqa.selenium.netty.server.ServerBindException;
3141
import org.openqa.selenium.remote.http.Contents;
3242
import org.openqa.selenium.remote.http.HttpClient;
3343
import org.openqa.selenium.remote.http.HttpHandler;
@@ -40,21 +50,70 @@
4050
import java.io.UncheckedIOException;
4151
import java.net.MalformedURLException;
4252
import java.net.URL;
43-
44-
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
45-
import static java.util.Collections.singletonMap;
46-
import static org.openqa.selenium.json.Json.JSON_UTF_8;
47-
import static org.openqa.selenium.remote.http.Contents.string;
53+
import java.time.temporal.ChronoUnit;
54+
import java.util.logging.Level;
55+
import java.util.logging.Logger;
4856

4957
public class NettyAppServer implements AppServer {
5058

5159
private final static Config sslConfig = new MapConfig(
5260
singletonMap("server", singletonMap("https-self-signed", true)));
53-
54-
private final Server<?> server;
55-
private final Server<?> secure;
61+
private static final Logger LOG = Logger.getLogger(NettyAppServer.class.getName());
62+
private Server<?> server;
63+
private Server<?> secure;
64+
private final RetryPolicy<Object> retryPolicy = new RetryPolicy<>()
65+
.withMaxAttempts(5)
66+
.withDelay(100, 1000, ChronoUnit.MILLIS)
67+
.handle(ServerBindException.class)
68+
.onRetry(e -> {
69+
LOG.log(Level.WARNING, String.format("NettyAppServer retry #%s. ", e.getAttemptCount()));
70+
initValues();
71+
})
72+
.onRetriesExceeded(e -> LOG.log(Level.WARNING, "NettyAppServer start aborted."));
5673

5774
public NettyAppServer() {
75+
initValues();
76+
}
77+
78+
public NettyAppServer(HttpHandler handler) {
79+
this(
80+
createDefaultConfig(),
81+
Require.nonNull("Handler", handler));
82+
}
83+
84+
private NettyAppServer(Config config, HttpHandler handler) {
85+
Require.nonNull("Config", config);
86+
Require.nonNull("Handler", handler);
87+
88+
server = new NettyServer(new BaseServerOptions(new MemoizedConfig(config)), handler);
89+
secure = null;
90+
}
91+
92+
private static Config createDefaultConfig() {
93+
return new MemoizedConfig(new MapConfig(
94+
singletonMap("server", singletonMap("port", PortProber.findFreePort()))));
95+
}
96+
97+
public static void main(String[] args) {
98+
MemoizedConfig
99+
config =
100+
new MemoizedConfig(new MapConfig(singletonMap("server", singletonMap("port", 2310))));
101+
BaseServerOptions options = new BaseServerOptions(config);
102+
103+
HttpHandler handler = new HandlersForTests(
104+
options.getHostname().orElse("localhost"),
105+
options.getPort(),
106+
TemporaryFilesystem.getDefaultTmpFS().createTempDir("netty", "server").toPath());
107+
108+
NettyAppServer server = new NettyAppServer(
109+
config,
110+
handler);
111+
server.start();
112+
113+
System.out.printf("Server started. Root URL: %s%n", server.whereIs("/"));
114+
}
115+
116+
private void initValues() {
58117
Config config = createDefaultConfig();
59118
BaseServerOptions options = new BaseServerOptions(config);
60119

@@ -78,31 +137,16 @@ public NettyAppServer() {
78137
secure = new NettyServer(secureOptions, secureHandler);
79138
}
80139

81-
public NettyAppServer(HttpHandler handler) {
82-
this(
83-
createDefaultConfig(),
84-
Require.nonNull("Handler", handler));
85-
}
86-
87-
private NettyAppServer(Config config, HttpHandler handler) {
88-
Require.nonNull("Config", config);
89-
Require.nonNull("Handler", handler);
90-
91-
server = new NettyServer(new BaseServerOptions(new MemoizedConfig(config)), handler);
92-
secure = null;
93-
}
94-
95-
private static Config createDefaultConfig() {
96-
return new MemoizedConfig(new MapConfig(
97-
singletonMap("server", singletonMap("port", PortProber.findFreePort()))));
98-
}
99-
100140
@Override
101141
public void start() {
102-
server.start();
103-
if (secure != null) {
104-
secure.start();
105-
}
142+
Failsafe.with(retryPolicy).run(
143+
() -> {
144+
server.start();
145+
if (secure != null) {
146+
secure.start();
147+
}
148+
}
149+
);
106150
}
107151

108152
@Override
@@ -181,21 +225,4 @@ public String getHostName() {
181225
public String getAlternateHostName() {
182226
return AppServer.detectAlternateHostname();
183227
}
184-
185-
public static void main(String[] args) {
186-
MemoizedConfig config = new MemoizedConfig(new MapConfig(singletonMap("server", singletonMap("port", 2310))));
187-
BaseServerOptions options = new BaseServerOptions(config);
188-
189-
HttpHandler handler = new HandlersForTests(
190-
options.getHostname().orElse("localhost"),
191-
options.getPort(),
192-
TemporaryFilesystem.getDefaultTmpFS().createTempDir("netty", "server").toPath());
193-
194-
NettyAppServer server = new NettyAppServer(
195-
config,
196-
handler);
197-
server.start();
198-
199-
System.out.printf("Server started. Root URL: %s%n", server.whereIs("/"));
200-
}
201228
}

0 commit comments

Comments
 (0)