Skip to content

Commit 4e23e58

Browse files
committed
server: fix startup error + misleading error message on port in use
- server: Misleading error message in httpsOnly mode fix #3722 - server: make sure server stop on startup error (exit main thread) fix #3723
1 parent b1579a8 commit 4e23e58

File tree

7 files changed

+135
-40
lines changed

7 files changed

+135
-40
lines changed

jooby/src/main/java/io/jooby/Jooby.java

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,34 @@ public static void runApp(@NonNull String[] args, @NonNull Consumer<Jooby> consu
12411241
runApp(args, ExecutionMode.DEFAULT, consumerProvider(consumer));
12421242
}
12431243

1244+
/**
1245+
* Setup default environment, logging (logback or log4j2) and run application.
1246+
*
1247+
* @param args Application arguments.
1248+
* @param server Server to run.
1249+
* @param consumer Application consumer.
1250+
*/
1251+
public static void runApp(
1252+
@NonNull String[] args, @NonNull Server server, @NonNull Consumer<Jooby> consumer) {
1253+
runApp(args, server, ExecutionMode.DEFAULT, consumer);
1254+
}
1255+
1256+
/**
1257+
* Setup default environment, logging (logback or log4j2) and run application.
1258+
*
1259+
* @param args Application arguments.
1260+
* @param server Server to run.
1261+
* @param consumer Application consumer.
1262+
*/
1263+
public static void runApp(
1264+
@NonNull String[] args,
1265+
@NonNull Server server,
1266+
@NonNull ExecutionMode executionMode,
1267+
@NonNull Consumer<Jooby> consumer) {
1268+
configurePackage(consumer.getClass().getPackage());
1269+
runApp(args, server, executionMode, List.of(consumerProvider(consumer)));
1270+
}
1271+
12441272
/**
12451273
* Setup default environment, logging (logback or log4j2) and run application.
12461274
*
@@ -1319,28 +1347,44 @@ public static void runApp(
13191347
@NonNull Server server,
13201348
@NonNull ExecutionMode executionMode,
13211349
@NonNull List<Supplier<Jooby>> provider) {
1350+
13221351
/* Dump command line as system properties. */
13231352
parseArguments(args).forEach(System::setProperty);
13241353
var apps = new ArrayList<Jooby>();
13251354
var targetServer = server.getLoggerOff().isEmpty() ? server : MutedServer.mute(server);
1326-
for (var factory : provider) {
1327-
var app = createApp(executionMode, factory);
1328-
app.server = targetServer;
1329-
/*
1330-
When running a single app instance, there is no issue with server options, when multiple
1331-
apps set options a warning will be printed
1332-
*/
1333-
var options = app.serverOptions;
1334-
if (options == null) {
1335-
options = ServerOptions.from(app.getConfig()).orElse(null);
1355+
try {
1356+
for (var factory : provider) {
1357+
var app = createApp(executionMode, factory);
1358+
app.server = targetServer;
1359+
/*
1360+
When running a single app instance, there is no issue with server options, when multiple
1361+
apps set options a warning will be printed
1362+
*/
1363+
var options = app.serverOptions;
1364+
if (options == null) {
1365+
options = ServerOptions.from(app.getConfig()).orElse(null);
1366+
}
1367+
if (options != null) {
1368+
options.setServer(server.getName());
1369+
server.setOptions(options);
1370+
}
1371+
apps.add(app);
13361372
}
1337-
if (options != null) {
1338-
options.setServer(server.getName());
1339-
server.setOptions(options);
1373+
targetServer.start(apps.toArray(new Jooby[0]));
1374+
} catch (Throwable t) {
1375+
if (targetServer != null) {
1376+
try {
1377+
targetServer.stop();
1378+
} catch (Exception ignore) {
1379+
}
13401380
}
1341-
apps.add(app);
1381+
LoggerFactory.getLogger(Jooby.class)
1382+
.error("Application initialization resulted in exception", t);
1383+
1384+
throw t instanceof StartupException
1385+
? (StartupException) t
1386+
: new StartupException("Application initialization resulted in exception", t);
13421387
}
1343-
targetServer.start(apps.toArray(new Jooby[0]));
13441388
}
13451389

13461390
/**
@@ -1366,13 +1410,6 @@ public static Jooby createApp(
13661410
try {
13671411
BOOT_EXECUTION_MODE = executionMode;
13681412
app = provider.get();
1369-
} catch (Throwable t) {
1370-
LoggerFactory.getLogger(Jooby.class)
1371-
.error("Application initialization resulted in exception", t);
1372-
1373-
throw t instanceof StartupException
1374-
? (StartupException) t
1375-
: new StartupException("Application initialization resulted in exception", t);
13761413
} finally {
13771414
BOOT_EXECUTION_MODE = ExecutionMode.DEFAULT;
13781415
}

jooby/src/main/java/io/jooby/ServerOptions.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,12 @@ public int getPort() {
252252
}
253253

254254
/**
255-
* True when SSL is enabled. Either bc the secure port or SSL options are set.
255+
* True when SSL is enabled. Either bc the secure port, httpsOnly or SSL options are set.
256256
*
257-
* @return True when SSL is enabled. Either bc the secure port or SSL options are set.
257+
* @return True when SSL is enabled. Either bc the secure port, httpsOnly or SSL options are set.
258258
*/
259259
public boolean isSSLEnabled() {
260-
return securePort != null || ssl != null;
260+
return securePort != null || ssl != null || httpsOnly;
261261
}
262262

263263
/**

modules/jooby-jetty/src/main/java/io/jooby/jetty/JettyServer.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.typesafe.config.Config;
3232
import edu.umd.cs.findbugs.annotations.NonNull;
3333
import io.jooby.*;
34+
import io.jooby.exception.StartupException;
3435
import io.jooby.internal.jetty.JettyHandler;
3536
import io.jooby.internal.jetty.JettyHttpExpectAndContinueHandler;
3637
import io.jooby.internal.jetty.PrefixHandler;
@@ -94,6 +95,7 @@ public JettyServer configure(Consumer<HttpConfiguration> configurer) {
9495
public io.jooby.Server start(@NonNull Jooby... application) {
9596
// force options to be non-null
9697
var options = getOptions();
98+
var portInUse = options.getPort();
9799
try {
98100
this.applications = List.of(application);
99101
/* Set max request size attribute: */
@@ -187,13 +189,13 @@ public io.jooby.Server start(@NonNull Jooby... application) {
187189
acceptors,
188190
selectors,
189191
secureConnectionFactories.toArray(new ConnectionFactory[0]));
190-
secureConnector.setPort(options.getSecurePort());
192+
portInUse = options.getSecurePort();
193+
secureConnector.setPort(portInUse);
191194
secureConnector.setHost(options.getHost());
192195

193196
server.addConnector(secureConnector);
194197
} else if (options.isHttpsOnly()) {
195-
throw new IllegalArgumentException(
196-
"Server configured for httpsOnly, but ssl options not set");
198+
throw new StartupException("Server configured for httpsOnly, but ssl options not set");
197199
}
198200

199201
var context = new ContextHandler();
@@ -250,7 +252,7 @@ public io.jooby.Server start(@NonNull Jooby... application) {
250252
fireReady(applications);
251253
} catch (Exception x) {
252254
if (io.jooby.Server.isAddressInUse(x.getCause())) {
253-
x = new BindException("Address already in use: " + options.getPort());
255+
x = new BindException("Address already in use: " + portInUse);
254256
}
255257
throw SneakyThrows.propagate(x);
256258
}

modules/jooby-netty/src/main/java/io/jooby/netty/NettyServer.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.jooby.ServerOptions;
2323
import io.jooby.SneakyThrows;
2424
import io.jooby.SslOptions;
25+
import io.jooby.exception.StartupException;
2526
import io.jooby.internal.netty.*;
2627
import io.jooby.netty.buffer.NettyDataBufferFactory;
2728
import io.netty.bootstrap.ServerBootstrap;
@@ -109,6 +110,7 @@ public String getName() {
109110
public Server start(@NonNull Jooby... application) {
110111
// force options to be non-null
111112
var options = getOptions();
113+
var portInUse = options.getPort();
112114
try {
113115
this.applications = List.of(application);
114116
boolean single = applications.size() == 1;
@@ -158,10 +160,10 @@ public Server start(@NonNull Jooby... application) {
158160
var https =
159161
newBootstrap(
160162
allocator, transport, newPipeline(options, sslContext, dateService, http2));
161-
https.bind(options.getHost(), options.getSecurePort()).get();
163+
portInUse = options.getSecurePort();
164+
https.bind(options.getHost(), portInUse).get();
162165
} else if (options.isHttpsOnly()) {
163-
throw new IllegalArgumentException(
164-
"Server configured for httpsOnly, but ssl options not set");
166+
throw new StartupException("Server configured for httpsOnly, but ssl options not set");
165167
}
166168

167169
fireReady(applications);
@@ -170,7 +172,7 @@ public Server start(@NonNull Jooby... application) {
170172
} catch (ExecutionException x) {
171173
var cause = x.getCause();
172174
if (Server.isAddressInUse(cause)) {
173-
cause = new BindException("Address already in use: " + options.getPort());
175+
cause = new BindException("Address already in use: " + portInUse);
174176
}
175177
throw SneakyThrows.propagate(cause);
176178
}

modules/jooby-undertow/src/main/java/io/jooby/undertow/UndertowServer.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public String getName() {
7373
public @NonNull Server start(@NonNull Jooby... application) {
7474
// force options to be non-null
7575
var options = getOptions();
76+
var portInUse = options.getPort();
7677
try {
7778
this.applications = List.of(application);
7879

@@ -138,7 +139,8 @@ public String getName() {
138139
var classLoader = this.applications.get(0).getClassLoader();
139140
SSLContext sslContext = options.getSSLContext(classLoader);
140141
if (sslContext != null) {
141-
builder.addHttpsListener(options.getSecurePort(), options.getHost(), sslContext);
142+
portInUse = options.getSecurePort();
143+
builder.addHttpsListener(portInUse, options.getHost(), sslContext);
142144
SslOptions ssl = options.getSsl();
143145
builder.setSocketOption(Options.SSL_ENABLED_PROTOCOLS, Sequence.of(ssl.getProtocol()));
144146
Optional.ofNullable(options.getSsl())
@@ -160,7 +162,7 @@ public String getName() {
160162
Throwable sourceException = x;
161163
Throwable cause = Optional.ofNullable(x.getCause()).orElse(x);
162164
if (Server.isAddressInUse(cause)) {
163-
sourceException = new BindException("Address already in use: " + options.getPort());
165+
sourceException = new BindException("Address already in use: " + portInUse);
164166
}
165167
throw SneakyThrows.propagate(sourceException);
166168
}
@@ -194,13 +196,13 @@ public synchronized Server stop() {
194196
}
195197

196198
private void shutdownServer() {
197-
if (server != null) {
198-
try {
199+
try {
200+
if (server != null) {
199201
server.stop();
200-
} finally {
201-
shutdownWorker();
202-
server = null;
203202
}
203+
} finally {
204+
shutdownWorker();
205+
server = null;
204206
}
205207
}
206208

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package examples;
7+
8+
import static io.jooby.Jooby.runApp;
9+
10+
import io.jooby.Context;
11+
import io.jooby.ServerOptions;
12+
import io.jooby.jetty.JettyServer;
13+
14+
public class PortInUse {
15+
public static void main(String[] args) {
16+
var options = new ServerOptions();
17+
// options.setHttpsOnly(true);
18+
runApp(
19+
args,
20+
new JettyServer().setOptions(options),
21+
app -> {
22+
app.get("/", Context::getRequestPath);
23+
});
24+
System.out.println("Exited");
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package examples;
7+
8+
import static io.jooby.Jooby.runApp;
9+
10+
import io.jooby.Context;
11+
import io.jooby.ServerOptions;
12+
import io.jooby.jetty.JettyServer;
13+
14+
public class PortInUse2 {
15+
public static void main(String[] args) {
16+
var options = new ServerOptions();
17+
// options.setHttpsOnly(true);
18+
runApp(
19+
args,
20+
new JettyServer().setOptions(options),
21+
app -> {
22+
app.get("/", Context::getRequestPath);
23+
});
24+
System.out.println("Exited");
25+
}
26+
}

0 commit comments

Comments
 (0)