From 33613a85afc4b1481367fbe92a17ee59c240250b Mon Sep 17 00:00:00 2001
From: Sven Eisenhauer
+Die Klasse Runtime
+des Pakets java.lang
+ermöglicht die Interaktion eines Java-Programms mit dem Bestandteil
+seiner Laufzeitumgebung, der dafür verantwortlich ist, Programme
+zu starten und zu beenden. Dabei ist es insbesondere möglich,
+mit seiner Hilfe externe Programme und Kommandos zu starten, mit ihnen
+zu kommunizieren und ihren Zustand zu überwachen.
+
+
+Objekte der Klasse Runtime
+dürfen vom Programm nicht selbst instanziert werden, sondern
+werden über die statische Methode getRuntime
+der Klasse Runtime
+beschafft:
+
+
+Um ein externes Programm zu starten, gibt es vier Methoden mit dem
+Namen exec:
+
+
+Das auszuführende Kommando kann wahlweise als Einzelstring angegeben
+werden (Kommandoname plus Argumente, getrennt durch Leerzeichen),
+oder die einzelnen Bestandteile können in einem Array übergeben
+werden. Das optionale zweite Argument envp
+ermöglicht es, dem zu startenden Programm eine Liste von Umgebungsvariablen
+zu übergeben.
+
+
+Das folgende Listing zeigt die einfachste Form der Anwendung von exec.
+Das Programm startet den notepad-Editor unter Windows:
+
+
+
+
+
+
+ Titel
+ Inhalt
+ Suchen
+ Index
+ DOC
+ Handbuch der Java-Programmierung, 5. Auflage
+
+ <<
+ <
+ >
+ >>
+ API
+ Kapitel 16 - Utility-Klassen I
+
+
+
+
+
+16.4 Die Klasse RunTime
+
+
+
+
+
+
+
+16.4.1 Grundlagen
+
+
+
+
+
+
+
+
+
+
+public static Runtime getRuntime()
+
+
+
+java.lang.Runtime
+
+
+
+
+
+
+
+
+
+public Process exec(String command)
+ throws IOException
+
+public Process exec(String command, String[] envp)
+ throws IOException
+
+public Process exec(String[] cmdarray)
+ throws IOException
+
+public Process exec(String[] cmdarray, String[] envp)
+ throws IOException
+
+
+
+java.lang.Runtime
+
+
+
+Listing 16.8: Starten von notepad.exe
+
+
+
+
+
+001 /* Listing1608.java */
+002
+003 import java.io.*;
+004
+005 public class Listing1608
+006 {
+007 public static void main(String[] args)
+008 {
+009 try {
+010 Runtime.getRuntime().exec("notepad");
+011 } catch (Exception e) {
+012 System.err.println(e.toString());
+013 }
+014 }
+015 }
+
+
+Listing1608.java
+
+
![]() |
+
+
+ +Leider macht die API-Dokumentation keine Angaben darüber, in +welchen Verzeichnissen nach dem auszuführenden Programm gesucht +wird. In den aktuellen Implementierungen des JDK scheint es jedoch +so zu sein, dass alle Verzeichnisse durchsucht werden, die in der +PATH-Umgebungsvariablen angegeben +sind. Unter Windows werden dabei aber offensichtlich nur Dateien mit +den Erweiterungen .com und .exe +automatisch berücksichtigt. Soll dagegen eine .bat-Datei +gestartet werden, muss die Erweiterung .bat +explizit angegeben werden. |
+
+
|
+![]() |
+
+exec +gibt ein Objekt des Typs Process +zurück, das dazu verwendet werden kann, mit dem gestarteten Programm +zu interagieren. Dazu definiert Process +folgende Methoden: +
+
+
++public abstract int waitFor() + throws InterruptedException + +public abstract int exitValue() + +public abstract void destroy() ++ + |
++java.lang.Process | +
+Ein Aufruf von waitFor +terminiert erst, wenn der zugehörige Prozess beendet wurde. Diese +Methode kann also dazu verwendet werden, auf das Ende des gestarteten +Programms zu warten. Das ist beispielsweise sinnvoll, um den Rückgabewert +des Programms auszuwerten oder um mit von ihm erzeugten oder veränderten +Daten zu arbeiten. Wird waitFor +nicht aufgerufen, kann auf aktuellen Betriebssystemen wohl davon ausgegangen +werden, dass das externe Programm parallel zum Java-Programm gestartet +wird und asynchron weiterläuft. Diese Aussage ist allerdings +mit Vorsicht zu genießen, denn spezifiziert ist dieses Verhalten +nicht. Im Zweifel hilft ausprobieren. + +
+Nach Ende des externen Programms kann mit exitValue +sein Rückgabewert abgefragt werden. Dieser gibt bei vielen Programmen +an, ob es fehlerfrei ausgeführt werden konnte oder nicht. Das +ist aber nicht zwangsläufig so. Unter Windows wird insbesondere +beim Aufruf von Batch-Dateien und bei der expliziten Ausführung +eines Kommandos in einen eigenen Kommandointerpreter der Rückgabewert +nicht weitergegeben. In diesem Fall liefert exitValue +immer den Wert 0. Wird exitValue +aufgerufen, wenn der Prozess noch läuft, gibt es eine IllegalThreadStateException. + +
+Die Methode destroy +dient dazu, das externe Programm abzubrechen. Es handelt sich hierbei +nicht um das normale Beenden eines Programms, sondern um einen harten +Abbruch. Ungesicherte Änderungen gehen also verloren und es können +Inkonsistenzen in manipulierten Daten entstehen. + +
+Das Process-Objekt +bietet zusätzlich die Möglichkeit, die Standardein- und +-ausgabe des externen Kommandos umzuleiten und aus dem eigenen Programm +heraus anzusprechen: +
+
+
++public OutputStream getOutputStream() + +public abstract InputStream getInputStream() + +public abstract InputStream getErrorStream() ++ + |
++java.lang.Process | +
+getInputStream +und getErrorStream +liefern einen InputStream, +mit dem die Ausgaben des Prozesses auf Standardausgabe und Standardfehler +gelesen werden können. Von getOutputStream +wird ein OutputStream +zur Verfügung gestellt, mit dem Daten in die Standardeingabe +des Prozesses geschrieben werden können. Auf diese Weise lassen +sich Programme, die über Standardein- und -ausgabe kommunizieren, +fernsteuern bzw. fernabfragen. + +
+Das folgende Programm fasst die wichtigsten Möglichkeiten zusammen: + + +
+
+
+
+001 /* RunCommand.java */
+002
+003 import java.io.*;
+004
+005 public class RunCommand
+006 {
+007 static final int MODE_UNCONNECTED = 0;
+008 static final int MODE_WAITFOR = 1;
+009 static final int MODE_CATCHOUTPUT = 2;
+010
+011 private static void runCommand(String cmd, int mode)
+012 throws IOException
+013 {
+014 Runtime rt = Runtime.getRuntime();
+015 System.out.println("Running " + cmd);
+016 Process pr = rt.exec(cmd);
+017 if (mode == MODE_WAITFOR) {
+018 System.out.println("waiting for termination");
+019 try {
+020 pr.waitFor();
+021 } catch (InterruptedException e) {
+022 }
+023 } else if (mode == MODE_CATCHOUTPUT) {
+024 System.out.println("catching output");
+025 BufferedReader procout = new BufferedReader(
+026 new InputStreamReader(pr.getInputStream())
+027 );
+028 String line;
+029 while ((line = procout.readLine()) != null) {
+030 System.out.println(" OUT> " + line);
+031 }
+032 }
+033 try {
+034 System.out.println(
+035 "done, return value is " + pr.exitValue()
+036 );
+037 } catch (IllegalThreadStateException e) {
+038 System.out.println(
+039 "ok, process is running asynchronously"
+040 );
+041 }
+042 }
+043
+044 private static void runShellCommand(String cmd, int mode)
+045 throws IOException
+046 {
+047 String prefix = "";
+048 String osName = System.getProperty("os.name");
+049 osName = osName.toLowerCase();
+050 if (osName.indexOf("windows") != -1) {
+051 if (osName.indexOf("95") != -1) {
+052 prefix = "command.com /c ";
+053 } else if (osName.indexOf("98") != -1) {
+054 prefix = "command.com /c ";
+055 }
+056 }
+057 if (prefix.length() <= 0) {
+058 System.out.println(
+059 "unknown OS: don\'t know how to invoke shell"
+060 );
+061 } else {
+062 runCommand(prefix + cmd, mode);
+063 }
+064 }
+065
+066 public static void main(String[] args)
+067 {
+068 try {
+069 if (args.length <= 0) {
+070 System.out.println(
+071 "Usage: java RunCommand [-shell] " +
+072 "[-waitfor|-catchoutput] <command>"
+073 );
+074 System.exit(1);
+075 }
+076 boolean shell = false;
+077 int mode = MODE_UNCONNECTED;
+078 String cmd = "";
+079 for (int i = 0; i < args.length; ++i) {
+080 if (args[i].startsWith("-")) {
+081 if (args[i].equals("-shell")) {
+082 shell = true;
+083 } else if (args[i].equals("-waitfor")) {
+084 mode = MODE_WAITFOR;
+085 } else if (args[i].equals("-catchoutput")) {
+086 mode = MODE_CATCHOUTPUT;
+087 }
+088 } else {
+089 cmd = args[i];
+090 }
+091 }
+092 if (shell) {
+093 runShellCommand(cmd, mode);
+094 } else {
+095 runCommand(cmd, mode);
+096 }
+097 } catch (Exception e) {
+098 System.err.println(e.toString());
+099 }
+100 }
+101 }
+
+ |
++RunCommand.java | +
+Das Hauptprogramm erwartet das zu startende Programm und seine Parameter +als Argumente. Wird die Option »-catchoutput« angegeben, +liest das Programm die Ausgaben des gestarteten Programms und gibt +sie auf seiner eigenen Standardausgabe aus. Wird »-waitfor« +angegeben, wartet das Programm auf das Ende des gestarteten Programms, +ohne dessen Ausgaben anzuzeigen. In beiden Fällen wird schließlich +der Rückgabewert des Programms ausgegeben. Durch Angabe der Option +»-shell« kann das externe Programm mit einem separaten Kommandointerpreter +gestartet werden. Das ist beispielsweise nützlich, um Shell-Kommandos +auszuführen, die nicht als eigenständige Programmdateien +existieren. + +
+Der folgende Aufruf verwendet das Beispielprogramm, um das interne
+MS-DOS-Kommando »set« auszuführen (es gibt die Inhalte
+aller Umgebungsvariablen aus):
+
+
+java RunCommand -shell -catchoutput set
+
+
+
+
+Seine Ausgabe könnte etwa so aussehen:
+
+
+Running command.com /c set
+catching output
+ OUT> winbootdir=C:\WINDOWS
+ OUT> COMSPEC=C:\COMMAND.COM
+ OUT> TEMP=C:\tmp
+ OUT> TMP=c:\tmp
+ OUT> USER=guido
+ OUT> windir=C:\WINDOWS
+ OUT> PATH=C:\JDK1.6\BIN;C:\WINDOWS;C:\WINDOWS\COMMAND;
+ OUT> CLASSPATH=.;c:\arc\prog\java
+ OUT> PROMPT=$p--$g
+done, return value is 0
+
+
+
+
![]() |
+
+
+ +Die Übergabe des Programms an einen separaten Kommandointerpreter +erfolgt in der Methode runShellCommand +ab Zeile 044. Wie ein derartiger +Aufruf ausgeführt wird, ist natürlich betriebssystem- und +konfigurationsabhängig. Das Beispielprogramm versucht, einige +brauchbare Varianten für gängige Betriebssysteme vorzudefinieren. +Weitere können leicht hinzugefügt werden. |
+
+
|
+![]() |
+
+
![]() |
+![]() |
+
+
+ +Während des Tests von RunCommand +gab es mitunter Schwierigkeiten beim Ausführen interner DOS-Programme +unter Windows 95 und 98. Während sich beispielsweise das Kommando +set problemlos aufrufen ließ, +gab es beim Aufruf von dir Hänger, +nach denen die MS-DOS-Task hart abgebrochen werden mußte. Die +JDK Bug Database listet eine ganze Reihe von Problemen in Zusammenhang +mit dem Aufruf von 16-Bit-Programmen unter Windows 95 oder 98 auf. +Sie rühren unter anderem daher, dass die Ein- und Ausgabepuffer +der DOS-Programme so klein sind, dass die Programme mitunter schon +blockieren, bevor die aufrufende Applikation die Chance hatte, eine +Verbindung zu ihnen herzustellen. Echte Workarounds für diese +Probleme scheinen nicht bekannt zu sein. Beim Aufruf von 32-Bit-Programmen +treten die Probleme offenbar nicht auf. |
+
+
|
+![]() |
+
| Titel + | Inhalt + | Suchen + | Index + | DOC + | Handbuch der Java-Programmierung, 5. Auflage, Addison +Wesley, Version 5.0.1 + |
| << + | < + | > + | >> + | API + | © 1998, 2007 Guido Krüger & Thomas +Stark, http://www.javabuch.de + |