Hallo,
in meinem java-Programm rufe ich öfters externe Befehle auf.
Da ich in der Hauptlogik meines Programmes die Übersicht behalten will, habe ich eine eigene Klasse cmd geschrieben.
Sie sieht so aus:
die Klasse ist abstrakt; ich kann aber nun für verschiedene Befehle einfache Klassen bauen; z.B. der gzip-Befehl:
jetzt hab ich aber noch ein Problem mit meiner run-Methode.
Und zwar hab ich gelesen, dass die Methode Process.waitFor irgendwie nur dann funktioniert, wenn alle InputStreams eines externen Programms "abgearbeitet" wurden.
ich schreibe also statt
... trotzdem "hängt sich mein Programm auf" - woran kann das liegen?!
mfg, guni
in meinem java-Programm rufe ich öfters externe Befehle auf.
Da ich in der Hauptlogik meines Programmes die Übersicht behalten will, habe ich eine eigene Klasse cmd geschrieben.
Sie sieht so aus:
Java:
public abstract class Cmd extends LinkedList<String> {
private static final long serialVersionUID = 1L;
private BufferedReader stdin = null;
private BufferedReader stderr = null;
private Runtime runtime = null;
// fügt angegebenes Command als ersten Listeneintrag von self hinzu
// Fehler wenn Befehl nicht existiert; d.h. unter Win nicht als
// <HOME>\bin\<BEFEHL>.exe bzw. unter Linux nicht als /bin/<BEFEHL>
// zu finden ist
public Cmd(Runtime r, String cmd) throws Exception {
super();
File command = new File(getSysPath(), cmd + getExt());
if (!command.exists()) {
throw new Exception("Fehler: Befehl nicht gefunden; "
+ command.getAbsolutePath());
}
add(command.getAbsolutePath());
runtime = r;
}
public BufferedReader getStdin() throws Exception {
if (stdin == null) {
throw new Exception(
"Kristischer Programmfehler: "
+ "Das Programm versucht den InputStream eines Prozesses zu lesen, "
+ "der noch nicht gestartet wurde");
}
return stdin;
}
public BufferedReader getStderr() throws Exception {
if (stderr == null) {
throw new Exception(
"Kristischer Programmfehler: "
+ "Das Programm versucht den ErrorStream eines Prozesses zu lesen, "
+ "der noch nicht gestartet wurde");
}
return stderr;
}
// gibt einen vollständigen Befehl aus
public void dump() {
Iterator<String> i = iterator();
while (i.hasNext()) {
System.out.println(i.next());
}
}
// führt einen Befehl in der Runtime aus
// die p.waitFor muss nach Verarbeiten der Streams eingebaut werden
protected Process run(File workingdir) throws Exception {
if (runtime == null) {
throw new Exception("Kritischer Programmfehler: "
+ "Befehl konnte nicht ausgeführt werden, da Runtime "
+ "nicht initialisiert ist.");
}
Process p = runtime.exec(this.toArray(new String[0]), null, workingdir);
stdin = new BufferedReader(new InputStreamReader(p.getInputStream()));
stderr = new BufferedReader(new InputStreamReader(p.getErrorStream()));
return p;
}
// wenn mehr als der eigentliche Befehl vorhanden ist, dann alles an
// Zusatzeinträgen wieder weg
protected void rmparams() {
if (this.size() > 1) {
String entry = this.removeFirst();
this.clear();
this.add(entry);
}
}
protected void printStream(BufferedReader r) throws IOException {
String line = "";
while ((line = r.readLine()) != null) {
System.out.println(line);
}
}
// gibt für Linux /bin zurück
// gibt für windows <HOME>/bin zurück
// Fehler wenn,
// Betriebssystem weder Windows noch Linux
// keine HOME-Umgebungsvariable gesetzt ist (wo /bin/cmd zu finden ist)
// /bin oder <HOME>\bin am System nicht existiert
// /bin oder <HOME>\bin kein Directory ist
protected File getSysPath() throws Exception {
File path = null;
String home = System.getenv("HOME");
home = "C:\\cygwin"; // TODO: nur für Testfälle
if (home == null) {
throw new Exception(
"Fehler: Commands können nicht initialisiert werden, "
+ "da keine Home-Variable gefunden wurde. "
+ "Command muss evt. unter <HOME>/bin zu finden sein");
}
if (OsValidator.isUnix()) {
path = new File("/bin");
} else if (OsValidator.isWindows()) {
path = new File(home + "\\bin\\");
} else {
throw new Exception("Fehler: Unbekanntes Betriebssystem");
}
if (!path.exists()) {
throw new Exception(
"Fehler: Command kann nicht initialisiert werden, "
+ "da angegebener Syspath nicht existiert: "
+ path.getAbsolutePath());
}
if (!path.isDirectory()) {
throw new Exception(
"Fehler: Command kann nicht initialisiert werden, "
+ "da angegebener Syspath kein Verzeichnis ist: "
+ path.getAbsolutePath());
}
return path;
}
// gibt für Linux "" zurück
// gibt für Windows ".exe" zurück
// Fehler wenn Betriebssystem weder Windows noch Linux
protected String getExt() throws Exception {
if (OsValidator.isUnix()) {
return "";
} else if (OsValidator.isWindows()) {
return ".exe";
} else {
throw new Exception("Fehler: Unbekanntes Betriebssystem");
}
}
}
die Klasse ist abstrakt; ich kann aber nun für verschiedene Befehle einfache Klassen bauen; z.B. der gzip-Befehl:
Java:
public class Gzip extends Cmd {
private static final long serialVersionUID = 1L;
public Gzip(Runtime r) throws Exception {
super(r, "gzip");
}
public void zip(File f) throws Exception {
super.rmparams();
add(f.getAbsolutePath());
run(new File("."));
}
public void unzip() {
super.rmparams();
add("-d");
// ...
}
}
jetzt hab ich aber noch ein Problem mit meiner run-Methode.
Und zwar hab ich gelesen, dass die Methode Process.waitFor irgendwie nur dann funktioniert, wenn alle InputStreams eines externen Programms "abgearbeitet" wurden.
ich schreibe also statt
Java:
run(new File("."))
Java:
Process p = run(new File("."));
printStream(super.getStdin());
printStream(super.getStderr());
p.waitFor();
mfg, guni