31
31
abstract class AbstractServerManager <T extends ModelControllerClient > implements ServerManager {
32
32
private static final Logger LOGGER = Logger .getLogger (AbstractServerManager .class );
33
33
34
- protected final ProcessHandle process ;
34
+ protected final ProcessHandle processHandle ;
35
+ private final Process process ;
35
36
final T client ;
36
37
private final boolean shutdownOnClose ;
37
38
private final DeploymentManager deploymentManager ;
38
39
private final AtomicBoolean closed ;
39
40
private final AtomicBoolean shutdown ;
41
+ private volatile Thread shutdownWaitThread ;
40
42
41
- protected AbstractServerManager (final ProcessHandle process , final T client ,
43
+ protected AbstractServerManager (final Process process , final ProcessHandle processHandle , final T client ,
42
44
final boolean shutdownOnClose ) {
43
45
this .process = process ;
46
+ this .processHandle = processHandle ;
44
47
this .client = client ;
45
48
this .shutdownOnClose = shutdownOnClose ;
46
49
deploymentManager = DeploymentManager .create (client );
@@ -76,14 +79,14 @@ public String launchType() {
76
79
77
80
@ Override
78
81
public CompletableFuture <ServerManager > kill () {
79
- if (process != null ) {
80
- if (process .isAlive ()) {
82
+ if (processHandle != null ) {
83
+ if (processHandle .isAlive ()) {
81
84
return CompletableFuture .supplyAsync (() -> {
82
85
internalClose (false , false );
83
- return process .destroyForcibly ();
86
+ return processHandle .destroyForcibly ();
84
87
}).thenCompose ((successfulRequest ) -> {
85
88
if (successfulRequest ) {
86
- return process .onExit ().thenApply ((processHandle ) -> this );
89
+ return processHandle .onExit ().thenApply ((ph ) -> this );
87
90
}
88
91
return CompletableFuture .completedFuture (this );
89
92
});
@@ -102,15 +105,15 @@ public boolean waitFor(final long startupTimeout, final TimeUnit unit) throws In
102
105
break ;
103
106
}
104
107
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 ());
107
110
}
108
111
TimeUnit .MILLISECONDS .sleep (sleep );
109
112
timeout -= sleep ;
110
113
}
111
114
if (timeout <= 0 ) {
112
- if (process != null ) {
113
- process .destroy ();
115
+ if (processHandle != null ) {
116
+ processHandle .destroy ();
114
117
}
115
118
return false ;
116
119
}
@@ -169,7 +172,7 @@ public void shutdown(final long timeout) throws IOException {
169
172
public CompletableFuture <ServerManager > shutdownAsync (final long timeout ) {
170
173
checkState ();
171
174
final ServerManager serverManager = this ;
172
- if (process != null ) {
175
+ if (processHandle != null ) {
173
176
return CompletableFuture .supplyAsync (() -> {
174
177
try {
175
178
if (shutdown .compareAndSet (false , true )) {
@@ -180,17 +183,17 @@ public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
180
183
}
181
184
return null ;
182
185
})
183
- .thenCombine (process .onExit (), (outcome , processHandle ) -> null )
186
+ .thenCombine (processHandle .onExit (), (outcome , processHandle ) -> null )
184
187
.handle ((ignore , error ) -> {
185
- if (error != null && process .isAlive ()) {
186
- if (process .destroyForcibly ()) {
188
+ if (error != null && processHandle .isAlive ()) {
189
+ if (processHandle .destroyForcibly ()) {
187
190
LOGGER .warnf (error ,
188
191
"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 ());
190
193
} else {
191
194
LOGGER .warnf (error ,
192
195
"Failed to shutdown server and destroy the process %d. The server may still be running in a process." ,
193
- process .pid ());
196
+ processHandle .pid ());
194
197
}
195
198
}
196
199
return serverManager ;
@@ -214,6 +217,29 @@ public CompletableFuture<ServerManager> shutdownAsync(final long timeout) {
214
217
});
215
218
}
216
219
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
+
217
243
@ Override
218
244
public boolean isClosed () {
219
245
return closed .get ();
@@ -245,26 +271,36 @@ void internalClose(final boolean shutdownOnClose, final boolean waitForShutdown)
245
271
}
246
272
}
247
273
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
+ }
251
286
}
252
287
}
253
288
}
254
289
255
290
abstract void internalShutdown (ModelControllerClient client , long timeout ) throws IOException ;
256
291
257
292
private void waitForShutdown (final ModelControllerClient client ) {
258
- if (process != null ) {
293
+ if (processHandle != null ) {
259
294
try {
260
- process .onExit ()
295
+ processHandle .onExit ()
261
296
.get ();
262
297
} 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 );
264
300
}
265
301
} else {
266
302
// Wait for the server manager to finish shutting down
267
- while (ServerManager .isRunning (client )) {
303
+ while (! closed . get () && ServerManager .isRunning (client )) {
268
304
Thread .onSpinWait ();
269
305
}
270
306
}
0 commit comments