Die WrapperManager.exec()
Funktion ist eine Alternative zu der Java-Runtime.exec() oder
Java-ProcessBuilder-Funktion, die den Nachteil hat, dass diese die fork()-Methode benutzt, die auf gewissen Plattformen sehr speicheraufwändig sein kann, um einen neuen Prozess zu erstellen.
Das Problem auf diesen Plattformen ist, dass innerhalb des Elternprozesses, der einen Nebenprozess starten möchte, fork() bewirkt, dass der Memory-Heap des Eltern- auf den Kindprozess nachgebildet wird.
Dies verdoppelt den beanspruchten Speicher innerhalb kurzer Zeit. Im Fall einer ziemlich großen Anwendung, die an die Speicherquoten des Systems heranreicht, kann es vorkommen, dass es nicht gelingt, auch nur eine kleine Anwendung zu starten, z.B. ls, oder dass der Speicher stark auf die Festplatte ausgelagert wird, was die Leistung drastisch reduziert.
Ein anderes Anliegen ist das Vereinfachen des Bindens und Lösens des Child-Prozesses vom Eltern-Prozess.
Wenn der Java-Prozess erwartet oder unerwartet endet, wird der Wrapper alle verbundenen Prozesse, die bisher noch nicht beendet wurden, beenden.
Für bessere Konsistenz wurde die WrapperManager.exec()-Funktion so ähnlich wie möglich zu der Runtime.exec() Funktion in Java implementiert.
Dieser Abschnitt wird Ihnen zeigen, wie ein Befehl ausgeführt werden kann.
Weitere Details werden Schritt für Schritt, während Sie diese Seite lesen, erklärt.
WrapperProcess p = WrapperManager.exec("ls -lisa");
Die oben genannte Zeile führt einen ls-Befehl
mit den Parametern -l, -i, -s, und -a aus, und weist dann ein WrapperProcess-Objekt zu, welches den Prozess darstellt.
Wenn der vollständige Pfad der Programm- (oder Skript-) Datei nicht weiter gegeben wurde,
werden das aktuelle Arbeitsverzeichnis wie auch die System-PATH-Verzeichnisse durchsucht; der erste gefundene Treffer wird ausgeführt.
Relative Adressmethoden, die vom Arbeitsverzeichnis aus starten, werden auch unterstützt.
NOTE
Der exec-Befehl akzeptiert den Befehl entweder als einen einfachen String oder als StringArray. Im Fall eines StringArrays befindet sich jeder Parameter in einem single-array-Feld.
E/A des Prozesses
Der erstellte Child-Prozess läuft im Hintergrund des Betriebssystems und ist gewöhnlich nicht sichtbar, solange es keine GUI-Anwendung ist.
Um mit dem Prozess zu kommunizieren, stellt die WrapperProcess-Klasse
drei Methoden zur Verfügung, um auf die Eingabe, die Ausgabe sowohl als auch auf die Fehlermeldungen des Prozesses zuzugreifen.
Es wird empfohlen, das zurückgegebene StreamObject mit einem BufferedReader zu verpacken.
Um den Reader zu öffnen, fahren Sie bitte wie folgt fort.
BufferedReader br = new BufferedReader( new InputStreamReader(p.getWrapperProcessInputStream()));
Dies erstellt den Reader von InputStream, in den der Child-Prozess seine Ausgabe hineinschreibt.
Um Daten vom ErrorStream zu lesen oder um Daten zum Child-Prozess zu schreiben,
fahren Sie bitte ähnlich zu folgendem Beispiel fort.
Sobald der Reader/Writer eingerichtet wurde, können wir Daten wie folgt lesen und schreiben:
Bitte beachten Sie, dass durch das Folgen dieses Beispiels alle Daten gelesen werden
bis der Child-Prozess den Stream auf seiner Seite schließt.
Konfiguration des Child-Prozesses
Wenn Sie einen Befehl ausführen, stellt der WrapperManager auch die Möglichkeit zur Verfügung den Child-Prozess zu konfigurieren. Dies wird getan, indem das WrapperProcessConfig-Objekt der exec()-Funktion übergeben wird. Der folgende Abschnitt erklärt die Konfigurationen, die von der WrapperProcessConfiguration erzeugt werden können.
Die setDetached()-Methode bestimmt, ob der Unterprozess eigenständig vom Wrapper ausgeführt wird.
Wenn der Prozess als eigenständig markiert ist, muss der Prozess nicht beendet werden, wenn der Wrapper sich beendet.
Falls nicht markiert, wird der Wrapper den Child-Prozess im Auge behalten und versuchen, den Prozess zu beenden, sobald der Eltern-Prozess enden sollte.
NOTE
Standardmäßig werden Prozesse, die eigenständig von ihrem Elternprozess sind, nicht gestartet.
Starttyp
Die setStartType()-Methode
bestimmt einen Starttyp, wie der Unterprozess vom Betriebssystem gestartet wird.
WARNING
Diese Eigenschaft hat keine Auswirkung auf Windows.
FORK_EXEC:
Dies ist der am meisten gebräuchliche Weg, wie in Linux/UNIX ein Child-Prozess gestartet wird.
Jedoch verursacht dieser Aufruf in ein paar Betriebssystemen (insbesondere Solaris), dass anfänglich der Speicher des Eltern- für den Child-Prozess verdoppelt wird.
Unter z/OS wird dieser Starttyp seit dem Anfangsrelease nicht unterstützt.
Auf HP-UX-Systemen wird der Wrapper automatisch umschalten, eher
vfork() als fork() nutzen.
VFORK_EXEC:
Die vfork()-Funktion unterscheidet sich von fork() nur darin, dass der Child-Prozess Programmcode und Daten mit dem Eltern-Prozess teilen kann.
Dies beschleunigt die Cloning-Aktivität wesentlich zu Lasten des Risikos der Integrität des Eltern-Prozesses, falls vfork() falsch benützt würde. Auf ein paar Systemen ist vfork identisch mit fork.
Unter z/OS wird dieser Starttyp seit dem Anfangsrelease nicht unterstützt.
POSIX_SPAWN:
Prozess wird erzeugt und verursacht keine Verdopplung des Speichers
(POSIX_SPAWN API).
?
Please check the English version for a more recent version of this text.
Dies ist nur unter Linux, Solaris (10+), AIX, z/OS und MacOS verfügbar.
DYNAMIC:
Der optimale Starttyp wird automatisch ausgewählt werden, je nachdem unter welchem Betriebssystem der Wrapper ausgeführt wird. Bitte beachten Sie, dass dies auch voraussetzt, dass das Ändern des Arbeitsverzeichnisses nicht unterstützt wird;
weil das Ändern des Arbeitsverzeichnisses bei Einsatz von POSIX_SPAWN nicht unterstützt wird.
Es gibt eine Test-Eigenschaft wrapper.child.allowCWDOnSpawn, die genutzt werden könnte, um zu erreichen, dass die Änderung des Arbeitsverzeichnisses möglich wird.
NOTE
Standardmäßig werden Prozesse mit dem Starttyp DYNAMIC gestartet.
Arbeitsverzeichnis
Die setWorkingDirectory()-Methode bestimmt das Arbeitsverzeichnis des Teilprozesses, oder "NULL", wenn der Subprozess das Arbeitsverzeichnis des gegenwärtigen Prozesses erben sollte.
Bitte beachten Sie, dass es beim Einsatz von POSIX_SPAWN nicht möglich ist, das Arbeitsverzeichnis direkt festzulegen. Ein vorgeschlagener Workaround wäre es, den Befehl, den Sie auszuführen, in ein Skript zu schreiben und eine Änderung des Verzeichnisses vor dem Ausführen des Befehls durchzuführen.
#!/bin/sh
chdir $1
shift
$*
NOTE
Wenn diese Eigenschaft nicht gesetzt ist, wird der Unterprozess das Arbeitsverzeichnis von seinem Eltern-Prozess erben.
wrapper.child.allowCWDOnSpawn-Eigenschaft
Die wrapper.child.allowCWDOnSpawn-Eigenschaft steuert,
ob der Wrapper das Arbeitsverzeichnis für den Starttyp POSIX_SPAWN und DYNAMIC versucht, zu ändern.
Diese Eigenschaft wird testweise zur Verfügung gestellt und könnte zu Problemen führen,
wenn der eingeschlossene Prozess den Java Native Interface (JNI) Code benutzt und
versuchen würde, einen Child-Prozess mittels POSIX_SPAWN zu erstellen.
Standardmäßig ist diese Eigenschaft auf FALSE eingestellt.
wrapper.child.allowCWDOnSpawn=TRUE
Festlegen der Umgebung
Die setEnvironment()-Methode legt die Umgebung des erstellten Unterprozesses fest.
Dieses Feld ist ein Array von Strings, jedes dieser hat Einstellungen zur Umgebungsvariablen im Format "name=value".
NOTE
Wenn diese Eigenschaft nicht gesetzt wurde, wird der Subprozess die Umgebung von seinem Eltern-Prozess erben.
Festlegen des Soft-Exit-Timeout
Seit der Wrapper Version 3.5.5 ist es auch möglich, ein Soft-Timeout für jeden Prozess individuell festzulegen; dabei wird der Wrapper dem Child-Prozess die Möglichkeit geben, sich selbst problemlos zu beenden. Währenddessen wird z.B. die java.lang.Process.destroy()-Methode stets ein Herunterfahren erzwingen, ohne dem Prozess die Möglichkeit zu geben, sich selbst zu beenden.
Vor der Wrapper Version 3.5.5 betrug der Timeout stets 5 Sek., um die Funktionalität konsistent zu halten, ist der Standardwert des Timeouts 5 Sek.
Der Timeout kann während der Erstellung des Child-Prozesses mittels der WrapperProcessConfig-Klasse festgelegt werden.
WrapperProcessConfig wpConfig = new WrapperProcessConfig().setSoftShutdownTimeout(10);
Die Konfiguration in dem obengenannten Beispiel sagt dem Wrapper, bis zu 10
Sekunden Child-Prozess, der mit dieser Konfiguration erstellt wurde, zu warten.
Wenn die Child-Prozesse sich nach 10 Sekunden nicht beenden, wird der Wrapper diese zwangsweise beenden.
Die folgenden Werte eines Timeouts sind möglich
>0:
Anzahl der Sekunden, die der Wrapper mindestens wartet, damit sich der Child-Prozess fehlerfrei beenden kann, bevor die Beendigung erzwungen wird.
0:
Der Wrapper erzwingt die Beendigung des Child-Prozesses sofort.
-1:
Der Wrapper erzwingt nie die Beendigung des Child-Prozesses,
sondern wartet zeitlich unbeschränkt auf den Child-Prozess, sich selbst zu beenden.
Erstellen eines Child-Prozesses für aktive User
Unter Windows kann WrapperProcessConfig.setCreateForActiveUser(boolean)
genutzt werden, um zu spezifizieren, ob die Kindprozesse eher in der aktuellen aktiven Sitzung gestartet werden sollen als
in der Sitzung, in der der Dienst läuft (eine Sitzung, in der der User SE_TCB_NAME
Privileg mit dem Betriebssystem hat). Unter Nicht-Windows-Plattformen oder gestartet im Konsolenmodus,
wird die Einstellung stillschweigend ignoriert.