/*
* JBoss, Home of Professional Open Source.
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.test.scripts.support;
import java.lang.InterruptedException ;
import java.io.BufferedReader ;
import java.io.InputStreamReader ;
import java.io.PrintWriter ;
import java.io.StringWriter ;
import java.io.FileReader ;
import java.io.FileWriter ;
import java.io.File ;
import java.io.FileInputStream ;
import java.io.FileOutputStream ;
import java.io.FileNotFoundException ;
import java.io.IOException ;
import org.jboss.test.scripts.support.AbstractShellScriptExecutor ;
/**
* Class to execute shell scripts asynchronously and collect output
*
* @author Richard Achmatowicz
* @version $Revision: 1.0
*/
public class AsyncShellScriptExecutor extends AbstractShellScriptExecutor {
/* variables for controlling use of shutdown.sar */
boolean useShutdown = false ;
String pathToShutdownSar = null ;
String deployDir = null ;
public boolean isRunning() {
return process != null ;
}
public boolean getUseShutdown() {
return useShutdown ;
}
public void setUseShutdown(boolean useShutdown) {
this.useShutdown = useShutdown ;
}
public String getPathToShutdownSar() {
return pathToShutdownSar ;
}
public void setPathToShutdownSar(String pathToShutdownSar) {
this.pathToShutdownSar = pathToShutdownSar ;
}
public String getDeployDir() {
return deployDir ;
}
public void setDeployDir(String deployDir) {
this.deployDir = deployDir ;
}
public void startShellCommand(String[] commandArray) throws Exception {
startShellCommand(commandArray, null, null) ;
}
public void startShellCommand(String[] commandArray, String[] envp) throws Exception {
startShellCommand(commandArray, envp, null) ;
}
public void startShellCommand(String[] commandArray, String[] envp, File workingDir) throws Exception {
/* create strings to write output to */
outWriter = new StringWriter() ;
outlog = new PrintWriter(outWriter,true);
errWriter = new StringWriter() ;
errorlog = new PrintWriter(errWriter,true);
/* transparency */
writeShellCommand(commandArray) ;
try {
process = Runtime.getRuntime().exec(commandArray, envp, workingDir);
} catch (IOException ioe) {
System.err.println("Could not start command."+ ioe);
return;
}
/* open the streams here */
final BufferedReader inStream = new BufferedReader(new InputStreamReader(process.getInputStream()));
final BufferedReader errStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
/* start pumping output from process to files */
Thread outPump = new OutputPumper(inStream, outlog);
outPump.start();
Thread errPump = new OutputPumper(errStream, errorlog);
errPump.start();
/* if we don't give the pumpers a little time, we can miss lines */
try {
Thread.sleep(100) ;
}
catch(InterruptedException ie) {
}
/* return control to the caller */
}
/*
* Join and wait
*/
public void joinShellCommand() {
boolean terminated = false ;
/* check for tetrmination of the command */
int exitCode = 0 ;
try {
exitCode = process.exitValue() ;
terminated = true ;
}
catch (IllegalThreadStateException itse) {
System.out.println("Process not yet terminated - waiting for termination (possibly forever)") ;
// only wait if the stuff wa not found? The process has terminated already?
try {
exitCode = process.waitFor();
terminated = true ;
} catch (InterruptedException ie) {
System.out.println("Error in wait") ;
}
}
if (terminated) {
System.out.println("Process terminated exit code = " + exitCode) ;
}
/* close the streams here */
outlog.close() ;
errorlog.close() ;
closeAllStreams(process) ;
/* indicate process is no longer running */
process = null ;
}
/*
* Join with timeout
*/
public void joinShellCommand(int timeout) {
int timeLeft = timeout ;
boolean terminated = false ;
/* check for termination of the command */
int exitCode = 0 ;
try {
exitCode = process.exitValue() ;
terminated = true ;
}
catch (IllegalThreadStateException itse) {
/* the command has not terminated, wait for timeout seconds */
System.out.println("Process not yet terminated - waiting for " + timeout + " seconds") ;
while (timeLeft > 0) {
try {
exitCode = process.exitValue();
terminated = true ;
break ;
} catch (IllegalThreadStateException itse2) {
System.out.println("Still waiting...") ;
try {
Thread.sleep(1*1000) ;
}
catch(InterruptedException ie) {
}
timeLeft-- ;
}
}
/* the command has not terminated, we need to kill it (somehow) */
if (!terminated) {
/* we are running a server instance via a shell */
if (useShutdown) {
try {
// shutdownServer(getDeployURL("shutdown.sar").getPath(), getDeployDir()) ;
shutdownServer(getPathToShutdownSar(), getDeployDir()) ;
}
catch(IOException e) {
/* we have got to do something about this case... */
/* short of getting the pid of the shell and sending a signal, I don't know */
System.out.println("Critical error: can't shut down server: " + e.getMessage()) ;
}
}
/* now kill the shell process if it is still active */
try {
exitCode = process.exitValue();
} catch (IllegalThreadStateException itse3) {
System.out.println("Shell command did not terminate - destroying shell process...command may still be running") ;
// close the streams first
outlog.close() ;
errorlog.close() ;
closeAllStreams(process) ;
// kill the shell process
process.destroy() ;
}
}
}
if (terminated) {
System.out.println("Process terminated with exit code " + exitCode) ;
}
/* close the streams here */
outlog.close() ;
errorlog.close() ;
closeAllStreams(process) ;
/* indicate process is no longer running */
process = null ;
}
/*
* Shutdown a server by deploying shutdown.sar into its deploy directory
* and then deleting it.
*
* This is about the only way we can kill a server started with run.sh/run.bat
* due to the difficulty of:
* (i) discovering the process id of the shell or the server started by the shell
* (ii) sending a signal to that process
* on both Windows and UNIX.
*/
private void shutdownServer(String pathToShutdownSar, String deployDir) throws IOException {
final int SHUTDOWN_DEPLOY_TIME = 5 ;
final int SHUTDOWN_UNDEPLOY_TIME = 10 ;
if (pathToShutdownSar == null || deployDir == null) {
throw new IOException("Skipping deployment of shutdown.sar: path to shutdown and deploy dir are not set") ;
}
/* first try to shutdown the server instance */
System.out.println("Shell command did not terminate - deploying shutdown.sar...") ;
String shutdownSource = null ;
String shutdownTarget = null ;
try {
shutdownSource = pathToShutdownSar ;
shutdownTarget = deployDir + "/shutdown.sar" ;
// System.out.println("source = " + source) ;
// System.out.println("target = " + target) ;
copyFile(new File(shutdownSource), new File(shutdownTarget));
}
catch(Exception e) {
throw new IOException("Problem deploying shutdown: " + e.getMessage()) ;
} ;
// give shutdown.sar time to deploy
try {
Thread.sleep(SHUTDOWN_DEPLOY_TIME*1000) ;
}
catch(InterruptedException ie) {
}
// now undeploy shutdown.sar
File shutdown = new File(shutdownTarget) ;
if (!shutdown.exists()) {
throw new IOException("Problem undeploying shutdown: shutdown.sar file not found") ;
}
shutdown.delete() ;
// give shutdown.sar time to undeploy (and cause the AS to exit)
try {
Thread.sleep(SHUTDOWN_UNDEPLOY_TIME*1000) ;
}
catch(InterruptedException ie2) {
}
}
private void copyFile(File in, File out) throws Exception {
FileInputStream fis = new FileInputStream(in);
FileOutputStream fos = new FileOutputStream(out);
try {
byte[] buf = new byte[1024];
int i = 0;
while ((i = fis.read(buf)) != -1) {
fos.write(buf, 0, i);
}
}
catch (Exception e) {
throw e;
}
finally {
if (fis != null) fis.close();
if (fos != null) fos.close();
}
}
}