blob: c42fb35661c4c1f6fb745d5e901dc4a662696ba2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.sling.testing.serversetup.jarexec;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.commons.exec.ProcessDestroyer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Simple ProcessDestroyer for a single process, meant to be used
* with our JarExecutor.
*/
class ShutdownHookSingleProcessDestroyer implements ProcessDestroyer, Runnable {
private final Logger log = LoggerFactory.getLogger(getClass());
private Thread shutdownHookThread;
private Process process;
private final int timeoutSeconds;
private final String processInfo;
private boolean waitOnShutdown = false;
public ShutdownHookSingleProcessDestroyer(String processInfo, int timeoutSeconds) {
this.processInfo = processInfo;
this.timeoutSeconds = timeoutSeconds;
}
public boolean getWaitOnShutdown() {
return waitOnShutdown;
}
public void setWaitOnShutdown(boolean waitOnShutdown) {
this.waitOnShutdown = waitOnShutdown;
}
public synchronized boolean add(Process p) {
if(process != null) {
throw new IllegalStateException("Process already set: " + process);
}
if(shutdownHookThread == null) {
shutdownHookThread = new Thread(this, getClass().getSimpleName());
Runtime.getRuntime().addShutdownHook(shutdownHookThread);
}
process = p;
return true;
}
public synchronized boolean remove(Process p) {
p = null;
return true;
}
public int size() {
return 1;
}
public void run() {
destroyProcess(waitOnShutdown);
}
public void destroyProcess(boolean waitForIt) {
Process toDestroy = null;
synchronized (this) {
toDestroy = process;
process = null;
}
if(toDestroy == null) {
return;
}
toDestroy.destroy();
if(waitForIt) {
log.info("Waiting for destroyed process {} to exit (timeout={} seconds)", processInfo, timeoutSeconds);
final Thread mainThread = Thread.currentThread();
final Timer t = new Timer(true);
final TimerTask task = new TimerTask() {
@Override
public void run() {
mainThread.interrupt();
}
};
t.schedule(task, timeoutSeconds * 1000L);
try {
toDestroy.waitFor();
try {
final int exit = toDestroy.exitValue();
log.info("Process {} ended with exit code {}", processInfo, exit);
} catch(IllegalStateException ise) {
log.error("Failed to destroy process " + processInfo);
}
} catch (InterruptedException e) {
log.error("Timeout waiting for process " + processInfo + " to exit");
} finally {
t.cancel();
}
}
}
}