/*
* Lokomo OneCMDB - An Open Source Software for Configuration
* Management of Datacenter Resources
*
* Copyright (C) 2006 Lokomo Systems AB
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via
* paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33
* Danderyd, Sweden.
*
*/
package org.onecmdb.core.internal.job;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class RunNativeProgram extends Thread {
private List<String> execArgs = new ArrayList<String>();
private Process process = null;
private int exitStatus = 0;
private CheckInputStream stdoutThread = null;
private CheckInputStream stderrThread = null;
public static final int EXIT_ALREADY_RUNNING = -128000;
public static final int EXIT_CANT_START = -128001;
public static final int EXIT_NOT_TERMINATED = -128002;
public static final int EXIT_INTERRUPTED = -128003;
private Log logger = null;
private OutputStream stdin = null;
private OutputStream stdout = null;
private OutputStream stderr = null;
public RunNativeProgram(String programPath, String[] args) {
execArgs.add(programPath);
if (args != null) {
Collections.addAll(execArgs, args);
}
}
public void setStdout(OutputStream out) {
this.stdout = out;
}
public void setStderr(OutputStream out) {
this.stderr = out;
}
public OutputStream getStdin() {
return (this.stdin);
}
/**
* DOCUMENT ME!
*/
public void run() {
// ////Log.log("Start process " + nativeProgram);
if (process != null) {
// Already running..., Don't start it again.
setExitStatus(EXIT_ALREADY_RUNNING);
return;
}
try {
this.process = Runtime.getRuntime().exec((String[])execArgs.toArray(new String[0]), null, null);
// TODO:
// Listen to stdin/stdout/stderr else there can be problems
// exceuting shells/bat-scripts.
stdoutThread = new CheckInputStream("stdout", process
.getInputStream(), stdout);
stderrThread = new CheckInputStream("stderr", process
.getErrorStream(), stderr);
stdin = process.getOutputStream();
// stdin.close();
stdoutThread.start();
stderrThread.start();
} catch (IOException e) {
setExitStatus(EXIT_CANT_START);
}
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public int getExitStatus() {
return (exitStatus);
}
private void setExitStatus(int status) {
exitStatus = status;
}
/**
* Try to kill the process.
*
*/
public void terminate() {
if (process != null) {
process.destroy();
}
}
/**
* Wait for this process to stop. A timeout in ms must be specified. If the
* process hasn't terminated inside the timeout then it will be abruptly
* killed. If timeout is set to 0 it will wait until process ends.
*/
public void waitForProcess(long timeout) {
try {
// Check that the process has started...
int loopCount = 0;
while (process == null) {
// ////Log.log("No process running....");
if (loopCount > 15) {
// ////Log.err("Process hasn't started yet", null);
setExitStatus(EXIT_CANT_START);
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// ////Log.log("WaitForProcess was interuppted!");
setExitStatus(EXIT_CANT_START);
return;
}
loopCount++;
}
if (timeout == 0) {
// ////Log.log("Wait unti process " + nativeProgram + "
// terminates");
process.waitFor();
setExitStatus(process.exitValue());
} else {
// ////Log.log("Set timeout to " + timeout + "s on process " +
// nativeProgram);
int sleepTime = 0;
int exitS = EXIT_NOT_TERMINATED;
boolean finished = false;
while (!finished) {
try {
Thread.sleep(1000);
exitS = process.exitValue();
finished = true;
} catch (IllegalThreadStateException e) {
sleepTime += 1000;
if (sleepTime > timeout) {
throw e;
}
}
}
setExitStatus(exitS);
// ////Log.log("Process " + nativeProgram + " has terminated");
}
} catch (IllegalThreadStateException e) {
// Process hans't exited...
// Kill it and set error status.
// ////Log.err("Process " + nativeProgram + " has not terminated.",
// e);
process.destroy();
// ////Log.err("Process " + nativeProgram + " was killed.", e);
setExitStatus(EXIT_NOT_TERMINATED);
} catch (InterruptedException e) {
// What to do.
// //Log.err("waitForEnd was interrupted...", e);
process.destroy();
// //Log.err("Process " + nativeProgram + " was killed.", e);
setExitStatus(EXIT_INTERRUPTED);
} finally {
if (stdoutThread != null) {
stdoutThread.terminate();
stdoutThread = null;
}
if (stderrThread != null) {
stderrThread.terminate();
stderrThread = null;
}
if (stdin != null) {
try {
stdin.close();
} catch (IOException e) {
}
}
}
// //Log.log("Process " + nativeProgram + " exitstatus set to " +
// getExitStatus());
}
/**
* DOCUMENT ME!
*
* @param argv
* DOCUMENT ME!
*/
public static void main(String[] argv) {
List<String> argList = new ArrayList<String>();
Collections.addAll(argList, argv);
String program = argList.get(0);
argList.remove(0);
String[] args = new String[0];
if (argList.size() > 0) {
args = (String[])argList.toArray(new String[0]);
}
RunNativeProgram runNative = new RunNativeProgram(argv[0], args);
runNative.setStdout(System.out);
runNative.start();
runNative.waitForProcess(Integer.parseInt(argv[1]));
}
/**
* Help class to scan stdin/stdout..
*/
class CheckInputStream extends Thread {
private volatile boolean terminate = false;
private InputStream input = null;
private OutputStream output = null;
private String name = null;
/**
* Creates a new CheckInputStream object.
*
* @param name
* DOCUMENT ME!
* @param input
* DOCUMENT ME!
*/
public CheckInputStream(String name, InputStream input,
OutputStream output) {
this.input = input;
this.name = name;
this.output = output;
}
/**
* DOCUMENT ME!
*/
public void run() {
try {
// Try to read input.
StringBuffer buffer = new StringBuffer();
while (!terminate) {
int len = input.available();
if (len > 0) {
byte buf[] = new byte[len];
//System.out.println("Avaliable " + input.available());
int readLen= input.read(buf, 0, len);
if (output != null) {
output.write(buf, 0, readLen);
}
}
if (len < 0) {
terminate();
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
}
} catch (IOException e) {
// //Log.log("Read " + name + " got " + e);
}
// //Log.log("CheckInputStream " + name + " terminated");
}
/**
* DOCUMENT ME!
*/
public synchronized void terminate() {
try {
if (this.output != null) {
while (input.available() > 0) {
int len = input.available();
byte buf[] = new byte[len];
//System.out.println("Avaliable " + input.available());
int readLen= input.read(buf, 0, len);
output.write(buf, 0, readLen);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
// System.out.print(" S" + input.available());
}
}
} catch (IOException e) {
}
// //Log.log("Terminating " + name);
// this.interrupt(); Will crash JVM on BSDI 4.1!!!
try {
input.close();
} catch (IOException e) {
// //Log.err("Got exception closing input : " + e, e);
}
/*
if (output != null) {
try {
output.close();
} catch (IOException e) {
}
}
*/
terminate = true;
}
}
}