Skip to content

Commit d62136f

Browse files
committed
Improved port checking, reduced logging
Signed-off-by: David Matějček <david.matejcek@omnifish.ee>
1 parent 2f2ceac commit d62136f

File tree

12 files changed

+252
-63
lines changed

12 files changed

+252
-63
lines changed

appserver/orb/orb-iiop/src/main/java/org/glassfish/enterprise/iiop/impl/IIOPSSLSocketFactory.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,6 @@ public ServerSocket createServerSocket(String type, InetSocketAddress inetSocket
265265
} else {
266266
serverSocket = new ServerSocket();
267267
}
268-
serverSocket.setReuseAddress(true);
269268
checkPort(inetSocketAddress);
270269
serverSocket.bind(inetSocketAddress);
271270
return serverSocket;
@@ -367,7 +366,6 @@ private ServerSocket createSSLServerSocket(String type, InetSocketAddress inetSo
367366
// by the ssf implementation when only the port is specified
368367
checkPort(inetSocketAddress);
369368
ss = ssf.createServerSocket(port, BACKLOG, inetSocketAddress.getAddress());
370-
ss.setReuseAddress(true);
371369
if (ciphers != null) {
372370
((SSLServerSocket) ss).setEnabledCipherSuites(ciphers);
373371
}
@@ -398,8 +396,11 @@ private static void checkPort(InetSocketAddress address) {
398396
if (port < 1) {
399397
return;
400398
}
401-
HostAndPort endpoint = new HostAndPort(address.getHostString(), port, false);
402-
ProcessUtils.waitFor(() -> !ProcessUtils.isListening(endpoint), Duration.ofSeconds(10L), true);
399+
final HostAndPort endpoint = new HostAndPort(address.getHostString(), port, false);
400+
if (!ProcessUtils.isListening(endpoint)) {
401+
return;
402+
}
403+
ProcessUtils.waitWhileListening(endpoint, Duration.ofSeconds(10L), false);
403404
}
404405

405406
/**

nucleus/admin/launcher/src/main/java/com/sun/enterprise/admin/launcher/GFLauncher.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,8 @@
2121
import com.sun.enterprise.universal.glassfish.GFLauncherUtils;
2222
import com.sun.enterprise.universal.glassfish.TokenResolver;
2323
import com.sun.enterprise.universal.process.ProcessStreamDrainer;
24-
import com.sun.enterprise.universal.process.ProcessUtils;
2524
import com.sun.enterprise.universal.xml.MiniXmlParser;
2625
import com.sun.enterprise.universal.xml.MiniXmlParserException;
27-
import com.sun.enterprise.util.HostAndPort;
2826
import com.sun.enterprise.util.io.FileUtils;
2927

3028
import java.io.BufferedWriter;
@@ -206,12 +204,6 @@ public abstract class GFLauncher {
206204
*/
207205
public final void launch() throws GFLauncherException {
208206
if (debugPort != null) {
209-
// As the debugger starts immediately with the new process,
210-
// and especially if there is some problem with shutdown, we can escalate it.
211-
if (ProcessUtils.isListening(new HostAndPort("localhost", debugPort, false))) {
212-
throw new GFLauncherException(
213-
"The debug port " + debugPort + " is already occupied by another process.");
214-
}
215207
if (debugSuspend) {
216208
LOG.log(INFO,
217209
() -> "Debugging will be available and the server will start suspended on port " + debugPort + ".");

nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/LocalServerCommand.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,10 @@
4949
import static com.sun.enterprise.admin.cli.CLIConstants.DEFAULT_HOSTNAME;
5050
import static com.sun.enterprise.admin.cli.ProgramOptions.PasswordLocation.LOCAL_PASSWORD;
5151
import static com.sun.enterprise.admin.servermgmt.cli.ServerLifeSignChecker.step;
52-
import static com.sun.enterprise.universal.process.ProcessUtils.isListening;
5352
import static com.sun.enterprise.universal.process.ProcessUtils.loadPid;
54-
import static com.sun.enterprise.universal.process.ProcessUtils.waitFor;
5553
import static com.sun.enterprise.universal.process.ProcessUtils.waitForNewPid;
5654
import static com.sun.enterprise.universal.process.ProcessUtils.waitWhileIsAlive;
55+
import static com.sun.enterprise.universal.process.ProcessUtils.waitWhileListening;
5756
import static com.sun.enterprise.util.SystemPropertyConstants.KEYSTORE_PASSWORD_DEFAULT;
5857
import static com.sun.enterprise.util.SystemPropertyConstants.MASTER_PASSWORD_ALIAS;
5958
import static com.sun.enterprise.util.SystemPropertyConstants.MASTER_PASSWORD_FILENAME;
@@ -373,9 +372,7 @@ protected final void waitForStop(final Long pid, final HostAndPort adminAddress,
373372
if (adminAddress == null) {
374373
return;
375374
}
376-
LOG.log(INFO, "Waiting until admin endpoint {0} is free.", adminAddress);
377-
final boolean portIsFree = waitFor(() -> !isListening(adminAddress), portTimeout, printDots);
378-
LOG.log(INFO, "Admin port is {0}.", portIsFree ? "free" : "blocked");
375+
final boolean portIsFree = waitWhileListening(adminAddress, portTimeout, printDots);
379376
if (portIsFree) {
380377
return;
381378
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (c) 2025 Contributors to the Eclipse Foundation.
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License v. 2.0, which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* This Source Code may also be made available under the following Secondary
9+
* Licenses when the conditions for such availability set forth in the
10+
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11+
* version 2 with the GNU Classpath Exception, which is available at
12+
* https://www.gnu.org/software/classpath/license.html.
13+
*
14+
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
*/
16+
17+
package com.sun.enterprise.admin.servermgmt.cli;
18+
19+
import com.sun.enterprise.universal.process.ProcessUtils;
20+
import com.sun.enterprise.util.HostAndPort;
21+
22+
import java.lang.System.Logger;
23+
import java.time.Duration;
24+
import java.util.Objects;
25+
import java.util.concurrent.CompletableFuture;
26+
import java.util.concurrent.ExecutionException;
27+
import java.util.concurrent.TimeUnit;
28+
import java.util.concurrent.TimeoutException;
29+
import java.util.function.Supplier;
30+
31+
import static java.lang.System.Logger.Level.TRACE;
32+
33+
34+
/**
35+
* Use case:
36+
* <ol>
37+
* <li>We have a running server listening on some endpoint
38+
* <li>We will watch it until it closes the endpoint.
39+
* <li>In parallel to the watching we ask the server to stop.
40+
* </ol>
41+
* Why - if we would start watching after the command, endpoint could be already in some state
42+
* when it would refuse new connections, but it would also refuse to rebind on the server side
43+
* of the new process. So we need to be sure the endpoint stopped listening before we start
44+
* binding again.
45+
*/
46+
public class PortWatcher {
47+
private static final Logger LOG = System.getLogger(PortWatcher.class.getName());
48+
49+
private final CompletableFuture<Boolean> job;
50+
51+
private PortWatcher(Supplier<Boolean> supplier) {
52+
this.job = CompletableFuture.supplyAsync(supplier);
53+
// We should be always sure we do listen to the old process and not to the new one.
54+
// If it starts to be flaky, replace with sleep.
55+
Thread.onSpinWait();
56+
}
57+
58+
/**
59+
* Blocks until we have the positive answer or endpoint is still listening after timeout.
60+
*
61+
* @param timeout can be null, then we may wait forever.
62+
* @return true if the endpoint disconnected before timeout, false if it timed out.
63+
*/
64+
public boolean get(Duration timeout) {
65+
LOG.log(TRACE, "get(timeout={0})", timeout);
66+
try {
67+
if (timeout == null) {
68+
return job.get();
69+
}
70+
return job.get(timeout.toMillis(), TimeUnit.MILLISECONDS);
71+
} catch (InterruptedException | ExecutionException | TimeoutException e) {
72+
job.cancel(true);
73+
return false;
74+
}
75+
}
76+
77+
/**
78+
* Watch the endpoint in a separate thread.
79+
*
80+
* @param endpoint
81+
* @param printDots
82+
* @return the {@link PortWatcher}
83+
*/
84+
public static PortWatcher watch(HostAndPort endpoint, boolean printDots) {
85+
Objects.requireNonNull(endpoint, "endpoint");
86+
return new PortWatcher(() -> ProcessUtils.waitWhileListening(endpoint, null, printDots));
87+
}
88+
}

nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/RestartDomainCommand.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.glassfish.api.admin.CommandException;
3333
import org.glassfish.hk2.api.PerLookup;
3434
import org.glassfish.hk2.api.ServiceLocator;
35-
import org.glassfish.main.jdke.i18n.LocalStringsImpl;
3635
import org.jvnet.hk2.annotations.Service;
3736

3837
import static com.sun.enterprise.admin.cli.CLIConstants.DEATH_TIMEOUT_MS;
@@ -58,7 +57,6 @@
5857
@Service(name = "restart-domain")
5958
@PerLookup
6059
public class RestartDomainCommand extends StopDomainCommand {
61-
private static final LocalStringsImpl I18N = new LocalStringsImpl(RestartDomainCommand.class);
6260

6361
@Param(name = "debug", optional = true)
6462
private Boolean debug;
@@ -97,7 +95,8 @@ protected void doCommand() throws CommandException {
9795
// oldPid is received from the running server.
9896
final Long oldPid = getServerPid();
9997
final HostAndPort oldAdminAddress = getReachableAdminAddress();
100-
final HostAndPort newAdminEndpoint = getAdminAddress("server");
98+
final boolean printDots = !programOpts.isTerse();
99+
final PortWatcher portWatcher = oldAdminAddress == null ? null : PortWatcher.watch(oldAdminAddress, printDots);
101100
final RemoteCLICommand cmd = new RemoteCLICommand("restart-domain", programOpts, env);
102101
if (debug == null) {
103102
cmd.executeAndReturnOutput("restart-domain");
@@ -106,8 +105,16 @@ protected void doCommand() throws CommandException {
106105
}
107106

108107
final Duration timeout = getRestartTimeout();
109-
final Duration startTimeout = step(null, timeout,
110-
() -> waitForStop(isLocal() ? oldPid : null, oldAdminAddress, timeout));
108+
final Duration startTimeout;
109+
if (isLocal()) {
110+
startTimeout = step(null, timeout, () -> waitForStop(oldPid, null, timeout));
111+
} else {
112+
startTimeout = timeout;
113+
}
114+
115+
if (portWatcher != null && !portWatcher.get(startTimeout)) {
116+
logger.warning("The endpoint is still listening after timeout: " + oldAdminAddress);
117+
}
111118

112119
final List<HostAndPort> userEndpoints = parseCustomEndpoints(customEndpoints);
113120
final ServerLifeSignCheck lifeSignCheck = new ServerLifeSignCheck("domain " + getDomainName(),
@@ -117,6 +124,7 @@ protected void doCommand() throws CommandException {
117124
logger.info(report);
118125
}
119126

127+
120128
/**
121129
* If the server isn't running, try to start it.
122130
*/

nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/ServerLifeSignChecker.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
import org.glassfish.api.admin.CommandException;
3131

32-
import static java.lang.System.Logger.Level.INFO;
32+
import static java.lang.System.Logger.Level.DEBUG;
3333

3434
public class ServerLifeSignChecker {
3535
private static final Logger LOG = System.getLogger(ServerLifeSignChecker.class.getName());
@@ -223,7 +223,7 @@ public static Duration step(String message, Duration timeout, Action action) thr
223223
return timeout;
224224
}
225225
if (message != null) {
226-
LOG.log(INFO, message);
226+
LOG.log(DEBUG, message);
227227
}
228228
Instant start = Instant.now();
229229
action.action();

nucleus/admin/server-mgmt/src/main/java/com/sun/enterprise/admin/servermgmt/cli/StartServerHelper.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,6 @@
5151
import static com.sun.enterprise.admin.cli.CLIConstants.RESTART_DEBUG_ON;
5252
import static com.sun.enterprise.admin.cli.CLIConstants.RESTART_NORMAL;
5353
import static com.sun.enterprise.admin.cli.CLIConstants.WALL_CLOCK_START_PROP;
54-
import static com.sun.enterprise.universal.process.ProcessUtils.isListening;
55-
import static com.sun.enterprise.universal.process.ProcessUtils.waitFor;
5654
import static java.lang.System.Logger.Level.DEBUG;
5755
import static java.lang.System.Logger.Level.INFO;
5856
import static org.glassfish.main.jdke.props.SystemProperties.setProperty;
@@ -94,14 +92,8 @@ public StartServerHelper(boolean terse, Duration timeout, ServerDirs serverDirs,
9492
if (launcher.getPidBeforeRestart() != null) {
9593
waitForParentToDie(launcher.getPidBeforeRestart(), timeout);
9694
configureLoggingOfRestart(serverDirs.getRestartLogFile());
97-
final Integer debugPort = launcher.getDebugPort();
98-
if (debugPort != null) {
99-
LOG.log(INFO, "Waiting few seconds until debug port {0} is free.", debugPort);
100-
final HostAndPort debugEndpoint = new HostAndPort("localhost", debugPort, false);
101-
final boolean portIsFree = waitFor(() -> !isListening(debugEndpoint), timeout, terse);
102-
LOG.log(INFO, "Debug port is {0}.", portIsFree ? "free" : "blocked");
103-
}
10495
}
96+
checkFreeDebugPort(launcher.getDebugPort(), Duration.ofSeconds(10L), terse);
10597
checkFreeAdminPorts(info.getAdminAddresses());
10698
deletePidFile();
10799
}
@@ -205,7 +197,7 @@ private void configureLoggingOfRestart(File logFile) {
205197
*
206198
* @throws CommandException if we timeout waiting for the parent to die or if the admin ports never free up
207199
*/
208-
private void waitForParentToDie(long pid, Duration timeout) throws GFLauncherException {
200+
private void waitForParentToDie(Long pid, Duration timeout) throws GFLauncherException {
209201
LOG.log(INFO, () -> "Waiting for death of the parent process with the pid " + pid);
210202
if (!ProcessUtils.waitWhileIsAlive(pid, timeout, false)) {
211203
throw new GFLauncherException("Waited " + timeout.toSeconds()
@@ -280,8 +272,23 @@ public static List<HostAndPort> parseCustomEndpoints(String customEndpoints) thr
280272
return endpoints;
281273
}
282274

275+
/**
276+
* Fast respawn can meet with previous JVM on ports, despite the JVM is already dead.
277+
* So we have to wait a bit.
278+
*/
279+
private static void checkFreeDebugPort(Integer debugPort, Duration timeout, boolean terse) {
280+
if (debugPort == null) {
281+
return;
282+
}
283+
final HostAndPort debugEndpoint = new HostAndPort("localhost", debugPort, false);
284+
if (!ProcessUtils.isListening(debugEndpoint)) {
285+
return;
286+
}
287+
ProcessUtils.waitWhileListening(debugEndpoint, timeout, !terse);
288+
}
289+
283290
private static void checkFreeAdminPorts(List<HostAndPort> endpoints) throws GFLauncherException {
284-
LOG.log(INFO, "Checking if all admin ports are free.");
291+
LOG.log(DEBUG, "Checking if all admin ports are free.");
285292
for (HostAndPort endpoint : endpoints) {
286293
if (!NetUtils.isPortFree(endpoint.getHost(), endpoint.getPort())) {
287294
throw new GFLauncherException("There is a process already using the admin port " + endpoint.getPort()

nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/ChangeNodeMasterPasswordCommand.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,8 @@ protected int executeCommand() throws CommandException {
8383

8484
ArrayList<String> serverNames = getInstanceDirs(serverDir);
8585
for (String serverName: serverNames) {
86-
if (isRunning(serverDir, serverName)) {
87-
throw new CommandException(strings.get("instance.is.running",
88-
serverName));
86+
if (isRunning(serverName)) {
87+
throw new CommandException(strings.get("instance.is.running", serverName));
8988
}
9089
}
9190

@@ -213,7 +212,7 @@ private ArrayList<String> getInstanceDirs(File parent) throws CommandException {
213212
}
214213

215214

216-
private boolean isRunning(File nodeDirChild, String serverName) throws CommandException {
215+
private boolean isRunning(String serverName) throws CommandException {
217216
File serverDir = new File(nodeDirChild, serverName);
218217
File configDir = new File(serverDir, "config");
219218
File domainXml = new File(configDir, "domain.xml");

nucleus/cluster/cli/src/main/java/com/sun/enterprise/admin/cli/cluster/RestartLocalInstanceCommand.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.sun.enterprise.admin.cli.CLICommand;
2121
import com.sun.enterprise.admin.cli.remote.RemoteCLICommand;
22+
import com.sun.enterprise.admin.servermgmt.cli.PortWatcher;
2223
import com.sun.enterprise.admin.servermgmt.cli.ServerLifeSignCheck;
2324
import com.sun.enterprise.util.HostAndPort;
2425

@@ -63,18 +64,26 @@ protected final void doCommand() throws CommandException {
6364
// Save old values before executing restart
6465
final Long oldPid = getServerPid();
6566
final HostAndPort oldAdminAddress = getReachableAdminAddress();
66-
67-
// run the remote restart-instance command and throw away the output
68-
RemoteCLICommand cmd = new RemoteCLICommand("_restart-instance", programOpts, env);
67+
final boolean printDots = !programOpts.isTerse();
68+
final PortWatcher portWatcher = oldAdminAddress == null ? null : PortWatcher.watch(oldAdminAddress, printDots);
69+
final RemoteCLICommand cmd = new RemoteCLICommand("_restart-instance", programOpts, env);
6970
if (debug == null) {
7071
cmd.executeAndReturnOutput("_restart-instance");
7172
} else {
7273
cmd.executeAndReturnOutput("_restart-instance", "--debug", debug.toString());
7374
}
7475

7576
final Duration timeout = getRestartTimeout();
76-
final Duration startTimeout = step("Waiting until instance stops.", timeout,
77-
() -> waitForStop(oldPid, oldAdminAddress, timeout));
77+
final Duration startTimeout;
78+
if (isLocal()) {
79+
startTimeout = step(null, timeout, () -> waitForStop(oldPid, null, timeout));
80+
} else {
81+
startTimeout = timeout;
82+
}
83+
84+
if (portWatcher != null && !portWatcher.get(startTimeout)) {
85+
logger.warning("The endpoint is still listening after timeout: " + oldAdminAddress);
86+
}
7887

7988
final ServerLifeSignCheck lifeSignCheck = new ServerLifeSignCheck("instance " + getInstanceName(), true, true, true, true, List.of());
8089
final String report = waitForStart(oldPid, lifeSignCheck, () -> List.of(getReachableAdminAddress()), startTimeout);

0 commit comments

Comments
 (0)