Skip to content

Commit 0ca605e

Browse files
committed
[61] Add an exitValue() and waitForTermination() method which both return an int for the exit code. This will use the process' exit if one was set. Otherwise, an unknown exit code of -1 is returned.
Signed-off-by: James R. Perkins <[email protected]>
1 parent 1afce2a commit 0ca605e

File tree

6 files changed

+200
-41
lines changed

6 files changed

+200
-41
lines changed

src/main/java/org/wildfly/plugin/tools/server/AbstractServerManager.java

+57-22
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,18 @@
3131
abstract class AbstractServerManager<T extends ModelControllerClient> implements ServerManager {
3232
private static final Logger LOGGER = Logger.getLogger(AbstractServerManager.class);
3333

34-
protected final ProcessHandle process;
34+
protected final ProcessHandle processHandle;
35+
private final Process process;
3536
final T client;
3637
private final boolean shutdownOnClose;
3738
private final DeploymentManager deploymentManager;
3839
private final AtomicBoolean closed;
40+
private volatile Thread shutdownWaitThread;
3941

40-
protected AbstractServerManager(final ProcessHandle process, final T client,
42+
protected AbstractServerManager(final Process process, final ProcessHandle processHandle, final T client,
4143
final boolean shutdownOnClose) {
4244
this.process = process;
45+
this.processHandle = processHandle;
4346
this.client = client;
4447
this.shutdownOnClose = shutdownOnClose;
4548
deploymentManager = DeploymentManager.create(client);
@@ -75,10 +78,10 @@ public String launchType() {
7578
@Override
7679
public CompletableFuture<ServerManager> kill() {
7780
final CompletableFuture<ServerManager> cf = new CompletableFuture<>();
78-
if (process != null && process.isAlive()) {
81+
if (processHandle != null && processHandle.isAlive()) {
7982
internalClose(false, false);
80-
if (process.destroyForcibly()) {
81-
cf.thenCombine(process.onExit(), (serverManager, processHandle) -> serverManager);
83+
if (processHandle.destroyForcibly()) {
84+
cf.thenCombine(processHandle.onExit(), (serverManager, processHandle) -> serverManager);
8285
}
8386
} else {
8487
cf.complete(this);
@@ -96,16 +99,16 @@ public boolean waitFor(final long startupTimeout, final TimeUnit unit) throws In
9699
break;
97100
}
98101
timeout -= (System.currentTimeMillis() - before);
99-
if (process != null && !process.isAlive()) {
102+
if (processHandle != null && !processHandle.isAlive()) {
100103
throw new RuntimeException(
101-
String.format("The process %d is no longer active.", process.pid()));
104+
String.format("The process %d is no longer active.", processHandle.pid()));
102105
}
103106
TimeUnit.MILLISECONDS.sleep(sleep);
104107
timeout -= sleep;
105108
}
106109
if (timeout <= 0) {
107-
if (process != null) {
108-
process.destroy();
110+
if (processHandle != null) {
111+
processHandle.destroy();
109112
}
110113
return false;
111114
}
@@ -162,7 +165,7 @@ public void shutdown(final long timeout) throws IOException {
162165
public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
163166
checkState();
164167
final ServerManager serverManager = this;
165-
if (process != null) {
168+
if (processHandle != null) {
166169
return CompletableFuture.supplyAsync(() -> {
167170
try {
168171
internalShutdown(client, timeout);
@@ -171,17 +174,17 @@ public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
171174
}
172175
return null;
173176
})
174-
.thenCombine(process.onExit(), (outcome, processHandle) -> null)
177+
.thenCombine(processHandle.onExit(), (outcome, processHandle) -> null)
175178
.handle((ignore, error) -> {
176-
if (error != null && process.isAlive()) {
177-
if (process.destroyForcibly()) {
179+
if (error != null && processHandle.isAlive()) {
180+
if (processHandle.destroyForcibly()) {
178181
LOGGER.warnf(error,
179182
"Failed to shutdown the server. An attempt to destroy the process %d has been made, but it may still temporarily run in the background.",
180-
process.pid());
183+
processHandle.pid());
181184
} else {
182185
LOGGER.warnf(error,
183186
"Failed to shutdown server and destroy the process %d. The server may still be running in a process.",
184-
process.pid());
187+
processHandle.pid());
185188
}
186189
}
187190
return serverManager;
@@ -203,6 +206,29 @@ public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
203206
});
204207
}
205208

209+
@Override
210+
public int waitForTermination() throws InterruptedException {
211+
checkState();
212+
shutdownWaitThread = Thread.currentThread();
213+
try {
214+
if (process != null) {
215+
return process.waitFor();
216+
}
217+
waitForShutdown(client);
218+
return UNKNOWN_EXIT_STATUS;
219+
} finally {
220+
shutdownWaitThread = null;
221+
}
222+
}
223+
224+
@Override
225+
public int exitValue() throws IllegalStateException {
226+
if (process != null) {
227+
return process.exitValue();
228+
}
229+
return UNKNOWN_EXIT_STATUS;
230+
}
231+
206232
@Override
207233
public boolean isClosed() {
208234
return closed.get();
@@ -232,26 +258,35 @@ void internalClose(final boolean shutdownOnClose, final boolean waitForShutdown)
232258
}
233259
}
234260
try {
235-
client.close();
236-
} catch (IOException e) {
237-
LOGGER.error("Failed to close the client.", e);
261+
// Interrupt the shutdown thread if it's still running
262+
final Thread shutdownThread = shutdownWaitThread;
263+
if (shutdownThread != null) {
264+
LOGGER.debugf("Interrupting shutdown thread %s.", shutdownThread.getName());
265+
shutdownThread.interrupt();
266+
}
267+
} finally {
268+
try {
269+
client.close();
270+
} catch (IOException e) {
271+
LOGGER.error("Failed to close the client.", e);
272+
}
238273
}
239274
}
240275
}
241276

242277
abstract void internalShutdown(ModelControllerClient client, long timeout) throws IOException;
243278

244279
private void waitForShutdown(final ModelControllerClient client) {
245-
if (process != null) {
280+
if (processHandle != null) {
246281
try {
247-
process.onExit()
282+
processHandle.onExit()
248283
.get();
249284
} catch (InterruptedException | ExecutionException e) {
250-
throw new RuntimeException(String.format("Error waiting for process %d to exit.", process.pid()), e);
285+
throw new RuntimeException(String.format("Error waiting for process %d to exit.", processHandle.pid()), e);
251286
}
252287
} else {
253288
// Wait for the server manager to finish shutting down
254-
while (ServerManager.isRunning(client)) {
289+
while (!closed.get() && ServerManager.isRunning(client)) {
255290
Thread.onSpinWait();
256291
}
257292
}

src/main/java/org/wildfly/plugin/tools/server/DomainManager.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
public class DomainManager extends AbstractServerManager<DomainClient> {
3030
private static final Logger LOGGER = Logger.getLogger(DomainManager.class);
3131

32-
DomainManager(final ProcessHandle process, final DomainClient client,
32+
DomainManager(final Process process, final ProcessHandle processHandle, final DomainClient client,
3333
final boolean shutdownOnClose) {
34-
super(process, client, shutdownOnClose);
34+
super(process, processHandle, client, shutdownOnClose);
3535
}
3636

3737
@Override
@@ -67,8 +67,8 @@ public ModelNode determineHostAddress() throws OperationExecutionException, IOEx
6767
*/
6868
@Override
6969
public boolean isRunning() {
70-
if (process != null) {
71-
return process.isAlive() && CommonOperations.isDomainRunning(client(), false);
70+
if (processHandle != null) {
71+
return processHandle.isAlive() && CommonOperations.isDomainRunning(client(), false);
7272
}
7373
return CommonOperations.isDomainRunning(client(), false);
7474
}

src/main/java/org/wildfly/plugin/tools/server/ServerManager.java

+51-8
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@
3636
*/
3737
@SuppressWarnings("unused")
3838
public interface ServerManager extends AutoCloseable {
39+
int UNKNOWN_EXIT_STATUS = -1;
3940

4041
/**
4142
* A builder used to build a {@link ServerManager}.
4243
*/
4344
class Builder {
4445
private final Configuration<?> configuration;
45-
private ProcessHandle process;
46+
private Process process;
47+
private ProcessHandle processHandle;
4648

4749
public Builder() {
4850
this(new StandaloneConfiguration(null));
@@ -75,7 +77,7 @@ public Builder client(final ModelControllerClient client) {
7577
* @return this builder
7678
*/
7779
public Builder process(final ProcessHandle process) {
78-
this.process = process;
80+
this.processHandle = process;
7981
return this;
8082
}
8183

@@ -90,7 +92,8 @@ public Builder process(final ProcessHandle process) {
9092
* @see #process(ProcessHandle)
9193
*/
9294
public Builder process(final Process process) {
93-
this.process = process == null ? null : process.toHandle();
95+
this.process = process;
96+
this.processHandle = process == null ? null : process.toHandle();
9497
return this;
9598
}
9699

@@ -141,7 +144,7 @@ public Builder shutdownOnClose(final boolean shutdownOnClose) {
141144
* @return a new {@link StandaloneManager}
142145
*/
143146
public StandaloneManager standalone() {
144-
return new StandaloneManager(process, configuration.client(), configuration.shutdownOnClose());
147+
return new StandaloneManager(process, processHandle, configuration.client(), configuration.shutdownOnClose());
145148
}
146149

147150
/**
@@ -152,7 +155,7 @@ public StandaloneManager standalone() {
152155
* @return a new {@link DomainManager}
153156
*/
154157
public DomainManager domain() {
155-
return new DomainManager(process, getOrCreateDomainClient(), configuration.shutdownOnClose());
158+
return new DomainManager(process, processHandle, getOrCreateDomainClient(), configuration.shutdownOnClose());
156159
}
157160

158161
/**
@@ -173,7 +176,8 @@ public DomainManager domain() {
173176
public CompletableFuture<ServerManager> build() {
174177
@SuppressWarnings("resource")
175178
final ModelControllerClient client = configuration.client();
176-
final ProcessHandle process = this.process;
179+
final Process process = this.process;
180+
final ProcessHandle processHandle = this.processHandle;
177181
return CompletableFuture.supplyAsync(() -> {
178182
// Wait until the server is running, then determine what type we need to return
179183
while (!isRunning(client)) {
@@ -182,9 +186,10 @@ public CompletableFuture<ServerManager> build() {
182186
final String launchType = launchType(client).orElseThrow(() -> new IllegalStateException(
183187
"Could not determine the type of the server. Verify the server is running."));
184188
if ("STANDALONE".equals(launchType)) {
185-
return new StandaloneManager(process, client, configuration.shutdownOnClose());
189+
return new StandaloneManager(process, processHandle, client, configuration.shutdownOnClose());
186190
} else if ("DOMAIN".equals(launchType)) {
187-
return new DomainManager(process, getOrCreateDomainClient(), configuration.shutdownOnClose());
191+
return new DomainManager(process, processHandle, getOrCreateDomainClient(),
192+
configuration.shutdownOnClose());
188193
}
189194
throw new IllegalStateException(
190195
String.format("Only standalone and domain servers are support. %s is not supported.", launchType));
@@ -558,6 +563,44 @@ default CompletableFuture<ServerManager> shutdownAsync(long timeout) {
558563
});
559564
}
560565

566+
/**
567+
* Waits for the server to be shutdown and the process, if defined, to terminate. This returns the exit status of
568+
* the process if it was defined. Otherwise, {@link #UNKNOWN_EXIT_STATUS -1} will be returned.
569+
* <p>
570+
* Note this is a blocking action and will block the current thread until the server has been exited or this server
571+
* manager has been {@linkplain #close() closed}. If this server manager has been closed, the thread waiting for
572+
* terminate will be interrupted.
573+
* </p>
574+
*
575+
* @return the exit status of the process, if defined. If not defined a value of {@code -1} will be returned
576+
*
577+
* @throws InterruptedException if the current thread is interrupted while waiting for the server to exit
578+
* @since 1.2
579+
*/
580+
default int waitForTermination() throws InterruptedException {
581+
while (!isClosed() && isRunning()) {
582+
Thread.onSpinWait();
583+
}
584+
return UNKNOWN_EXIT_STATUS;
585+
}
586+
587+
/**
588+
* Returns the exit status of the process if it's defined. If the {@link Builder#process(Process)} was used and the
589+
* process has not yet terminated, an {@link IllegalStateException} will be thrown.
590+
* <p>
591+
* If no process was set or the process is defined as a {@link ProcessHandle}, then {@link #UNKNOWN_EXIT_STATUS -1}
592+
* will be returned.
593+
* </p>
594+
*
595+
* @return the exit status of the process, if defined. If not defined a value of {@code -1} will be returned
596+
*
597+
* @throws IllegalStateException if the process has not yet terminated
598+
* @since 1.2
599+
*/
600+
default int exitValue() throws IllegalStateException {
601+
return UNKNOWN_EXIT_STATUS;
602+
}
603+
561604
/**
562605
* Reloads the server and returns immediately.
563606
*

src/main/java/org/wildfly/plugin/tools/server/StandaloneManager.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
public class StandaloneManager extends AbstractServerManager<ModelControllerClient> {
2525
private static final Logger LOGGER = Logger.getLogger(StandaloneManager.class);
2626

27-
StandaloneManager(final ProcessHandle process, final ModelControllerClient client,
27+
StandaloneManager(final Process process, final ProcessHandle processHandle, final ModelControllerClient client,
2828
final boolean shutdownOnClose) {
29-
super(process, client, shutdownOnClose);
29+
super(process, processHandle, client, shutdownOnClose);
3030
}
3131

3232
@Override
@@ -75,8 +75,8 @@ public void reloadIfRequired(final long timeout, final TimeUnit unit) throws IOE
7575

7676
@Override
7777
public boolean isRunning() {
78-
if (process != null) {
79-
return process.isAlive() && CommonOperations.isStandaloneRunning(client());
78+
if (processHandle != null) {
79+
return processHandle.isAlive() && CommonOperations.isStandaloneRunning(client());
8080
}
8181
return CommonOperations.isStandaloneRunning(client());
8282
}

src/test/java/org/wildfly/plugin/tools/Environment.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,13 @@ public static StandaloneManager launchStandalone(final boolean shutdownOnClose)
168168
}
169169

170170
public static DomainManager launchDomain() {
171+
return launchDomain(true);
172+
}
173+
174+
public static DomainManager launchDomain(final boolean shutdownOnClose) {
171175
final DomainManager serverManager = ServerManager
172176
.start(Configuration.create(DomainCommandBuilder.of(Environment.WILDFLY_HOME))
173-
.shutdownOnClose(true)
177+
.shutdownOnClose(shutdownOnClose)
174178
.managementAddress(HOSTNAME)
175179
.managementPort(PORT));
176180
try {

0 commit comments

Comments
 (0)