/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This library 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 library 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
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 1997-2003, by :
* Corporate:
* Astrium SAS (MATRA MARCONI SPACE)
* EADS CRC
* Individual:
* Jean-Louis PATANE
* Claude Cazenave
* Nicolas Brodu
*
*
* $Id: SysExec.java,v 1.7 2007/10/23 11:14:00 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
*
*/
package simtools.util ;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Vector;
import java.util.logging.Logger;
/**
* This class defines a way to execute system commands
*
* @author Jean-Louis PATANE
*
* @version 1.0, 12/11/97
*/
public class SysExec {
/**
* A looger to dump error or warning messages in a soket, an output stream, a file...
*/
static Logger _logger = simtools.util.LogConfigurator.getLogger(SysExec.class.getName());
/**
* Constant for process execution error.
*/
static public final int MISS_ERROR = 999 ;
/**
* Constant for process execution error.
*/
static public final int WAIT_ERROR = 888 ;
/**
* Constant for process execution error.
*/
static public final int EXEC_ERROR = 777 ;
/**
* Constant for process execution error.
*/
static public final int READ_ERROR = 666 ;
/**
* Constant for process execution error.
*/
static public final int SYNC_ERROR = 555 ;
/**
* Constant for process execution error.
*/
static public final int KILL_ERROR = 444 ;
/**
* This commands of strings holds process execution commands.
*/
Vector commands ;
/**
* This commands of strings holds process environment variables.
*/
Vector envVariables;
/**
* This file represent the working directory
*/
File workingDirectory;
/**
* This flag is used by children threads to know if the process is terminated.
*/
public boolean finished ;
/**
* The exit value of the process (0 == ok)
*/
public int exitValue ;
/**
*
*/
public String errorMessage;
/**
* This thread manages with the stdout of the process.
*/
public OutPrintThread outPrintThread ;
/**
* This thread manages with the stderr of the process.
*/
public ErrPrintThread errPrintThread ;
/**
* This thread waits for the end of the process.
*/
public WaitThread waitThread ;
/**
* The child process to execute the command line currently built.
*/
Process process ;
/**
* The object where to write output & errors.
*/
ErrOutWriter writer ;
/**
* Write output & errors to logger.
*/
boolean dumpToLogger;
/**
* Initialization of the instance
* with an empty vector of process execution commands.
*/
public SysExec() {
commands = new Vector() ;
envVariables = new Vector();
process = null ;
writer = null ;
outPrintThread = null ;
errPrintThread = null ;
waitThread = null;
workingDirectory = null;
dumpToLogger = false;
}
/**
* @param theWriter: an ErrOutWriter object (for output & error streams)
*/
public SysExec(ErrOutWriter theWriter) {
this();
writer = theWriter;
}
/**
* @param dumpToLogger: an logger object (for output & error streams)
*/
public SysExec(boolean dumpToLogger){
this();
dumpToLogger = true;
}
/**
* Inner interface for output & error streams.
*/
public interface ErrOutWriter {
public void outPrintln(String line);
public void errPrintln(String line);
}
/**
* Remove all commands in the command line currently built.
*/
public void reset() {
envVariables.removeAllElements();
commands.removeAllElements();
workingDirectory = null;
}
/**
* Append a command at the end of the command line currently built.
*
* @param command to append at the end of the command line
* @see UNIX_PreProcH
* @see MSDOS_PreProc
*/
public void add(String cmd) {
commands.addElement(cmd) ;
}
/**
* Stops the currently working process.
*/
public void killAll() {
if (process != null) {
process.destroy() ;
}
exitValue = KILL_ERROR ;
}
/**
* set working directory for SysExec
* @param workingDirectoryPath : the path
* @return : true if the working has been correctly set
*/
public boolean setWorkingDirectory(String workingDirectoryPath){
boolean result = false;
workingDirectory = new File(workingDirectoryPath);
if(workingDirectory.isDirectory()){
result = true;
}else{
workingDirectory = null;
}
return result;
}
/**
* add an environment variable
* @param variableName : the name of the variable
* @param variableValue : th value of the variable
* @return : true if the variable has been correctly added and, else false
*/
public boolean addEnvVariable(String variableName, String variableValue){
boolean result = false;
if(variableName!=null && variableValue!=null){
envVariables.add((String)(variableName+"="+variableValue));
result = true;
}
return result;
}
/**
* Create an external child process to execute the command line currently built,
* and wait for this process terminates.
*
* @return the exit value of the process (0 == ok)
* @see UNIX_PreProcH
* @see MSDOS_PreProc
*/
public int run() {
// First wait for the end of previous run
while(waitThread != null) {
try {
waitThread.join() ;
continue ;
}
catch (NullPointerException e) {
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println("Waiting for the end of previous run ...");
}
// Get the parameters for the call
String [] params = (String[]) commands.toArray(new String[commands.size()]);
//Get the envirronment variable for the call
String [] env = (String[]) envVariables.toArray(new String[envVariables.size()]);
exitValue = MISS_ERROR ;
try {
if(workingDirectory != null){
//If working directory is not null. (with or wothout env var).
process = Runtime.getRuntime().exec(params,env,workingDirectory) ;
}else if(env != null && env.length > 0){
//Here working directory is null, but there is env var defined.
process = Runtime.getRuntime().exec(params,env);
}else{
//and finally, here there is no env, nor working directory defined.
process = Runtime.getRuntime().exec(params);
}
// Gets the input stream of the subprocess. This stream is usually buffered.
// The input stream connected to the normal output of the subprocess.
InputStream processOutInputStream = process.getInputStream() ;
// Gets the error stream of the subprocess. This stream is usually unbuffered.
// The input stream connected to the error stream of the subprocess.
InputStream processErrInputStream = process.getErrorStream() ;
finished = false ;
//Starting all the outputs thread.
outPrintThread = new OutPrintThread(this,processOutInputStream) ;
errPrintThread = new ErrPrintThread(this,processErrInputStream) ;
waitThread = new WaitThread(this,process) ;
outPrintThread.start() ;
errPrintThread.start() ;
//waiting for task completion.
waitThread.start() ;
waitThread.join() ;
}catch (IOException ex2) {
exitValue = EXEC_ERROR ;
errPrintln("SysExec error : " + ex2) ;
errorMessage = ex2.getMessage();
}catch (InterruptedException ex1) {
exitValue = SYNC_ERROR ;
errPrintln("SysExec error : " + ex1) ;
errorMessage = ex1.getMessage();
}
//Nullify all threads.
process = null;
//Here there is no need to stop the different threads.
//Since when SysExec.finished become true, these thread will be stopped automatically.
outPrintThread = null ;
errPrintThread = null ;
waitThread = null ;
//return the result.
return exitValue ;
}
/**
* This method can be overwritten in a derived class to
* catch each text line sent by the process in its stdout.
*
* @param line A text line sent by the process in its stdout.
*/
public void outPrintln(String line) {
if(writer != null) {
writer.outPrintln(line) ;
}else {
if (dumpToLogger)
_logger.fine(line) ;
else
System.out.println(line);
}
}
/**
* This method can be overwritten in a derived class to
* catch each text line sent by the process in its stderr.
*
* @param line A text line sent by the process in its stderr.
*/
public void errPrintln(String line) {
if(writer != null) {
writer.errPrintln(line) ;
}else {
if (dumpToLogger)
_logger.severe(line) ;
else
System.err.println(line);
}
}
class OutPrintThread extends PrintThread {
public OutPrintThread(SysExec _parent, InputStream _input) {
super("OutPrintThread",_parent,_input) ;
}
protected void println(String line) {
parent.outPrintln(line) ;
}
}
class ErrPrintThread extends PrintThread {
public ErrPrintThread(SysExec _parent, InputStream _input) {
super("ErrPrintThread",_parent,_input) ;
}
protected void println(String line) {
parent.errPrintln(line) ;
}
}
abstract class PrintThread extends Thread {
InputStream input ;
SysExec parent ;
abstract protected void println(String line) ;
public PrintThread(String _name, SysExec _parent, InputStream _input) {
super(_name) ;
parent = _parent ;
input = _input ;
}
public void run() {
try {
StringBuffer tmp = new StringBuffer(200) ;
DataInputStream r = new DataInputStream(input) ;
while (! parent.finished) {
sleep(10);
while (r.available() > 0) {
int b = r.readUnsignedByte() ;
if (b != '\r') {
if (b == '\n') {
println(tmp.toString()) ;
yield() ;
tmp = new StringBuffer(200) ;
}
else {
tmp.append((char) b) ;
}
}
}
}
if (tmp.toString().length() > 0) {
println(tmp.toString()) ;
}
}
catch (IOException e) {
parent.exitValue = READ_ERROR ;
//Do not display IOException, since it can be raised if nominal case is to kill the parent.
//parent.errPrintln("SysExec error " + getName() + " : " + e) ;
}
catch (InterruptedException ie) {
parent.exitValue = READ_ERROR ;
parent.errPrintln("SysExec error " + getName() + " : " + ie) ;
}
}
}
class WaitThread extends Thread {
SysExec parent ;
Process process ;
public WaitThread(SysExec _parent, Process _process) {
super("WaitThread") ;
parent = _parent ;
process = _process ;
}
public void run() {
try {
parent.exitValue = process.waitFor() ;
}
catch (InterruptedException e) {
parent.exitValue = WAIT_ERROR ;
parent.errPrintln("SysExec error " + getName() + " : " + e) ;
}
try {
parent.finished = true ;
parent.outPrintThread.join() ;
parent.errPrintThread.join() ;
}
catch (InterruptedException e) {
parent.exitValue = SYNC_ERROR ;
parent.errPrintln("SysExec error " + getName() + " : " + e) ;
}
catch (NullPointerException e) {
parent.exitValue = SYNC_ERROR ;
parent.errPrintln("SysExec error " + getName() + " : " + e) ;
}
}
}
public void print() {
Enumeration e = commands.elements() ;
while (e.hasMoreElements()) {
_logger.fine("--> \"" + (String) e.nextElement() + "\"") ;
}
}
public static void main(String args[]) {
SysExec se = new SysExec() ;
se.add("sh") ;
se.add("-c") ;
se.add("ls > ls.txt") ;
se.print() ;
se.run() ;
se.reset() ;
se.add("cat") ;
se.add("ls.txt") ;
se.print() ;
se.run() ;
}
}