Skip to content

Commit 01e8d03

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 cf63dd3 commit 01e8d03

File tree

6 files changed

+202
-42
lines changed

6 files changed

+202
-42
lines changed

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

+59-23
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,19 @@
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;
3940
private final AtomicBoolean shutdown;
41+
private volatile Thread shutdownWaitThread;
4042

41-
protected AbstractServerManager(final ProcessHandle process, final T client,
43+
protected AbstractServerManager(final Process process, final ProcessHandle processHandle, final T client,
4244
final boolean shutdownOnClose) {
4345
this.process = process;
46+
this.processHandle = processHandle;
4447
this.client = client;
4548
this.shutdownOnClose = shutdownOnClose;
4649
deploymentManager = DeploymentManager.create(client);
@@ -76,14 +79,14 @@ public String launchType() {
7679

7780
@Override
7881
public CompletableFuture<ServerManager> kill() {
79-
if (process != null) {
80-
if (process.isAlive()) {
82+
if (processHandle != null) {
83+
if (processHandle.isAlive()) {
8184
return CompletableFuture.supplyAsync(() -> {
8285
internalClose(false, false);
83-
return process.destroyForcibly();
86+
return processHandle.destroyForcibly();
8487
}).thenCompose((successfulRequest) -> {
8588
if (successfulRequest) {
86-
return process.onExit().thenApply((processHandle) -> this);
89+
return processHandle.onExit().thenApply((ph) -> this);
8790
}
8891
return CompletableFuture.completedFuture(this);
8992
});
@@ -102,15 +105,15 @@ public boolean waitFor(final long startupTimeout, final TimeUnit unit) throws In
102105
break;
103106
}
104107
timeout -= (System.currentTimeMillis() - before);
105-
if (process != null && !process.isAlive()) {
106-
throw new ServerManagerException("The process %d is no longer active.", process.pid());
108+
if (processHandle != null && !processHandle.isAlive()) {
109+
throw new ServerManagerException("The process %d is no longer active.", processHandle.pid());
107110
}
108111
TimeUnit.MILLISECONDS.sleep(sleep);
109112
timeout -= sleep;
110113
}
111114
if (timeout <= 0) {
112-
if (process != null) {
113-
process.destroy();
115+
if (processHandle != null) {
116+
processHandle.destroy();
114117
}
115118
return false;
116119
}
@@ -169,7 +172,7 @@ public void shutdown(final long timeout) throws IOException {
169172
public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
170173
checkState();
171174
final ServerManager serverManager = this;
172-
if (process != null) {
175+
if (processHandle != null) {
173176
return CompletableFuture.supplyAsync(() -> {
174177
try {
175178
if (shutdown.compareAndSet(false, true)) {
@@ -180,17 +183,17 @@ public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
180183
}
181184
return null;
182185
})
183-
.thenCombine(process.onExit(), (outcome, processHandle) -> null)
186+
.thenCombine(processHandle.onExit(), (outcome, processHandle) -> null)
184187
.handle((ignore, error) -> {
185-
if (error != null && process.isAlive()) {
186-
if (process.destroyForcibly()) {
188+
if (error != null && processHandle.isAlive()) {
189+
if (processHandle.destroyForcibly()) {
187190
LOGGER.warnf(error,
188191
"Failed to shutdown the server. An attempt to destroy the process %d has been made, but it may still temporarily run in the background.",
189-
process.pid());
192+
processHandle.pid());
190193
} else {
191194
LOGGER.warnf(error,
192195
"Failed to shutdown server and destroy the process %d. The server may still be running in a process.",
193-
process.pid());
196+
processHandle.pid());
194197
}
195198
}
196199
return serverManager;
@@ -214,6 +217,29 @@ public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
214217
});
215218
}
216219

220+
@Override
221+
public int waitForTermination() throws InterruptedException {
222+
checkState();
223+
shutdownWaitThread = Thread.currentThread();
224+
try {
225+
if (process != null) {
226+
return process.waitFor();
227+
}
228+
waitForShutdown(client);
229+
return UNKNOWN_EXIT_STATUS;
230+
} finally {
231+
shutdownWaitThread = null;
232+
}
233+
}
234+
235+
@Override
236+
public int exitValue() throws IllegalStateException {
237+
if (process != null) {
238+
return process.exitValue();
239+
}
240+
return UNKNOWN_EXIT_STATUS;
241+
}
242+
217243
@Override
218244
public boolean isClosed() {
219245
return closed.get();
@@ -245,26 +271,36 @@ void internalClose(final boolean shutdownOnClose, final boolean waitForShutdown)
245271
}
246272
}
247273
try {
248-
client.close();
249-
} catch (IOException e) {
250-
LOGGER.error("Failed to close the client.", e);
274+
// Interrupt the shutdown thread if it's still running
275+
final Thread shutdownThread = shutdownWaitThread;
276+
if (shutdownThread != null) {
277+
LOGGER.debugf("Interrupting shutdown thread %s.", shutdownThread.getName());
278+
shutdownThread.interrupt();
279+
}
280+
} finally {
281+
try {
282+
client.close();
283+
} catch (IOException e) {
284+
LOGGER.error("Failed to close the client.", e);
285+
}
251286
}
252287
}
253288
}
254289

255290
abstract void internalShutdown(ModelControllerClient client, long timeout) throws IOException;
256291

257292
private void waitForShutdown(final ModelControllerClient client) {
258-
if (process != null) {
293+
if (processHandle != null) {
259294
try {
260-
process.onExit()
295+
processHandle.onExit()
261296
.get();
262297
} catch (InterruptedException | ExecutionException e) {
263-
throw new ServerManagerException(e, "Error waiting for process %d to exit.", process.pid());
298+
throw new ServerManagerException(String.format("Error waiting for process %d to exit.", processHandle.pid()),
299+
e);
264300
}
265301
} else {
266302
// Wait for the server manager to finish shutting down
267-
while (ServerManager.isRunning(client)) {
303+
while (!closed.get() && ServerManager.isRunning(client)) {
268304
Thread.onSpinWait();
269305
}
270306
}

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

@@ -143,7 +146,7 @@ public Builder shutdownOnClose(final boolean shutdownOnClose) {
143146
* @return a new {@link StandaloneManager}
144147
*/
145148
public StandaloneManager standalone() {
146-
return new StandaloneManager(process, configuration.client(), configuration.shutdownOnClose());
149+
return new StandaloneManager(process, processHandle, configuration.client(), configuration.shutdownOnClose());
147150
}
148151

149152
/**
@@ -154,7 +157,7 @@ public StandaloneManager standalone() {
154157
* @return a new {@link DomainManager}
155158
*/
156159
public DomainManager domain() {
157-
return new DomainManager(process, getOrCreateDomainClient(), configuration.shutdownOnClose());
160+
return new DomainManager(process, processHandle, getOrCreateDomainClient(), configuration.shutdownOnClose());
158161
}
159162

160163
/**
@@ -175,7 +178,8 @@ public DomainManager domain() {
175178
public CompletableFuture<ServerManager> build() {
176179
@SuppressWarnings("resource")
177180
final ModelControllerClient client = configuration.client();
178-
final ProcessHandle process = this.process;
181+
final Process process = this.process;
182+
final ProcessHandle processHandle = this.processHandle;
179183
return CompletableFuture.supplyAsync(() -> {
180184
// Wait until the server is running, then determine what type we need to return
181185
while (!isRunning(client)) {
@@ -189,9 +193,10 @@ public CompletableFuture<ServerManager> build() {
189193
final String launchType = launchType(client).orElseThrow(() -> new ServerManagerException(
190194
"Could not determine the type of the server. Verify the server is running."));
191195
if ("STANDALONE".equals(launchType)) {
192-
return new StandaloneManager(process, client, configuration.shutdownOnClose());
196+
return new StandaloneManager(process, processHandle, client, configuration.shutdownOnClose());
193197
} else if ("DOMAIN".equals(launchType)) {
194-
return new DomainManager(process, getOrCreateDomainClient(), configuration.shutdownOnClose());
198+
return new DomainManager(process, processHandle, getOrCreateDomainClient(),
199+
configuration.shutdownOnClose());
195200
}
196201
throw new ServerManagerException("Only standalone and domain servers are support. %s is not supported.",
197202
launchType);
@@ -574,6 +579,44 @@ default CompletableFuture<ServerManager> shutdownAsync(long timeout) {
574579
});
575580
}
576581

582+
/**
583+
* Waits for the server to be shutdown and the process, if defined, to terminate. This returns the exit status of
584+
* the process if it was defined. Otherwise, {@link #UNKNOWN_EXIT_STATUS -1} will be returned.
585+
* <p>
586+
* Note this is a blocking action and will block the current thread until the server has been exited or this server
587+
* manager has been {@linkplain #close() closed}. If this server manager has been closed, the thread waiting for
588+
* terminate will be interrupted.
589+
* </p>
590+
*
591+
* @return the exit status of the process, if defined. If not defined a value of {@code -1} will be returned
592+
*
593+
* @throws InterruptedException if the current thread is interrupted while waiting for the server to exit
594+
* @since 1.2
595+
*/
596+
default int waitForTermination() throws InterruptedException {
597+
while (!isClosed() && isRunning()) {
598+
Thread.onSpinWait();
599+
}
600+
return UNKNOWN_EXIT_STATUS;
601+
}
602+
603+
/**
604+
* Returns the exit status of the process if it's defined. If the {@link Builder#process(Process)} was used and the
605+
* process has not yet terminated, an {@link IllegalStateException} will be thrown.
606+
* <p>
607+
* If no process was set or the process is defined as a {@link ProcessHandle}, then {@link #UNKNOWN_EXIT_STATUS -1}
608+
* will be returned.
609+
* </p>
610+
*
611+
* @return the exit status of the process, if defined. If not defined a value of {@code -1} will be returned
612+
*
613+
* @throws IllegalStateException if the process has not yet terminated
614+
* @since 1.2
615+
*/
616+
default int exitValue() throws IllegalStateException {
617+
return UNKNOWN_EXIT_STATUS;
618+
}
619+
577620
/**
578621
* Reloads the server and returns immediately.
579622
*

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)