| /* |
| * 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(); |
| } |
| } |
| } |
| } |