Programiranje

Ko Runtime.exec () ne bo

Kot del jezika Java je java.lang paket implicitno uvožen v vsak program Java. Pasti tega paketa se pogosto pojavijo in prizadenejo večino programerjev. Ta mesec bom razpravljal o pasti, ki se skrivajo v Runtime.exec () metoda.

Zamka 4: Ko Runtime.exec () ne bo

Razred java.lang.Trajanje vsebuje statično metodo, imenovano getRuntime (), ki pridobi trenutno Java Runtime Environment. To je edini način za sklicevanje na Izvajanje predmet. S tem sklicem lahko zaženete zunanje programe tako, da prikličete Izvajanje razredov exec () metoda. Razvijalci to metodo pogosto pokličejo, da zaženejo brskalnik za prikaz strani s pomočjo v HTML-ju.

Obstajajo štiri preobremenjene različice exec () ukaz:

  • javni proces exec (ukaz String);
  • javni postopek exec (String [] cmdArray);
  • javni proces exec (ukaz String, String [] envp);
  • javni postopek exec (String [] cmdArray, String [] envp);

Za vsako od teh metod se ukaz - in morda niz argumentov - posreduje klicu funkcije, specifičnemu za operacijski sistem. To nato ustvari postopek, specifičen za operacijski sistem (delujoči program) s sklicevanjem na Proces razred vrnjen v Java VM. The Proces razred je abstrakten razred, ker je poseben podrazred Proces obstaja za vsak operacijski sistem.

V te metode lahko prenesete tri možne vhodne parametre:

  1. En sam niz, ki predstavlja program za izvajanje in vse argumente tega programa
  2. Niz nizov, ki ločujejo program od njegovih argumentov
  3. Niz spremenljivk okolja

V obrazec vnesite spremenljivke okolja ime = vrednost. Če uporabljate različico exec () z enim nizom za program in njegove argumente, upoštevajte, da je niz razčlenjen z uporabo presledka kot ločila prek StringTokenizer razred.

Spotakanje v IllegalThreadStateException

Prva pasti v zvezi z Runtime.exec () ali je IllegalThreadStateException. Najpogostejši prvi test API-ja je kodiranje njegovih najbolj očitnih metod. Na primer za izvedbo procesa, ki je zunaj Java VM, uporabimo exec () metoda. Za prikaz vrednosti, ki jo vrne zunanji postopek, uporabimo datoteko exitValue () metoda na Proces razred. V našem prvem primeru bomo poskusili izvršiti prevajalnik Java (javac.exe):

Seznam 4.1 BadExecJavac.java

uvoz java.util. *; uvoz java.io. *; javni razred BadExecJavac {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Proces proc = rt.exec ("javac"); int exitVal = proc.exitValue (); System.out.println ("Process outputValue:" + exitVal); } catch (Throwable t) {t.printStackTrace (); }}} 

Tek BadExecJavac proizvaja:

E: \ classes \ com \ javaworld \ jpitfalls \ article2> java BadExecJavac java.lang.IllegalThreadStateException: postopek ni potekel na java.lang.Win32Process.exitValue (Native Method) pri BadExecJavac.main (BadExejac. 

Če zunanji postopek še ni zaključen, se exitValue () metoda vrže IllegalThreadStateException; zato ta program ni uspel. Medtem ko je v dokumentaciji navedeno to dejstvo, zakaj ta metoda ne more počakati, da bo lahko dala veljaven odgovor?

Podrobnejši pregled metod, ki so na voljo v Proces razred razkriva a počakaj na() metoda, ki počne točno to. Pravzaprav, počakaj na() vrne tudi izhodno vrednost, kar pomeni, da je ne bi uporabili exitValue () in počakaj na() v povezavi med seboj, ampak bi raje izbrali eno ali drugo. Edini možni čas, ki bi ga uporabili exitValue () namesto počakaj na() bi bilo, ko ne želite, da vaš program blokira čakanje v zunanjem procesu, ki se morda nikoli ne konča. Namesto uporabe počakaj na() metode, bi raje posredoval logični parameter z imenom počakaj na v exitValue () metoda za določitev, ali naj trenutna nit počaka. Logična vrednost bi bila bolj koristna, ker exitValue () je ustreznejše ime za to metodo in ni nujno, da dve metodi izvajata isto funkcijo pod različnimi pogoji. Takšna preprosta diskriminacija pogojev je področje vhodnega parametra.

Zato se, da se izognete tej pasti, bodisi ulovite IllegalThreadStateException ali počakajte, da se postopek zaključi.

Zdaj odpravimo težavo v seznamu 4.1 in počakamo, da se postopek zaključi. Na seznamu 4.2 program znova poskusi izvršiti javac.exe in nato počaka, da se zunanji postopek zaključi:

Seznam 4.2 BadExecJavac2.java

uvoz java.util. *; uvoz java.io. *; javni razred BadExecJavac2 {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Proces proc = rt.exec ("javac"); int exitVal = proc.waitFor (); System.out.println ("Process outputValue:" + exitVal); } catch (Throwable t) {t.printStackTrace (); }}} 

Na žalost, tek BadExecJavac2 ne proizvaja nobenih rezultatov. Program visi in se nikoli ne dokonča. Zakaj javac postopek nikoli zaključen?

Zakaj visi Runtime.exec ()

Jdokova dokumentacija Javadoc ponuja odgovor na to vprašanje:

Ker nekatere izvorne platforme zagotavljajo le omejeno velikost medpomnilnika za standardne vhodne in izhodne tokove, lahko neuspešno pisanje vhodnega toka ali branje izhodnega toka podprocesa povzroči, da podproces blokira in celo zastoji.

Ali gre zgolj za to, da programerji ne berejo dokumentacije, kot je razvidno iz pogosto citiranega nasveta: preberite ta priročnik (RTFM)? Odgovor je delno pritrdilen. V tem primeru bi branje Javadoca prišlo na pol poti; pojasnjuje, da morate tokove obravnavati v zunanjem procesu, ne pove pa vam, kako.

Tu je v igri še ena spremenljivka, kar je razvidno iz številnih vprašanj programerjev in napačnih predstav v zvezi s tem API-jem v novičarskih skupinah: čeprav Runtime.exec () in Process API-ji se zdijo zelo preprosti, ta preprostost zavaja, ker je preprosta ali očitna uporaba API-ja nagnjena k napakam. Lekcija tukaj za oblikovalca API je, da si preproste API-je rezervira za enostavne operacije. Operacije, ki so nagnjene k zapletenosti in odvisnostim od platforme, bi morale natančno odražati domeno. Možno je, da se abstrakcija prenese predaleč. The JConfig knjižnica ponuja primer popolnejšega API-ja za obdelavo datotek in procesnih operacij (za več informacij glejte Viri spodaj).

Zdaj pa sledimo dokumentaciji JDK in ravnajmo z izhodnimi podatki datoteke javac proces. Ko tečeš javac brez kakršnih koli argumentov ustvari nabor izjav o uporabi, ki opisujejo, kako zagnati program in pomen vseh razpoložljivih možnosti programa. Vedoč, da gre za to stderr tok, lahko enostavno napišete program za izčrpavanje tega toka, preden počakate, da se postopek konča. Seznam 4.3 dokonča to nalogo. Čeprav bo ta pristop deloval, ni dobra splošna rešitev. Tako je poimenovan program iz 4.3 MediocreExecJavac; ponuja le povprečno rešitev. Boljša rešitev bi izpraznila standardni tok napak in standardni izhodni tok. In najboljša rešitev bi te tokove istočasno izpraznila (to bom pokazala kasneje).

Seznam 4.3 MediocreExecJavac.java

uvoz java.util. *; uvoz java.io. *; javni razred MediocreExecJavac {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Proces proc = rt.exec ("javac"); InputStream stderr = proc.getErrorStream (); InputStreamReader isr = nov InputStreamReader (stderr); BufferedReader br = novo BufferedReader (isr); Vrstica niza = null; System.out.println (""); while ((vrstica = br.readLine ())! = null) System.out.println (vrstica); System.out.println (""); int exitVal = proc.waitFor (); System.out.println ("Process outputValue:" + exitVal); } catch (Throwable t) {t.printStackTrace (); }}} 

Tek MediocreExecJavac ustvarja:

E: \ classes \ com \ javaworld \ jpitfalls \ article2> java MediocreExecJavac Uporaba: javac, kjer je vključeno: -g Ustvari vse informacije o odpravljanju napak -g: nič Ustvari nobenih informacij o odpravljanju napak -g: {lines, vars, source} Ustvari le nekatere informacije o odpravljanju napak -O Optimiziraj; lahko ovira odpravljanje napak ali povečuje datoteke razredov -nowarn Ustvari brez opozoril -verbose Izhodna sporočila o tem, kaj počne prevajalnik -prenehanje Izhodne lokacije virov, kjer se uporabljajo zastareli API-ji -classpath Določite, kje najti datoteke uporabniškega razreda -sourcepath Določite, kje najti vhodne izvorne datoteke -bootclasspath Preglasite lokacijo datotek razreda zagonskega trapa -extdirs Preglasite lokacijo nameščenih razširitev -d Določite, kam naj se postavijo generirane datoteke razreda -encoding Določite kodiranje znakov, ki se uporabljajo v izvornih datotekah -cilj Ustvari datoteke razredov za določeno različico VM Process outputValue: 2 

Torej, MediocreExecJavac deluje in ustvari izhodno vrednost 2. Običajno je izhodna vrednost 0 kaže na uspeh; katera koli drugačna vrednost pomeni napako. Pomen teh izhodnih vrednosti je odvisen od določenega operacijskega sistema. Napaka Win32 z vrednostjo 2 je napaka "datoteke ni mogoče najti". To je smiselno, saj javac pričakuje, da bomo sledili programu z datoteko izvorne kode za prevajanje.

Tako, da bi se izognili drugi pasti - visi za vedno Runtime.exec () - če program, ki ga zaženete, proizvaja izhodne podatke ali pričakuje vhodne podatke, poskrbite, da boste obdelali vhodne in izhodne tokove.

Ob predpostavki, da je ukaz izvršljiv program

Pod operacijskim sistemom Windows številni novi programerji naletijo nanje Runtime.exec () ko ga poskušate uporabiti za neizvršljive ukaze, kot je dir in kopirati. Kasneje naletijo na Runtime.exec ()je tretja pasta. Seznam 4.4 dokazuje točno to:

Seznam 4.4 BadExecWinDir.java

uvoz java.util. *; uvoz java.io. *; javni razred BadExecWinDir {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Proces proc = rt.exec ("dir"); InputStream stdin = proc.getInputStream (); InputStreamReader isr = nov InputStreamReader (stdin); BufferedReader br = novo BufferedReader (isr); Vrstica niza = null; System.out.println (""); while ((line = br.readLine ())! = null) System.out.println (line); System.out.println (""); int exitVal = proc.waitFor (); System.out.println ("Process outputValue:" + exitVal); } catch (Throwable t) {t.printStackTrace (); }}} 

Tek BadExecWinDir proizvaja:

E: \ classes \ com \ javaworld \ jpitfalls \ article2> java BadExecWinDir java.io.IOException: CreateProcess: dir error = 2 at java.lang.Win32Process.create (Native Method) at java.lang.Win32Process. (Unknown Source) at java.lang.Runtime.execInternal (Native Method) at java.lang.Runtime.exec (Unknown Source) at java.lang.Runtime.exec (Unknown Source) at java.lang.Runtime.exec (Unknown Source) at java .lang.Runtime.exec (neznan vir) na BadExecWinDir.main (BadExecWinDir.java:12) 

Kot smo že omenili, je vrednost napake 2 pomeni "datoteka ni najdena", kar v tem primeru pomeni, da je izvršljiva datoteka z imenom dir.exe ni bilo mogoče najti. To je zato, ker je imeniški ukaz del tolmača ukazov Windows in ne ločena izvedljiva datoteka. Če želite zagnati tolmač ukazov Windows, zaženite enega od njih command.com ali cmd.exe, odvisno od operacijskega sistema Windows, ki ga uporabljate. Seznam 4.5 zažene kopijo tolmača ukazov Windows in nato izvrši uporabniški ukaz (npr. dir).

Seznam 4.5 GoodWindowsExec.java

$config[zx-auto] not found$config[zx-overlay] not found