# Process.wait() auf Folgeprozesse mitwarten



## guni (29. Jan 2009)

hallo,

mit Runtime.exec starte ich in java eine Setup.exe und rufe dann für den Prozess die waitFor-Methode auf. Sinn der ganzen Sache ist, dass mein Java Programm erst weitermachen soll, wenn das setup durch ist.

das Problem ist folgendes:
Setup.exe ruft eine Installer.exe auf. damit endet der Prozess
Installer.exe führt ein paar unzips durch und macht warscheinlich sonst noch ein paar Dinge; entpackt jedenfalls eine komplette JRE und eine Menge Installfiles in einen temporären Ordner. dann ruft er einen javaw-prozess mit einer unmenge an parametern auf und endet damit.
javaw ist vermutlich der prozess, der dann bis zum ende des setups erhalten bleibt.

mein problem ist jedenfalls, dass die setup.exe, die ich urspünglich aufrufe (und auf die ich auch meinen waitFor() ausführe) gleich wieder weg ist.
wie kann ich meinem programm sagen, dass es auch auf die forked processes warten soll?!

mfg, guni


----------



## HoaX (29. Jan 2009)

garnicht.


----------



## guni (29. Jan 2009)

gibts einen anderen ansatz, das programm aus java heraus aufzurufen und zu warten, bis es fertig ist?


----------



## Beni (29. Jan 2009)

Vielleicht kannst du zuerst die Konsole (cmd.exe, bash, terminal, ... was immer fuer ein System du hast) aufrufen, und dann ueber die Konsole diese Befehle abschicken? Manchmal ist die Konsole etwas intelligenter als exec.


----------



## guni (29. Jan 2009)

ja ... die idee find ich gar nicht schlecht. mein problem is nur: der installer wird trotzdem in einem eigenen prozess gestartet und damit wüsste dann auch die console nicht, wann sie wieder zugehen soll :-(


----------



## guni (29. Jan 2009)

.. ich hätte da noch einen etwas primitiven Ansatz (gefällt mir gar nicht!) aber zur Not könnte man ihn hernehmen:


```
public void instructSomeWork(String[] foreigner) throws IOException, InterruptedException {
		BufferedReader in = new BufferedReader( new InputStreamReader(System.in) );
		Runtime.getRuntime().exec(foreigner);
		System.out.println("Please press any key when the external process has finished ... ");
		in.readLine();
		in.close();
	}
```

mein Problem ist:

Exception in thread "main" java.io.IOException: Stream closed
	at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:145)
	at java.io.BufferedInputStream.read(BufferedInputStream.java:308)
	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
	at java.io.InputStreamReader.read(InputStreamReader.java:167)
	at java.io.BufferedReader.fill(BufferedReader.java:136)
	at java.io.BufferedReader.readLine(BufferedReader.java:299)
	at java.io.BufferedReader.readLine(BufferedReader.java:362)
	at com.eurofunk.lesalaire.setup.Layabout.instructSomeWork(Layabout.java:29)
	at com.eurofunk.lesalaire.setup.Reviewer.main(Reviewer.java:64)

warum schließt das Programm meinen Stream? Zu der Zeile sollte es doch erst kommen, wenn ich eine Taste gedrückt habe!


----------



## Leroy42 (29. Jan 2009)

guni hat gesagt.:
			
		

> warum schließt das Programm meinen Stream? Zu der Zeile sollte es doch erst kommen, wenn ich eine Taste gedrückt habe!



Weil du (eventuell) noch vorhrige Zeilenende-Zeichen (die beim letzten read nicht
eingelesen wurden) durch das readLine einliest *und* dann gleich den Stream closed.


----------



## guni (30. Jan 2009)

aha ...
versteh ich jetzt zwar nicht ganz, aber: wie kann ich es verhindern?!


----------



## SlaterB (30. Jan 2009)

vor dem Starten des Prozesses alles noch offene aus dem BufferedReader auslesen,
reader.availabe() hilft vielleicht dabei

----------

in einem Java-Programm sollte es maximal genau einen BufferedReader auf System.in geben,
kannst du als statische Variable ablegen oder so

den System.in-Stream zu closen() scheint mir keinen Sinn zu machen


----------



## guni (30. Jan 2009)

ok ... werde das so umbauen. danke!


----------



## guni (30. Jan 2009)

@Slater B: 
danke für deine Antwort.
habe meinen BufferedReader global deklariert und in einen Singelton ausgelagert   

der gedanke, auf den prozess zu warten, lässt mich trotzdem nicht los. 
nachdem es mein setup auch unter linux gibt, werde ich mir mal im detail anschauen, welche schritte der installer durchführt, bevor er den eigentlichen installationsprozess ausführt! vielleicht kann ich es dann darauf beschränken und doch einen waitfor einbauen.
jedenfalls danke mal soweit ...

mfg, guni


----------



## guni (2. Feb 2009)

juhuu! ich habe nun doch eine elegante Möglichkeit gefunden, mein problem zu lösen.
das ganze funktioniert deswegen, weil jeder Prozess nur einen einzigen Folgeprozess startet und der Folgeprozess dann interessanterweise die Id jenes Prozesses, der ihn gestartet hat, als Parent ID erhält (obwohl der andere Prozess dann verschwindet).
wie auch immer: das ganze könnte so irgendwie aussehen:


```
method ... {
   Process p = Runtime.exec(Programm);
   wait(p.pid);
   ...
}

public void wait(long parent) {
   processlist = getProcessSnapshot(); // CreateToolhelp32Snapshot
   begin: iterate through process list
      if currentprocess.parent == parent {
         currentprocess.waitFor();
         wait(currentprocess.pid);
      }
   end: iterate through process list
}
```

das problem ist: ich müsste irgendwie mit jni auf die Snapshotfunktion zugreifen und einen Iterate machen!
kann mir da irgendwer helfen?

lg, guni


----------



## Spacerat (2. Feb 2009)

Mit "exec" startet man System-Prozesse über die man keinerlei Einfluss hat. Man kann nur auf dessen Beendigung warten oder dessen Ausgaben lesen. Startet ein solcher Prozess einen weiteren, bekommt die JVM davon auch nichts mit, weil diese ihn eben nicht gestartet hat. Unter Linux kann man die "PID"-Files laufender Prozesse suchen, unter Windows kann man den Taskmanager möglicherweise dazu veranlassen, sich die laufenden Prozesse in der Konsole ausgeben zu lassen (wie z.B. in der Windows2000-Reparatur-Konsole, k.A. ob das noch geht). In einem Java-Thread fragt man das Vorhandensein der betreffenden Einträge ab und lässt sein Programm warten, bis dieser Thread beendet wurde. Ist natürlich alles andere als Systemunabhängig.

mfg Spacerat


----------



## guni (2. Feb 2009)

ja ... ein systemunabhängiges programm zu schreiben habe ich schon aufgegeben. 
ich habe auch schon eine möglichkeit gefunden, die PID eines child prozesses zu finden. 
fragt sich jetzt nur mehr, wie ich bei gegebener PID ein java Process-Objekt erzeuge, von dem ich dann die wait-Methode aufrufen kann. also irgendwas wie 

new Process(PID) ... 

geht das?!

mfg, guni


----------



## Spacerat (2. Feb 2009)

Nein...
Baue dir einen Thread, welcher ständig abfragt, ob diese Pid noch vorhanden ist und sich bei nichtmehr Vorhandensein beendet. Dein Hauptprogramm muss nun nur noch solange warten, bis sich dieser Thread beendet hat.

mfg Spacerat


----------



## guni (3. Feb 2009)

hallo spacerat.

mir fehlt dazu ein bisschen der ansatz. ich bin relativ neu in java und threads sind mir generell ein bisschen ein mysterium.
ganz verstehe ich deinen ansatz auch nicht. ok; ich verstehe, dass mein programm nicht auf fremde prozesse, sondern auf den eigenen Thread warten soll. ABER: der thread hat ja gar keine wait-methode! wie mache ich das dann? ausserdem: ist es nicht unschön, den thread in einem zeitintervall abfragen zu lassen, ob ein kindprozess existiert?
genaugenommen müsste der thread immer erst am ende eines prozesses warten!
das würde dann so aussehen:


----------



## guni (3. Feb 2009)

ok ... mir fehlt einfach jeder ansatz! ist da irgendwer, der mir mit ein paar code-ideen aushelfen könnte?!
danke! guni


----------



## guni (3. Feb 2009)

hat da einfach niemand eine idee dazu?


----------



## Developer_X (3. Feb 2009)

System.sleep(0);


----------



## guni (3. Feb 2009)

hä? System hat bei mir gar keine sleep-Methode. Und selbst wenn es eine hätte: was würde mir das bringen?!


----------



## HoaX (3. Feb 2009)

Spacerat hat gesagt.:
			
		

> Nein...
> Baue dir einen Thread, welcher ständig abfragt, ob diese Pid noch vorhanden ist und sich bei nichtmehr Vorhandensein beendet. Dein Hauptprogramm muss nun nur noch solange warten, bis sich dieser Thread beendet hat.



Damit sagt er das gleiche wie du selbst schreibst: Pollen ob der (Unter-)Prozess noch läuft oder nicht. Hat nichts mit auf-einen-Java-Thread-warten zu tun.

Den Aufruf der nativen Methode um den Snapshot zu erstellen machst du am einfachsten per JNA: https://jna.dev.java.net/
Beispiele sind auf der Seite zu genüge, ansonsten frag nochmal speziell nach.



			
				Developer_X hat gesagt.:
			
		

> System.sleep(0);


Das kannste getrost ignorieren, macht keinen Sinn


----------



## guni (3. Feb 2009)

hallo hoax,

jna ist ein tooles stichwort.
mein derzeitiger ansatz war den output von einem ps / wmic / tasklist ... zu parsen ...

na ja - mein problem ist: ich brauch ja trotzdem einen Java-Thread-Wait Funktion ...
weil ich muss ja wissen, zu welchen zeitpunkten ich meinen poll durchführen muss, oder?!

mfg, guni


----------



## Spacerat (3. Feb 2009)

So... wieder da...
Ich hatte mir das shematisch so vorgestellt...

```
class Main
{
    Process p = Runtime.getRuntime().exec("externes programm");
    Thread w = new Thread() {
        public void run()
        {
            try {
                // ProzessPids pollen
                while(ProzessPIDs_vorhanden) {
                    // erneut pollen
                    Thread.sleep(500);
                }
            } catch(InterruptedException e) {
                // nothing
            }
        }
    };
    w.start();
    synchronized(w) {
        try{
            w.join();
        } catch(InterruptedException e) {
            // nothing
        }
    }
    //Programm fortsetzen
}
```

mfg Spacerat


----------



## guni (6. Feb 2009)

hmm ... danke für den ansatz. sysnchronized versteh ich sowieso nicht wirklich; das hab ich mir schon in dem einen oder anderen forum durchgelesen; wirkt aber für mich ziemlich kompliziert (irgendein lock, damit nicht 2 threads auf die gleichen variablen zugreifen oder so ...)
was mich aber mehr stört is ein polling mit sleep! kann ich meine abfrage nicht einfach dann durchführen wenn der beobachtete prozess beendet wird?!


----------



## guni (9. Feb 2009)

@Spacerat ...
ich versteh zwar dein synchronized noch immer nicht ganz aber was solls. Vom Prinzip her schau ich jetzt so halbwegs dahinter ...
trotzdem bringe ich mein Programm noch nicht zum Laufen.
hier nochmal beschrieben, was es können soll:

ich habe mein java-programm rennen.
ich starte irgendwann eine setup.exe mit Runtime.execute
ich ermittle die PID von setup.exe (meine setup.exe forked ein paar prozesse und endet dann)
ich starte einen Thread, dem ich die aktuelle PID übergebe 
mein MainThread, muss warten, bis Thread beendet ist

thread sieht so aus:

nichtsgefunden = falsch

wiederhole bis nichtsgefunden = wahr
   schlafe (t)
   ermittle process snapshot   
   nichtsgefunden = wahr
   durchlaufe process snapshot
      wenn aktuellerProzess == parameterpid dann
         nichtsgefunden = falsch
      wenn aktuellerProzess.parentid == parameterpid
         nichtsgefunden = falsch
         parameterpid = aktuellerProzess.pid

Hauptprogramm setzt fort, wenn der Thread keinen Kindprozess mehr hat ...


----------



## HoaX (9. Feb 2009)

den neuen thread kannste vergessen. wenn ich einen neuen thread starte und als nächstes warte bis der fertig ist, dann brauch ich die nebenläufigkeit nicht, also kann ich ihn gleich weglassen und die while-schleife direkt aufrufen.

du bauchst quasi:
	
	
	
	





```
pid = starteSetup();
while(isPidRunning(pid)) {
    Thread.sleep(1000);
}
```


----------



## Spacerat (9. Feb 2009)

Das synchronized ist notwendig, damit der Hauptthread den Monitor auf den Poller-Thread bekommt, damit "join()" aufgerufen werden kann. Mit "join()" wartet der Hauptthread auf die beendigung des Poller-Threads.
alles was bei dir unterhalb von "mein thread sieht so aus" steht, müsstest du in die while-Schleife meines Poll-Threads bringen. Nun kenn ich weder den Code deines Threads, noch das API mit welchem du an die Parent-IDs kommst (die Process-Klasse aus java.lang verwendest du jedenfalls nicht). "while(ProcessPIDs_vorhanden)" sollte aber Aussagekräftig genug sein. Statt "Thread.sleep()" kann auch "Thread.yield()" verwendet werden.

Was für eine Abfrage willst du eigentlich noch durchführen? Du musst keineswegs wissen, wann du diesen Poll durführen musst, oder besser, dieser Poll muss ständig das Vorhandensein der ProcessPIDs abfragen. Deswegen ist da ja auch eine "while"Schleife. Ich habe da mal zwei Kommentare eingefügt.

@Hoax: Villeicht hilfts, wenn du vorm Antworten das Thema durchliest. Dein Ansatz ist längst Geschichte.  . Was mir womöglich entgangen ist, ist die Tatsache, das es so villeicht mit "JNA" funzt. Bin ich allerdings auch kein Fan von.

mfg Spacerat


----------



## Ariol (9. Feb 2009)

Versuch mal ob das bei dir klappt...

Unter Linux geht das:


```
Process p = Runtime.getRuntime().exec("gnome-terminal");
		Field f = p.getClass().getDeclaredField("pid");
		f.setAccessible(true);
		System.out.println( f.get( p ) );
```


----------



## guni (17. Feb 2009)

@Spacerat
> Villeicht hilfts, wenn du vorm Antworten das Thema durchliest. 
> Dein Ansatz ist längst Geschichte.

nein. das denke ich nicht. bis jetzt ist eine Schleife im Hauptprogramm noch nie zur Debatte gestanden. Ich finde diese Art von Abfrage aber trotzdem unschön.
Warum soll ich meinen Programmblock alle paar Millisekunden neu laufen lassen. Ich will ihn doch erst dann durchführen, wenn der Prozess wirklich beendet ist! Kann ich mich von Beendigung eines Prozesses nicht benachrichtigen lassen? Sind die Dinger so unhöflich, dass sie sich im System nicht verabschieden, bevor sie gehn ;-)

@Ariol:
hmm ... mit deinem Ansatz könnte ich theoretisch die ID eines laufenden Prozesses ermitteln. Mein Problem ist aber, dass ich zu einem bestimmten Zeitpunk alle laufenden Prozesse nach ihrem Parentprocess "fragen möchte". Und da hilft mir dein Ansatz eigentlich auch nicht weiter, oder?!


----------



## thE_29 (17. Feb 2009)

Sowas hier?
http://www.java-forum.org.server659-han.de-nserver.de/showthread.php?t=34052&highlight=prozess


----------

