Programmēšana

Kad Runtime.exec () nebūs

Kā daļu no Java valodas, java.lang pakete tiek netieši importēta katrā Java programmā. Šīs paketes slazdi bieži parādās, ietekmējot lielāko daļu programmētāju. Šajā mēnesī es apspriedīšu slazdus, ​​kas slēpjas Runtime.exec () metodi.

Pitfall 4: Kad Runtime.exec () nebūs

Klase java.lang.Runtime piedāvā statisku metodi, ko sauc getRuntime (), kas izgūst pašreizējo Java izpildlaika vidi. Tas ir vienīgais veids, kā iegūt atsauci uz Izpildlaiks objekts. Izmantojot šo atsauci, varat palaist ārējās programmas, izsaucot Izpildlaiks klases exec () metodi. Izstrādātāji bieži sauc šo metodi, lai palaistu pārlūkprogrammu palīdzības lapas parādīšanai HTML.

Ir četras pārslogotas exec () komanda:

  • public Process exec (virknes komanda);
  • publiskais process exec (virkne [] cmdArray);
  • public Process exec (virkne komanda, virkne [] envp);
  • publiskais process exec (virkne [] cmdArray, virkne [] envp);

Katrai no šīm metodēm komanda - un, iespējams, argumentu kopa - tiek nodota operētājsistēmai raksturīgam funkciju izsaukumam. Pēc tam tiek izveidots operētājsistēmai raksturīgs process (darbojas programma) ar atsauci uz a Process klase atgriezās Java VM. The Process klase ir abstrakta klase, jo īpaša Process eksistē katrai operētājsistēmai.

Šajās metodēs varat nodot trīs iespējamos ievades parametrus:

  1. Viena virkne, kas apzīmē gan izpildāmo programmu, gan visus šīs programmas argumentus
  2. Virkņu masīvs, kas atdala programmu no tās argumentiem
  3. Vides mainīgo masīvs

Ievadiet vides mainīgos formā nosaukums = vērtība. Ja izmantojat exec () ar vienu virkni gan programmai, gan tās argumentiem, ņemiet vērā, ka virkne tiek parsēta, izmantojot atdalītāju balto atstarpi, izmantojot StringTokenizer klasē.

Paklupšana uz IllegalThreadStateException

Pirmā kļūda, kas attiecas uz Runtime.exec () ir IllegalThreadStateException. Visizplatītākais API pirmais tests ir kodēt tā visredzamākās metodes. Piemēram, lai izpildītu procesu, kas nav Java VM, mēs izmantojam exec () metodi. Lai redzētu vērtību, ko atgriež ārējais process, mēs izmantojam exitValue () metode uz Process klasē. Pirmajā piemērā mēs mēģināsim izpildīt Java kompilatoru (javac.exe):

Saraksts 4.1 BadExecJavac.java

importēt java.util. *; importēt java.io. *; public class BadExecJavac {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Process proc = rt.exec ("javac"); int exitVal = proc.exitValue (); System.out.println ("Process exitValue:" + exitVal); } noķert (Metams t) {t.printStackTrace (); }}} 

Skrējiens BadExecJavac ražo:

E: \ class \ com \ javaworld \ jpitfalls \ article2> java BadExecJavac java.lang.IllegalThreadStateException: process nav aizvērts vietnē java.lang.Win32Process.exitValue (vietējā metode) vietnē BadExecJavac.main (BadExecJavac.java: 

Ja ārējais process vēl nav pabeigts, exitValue () metode iemetīs IllegalThreadStateException; tāpēc šī programma neizdevās. Lai gan dokumentācijā ir norādīts šis fakts, kāpēc šī metode nevar gaidīt, līdz tā var sniegt derīgu atbildi?

Rūpīgāks ieskats vietnē pieejamās metodēs Process klase atklāj a gaidīt() metode, kas to precīzi dara. Patiesībā, gaidīt() atgriež arī izejas vērtību, kas nozīmē, ka jūs to neizmantotu exitValue () un gaidīt() savienojumā ar otru, bet drīzāk izvēlētos vienu vai otru. Vienīgais iespējamais laiks, ko izmantosit exitValue () tā vietā gaidīt() tas būtu tad, kad nevēlaties, lai jūsu programma bloķētu gaidīšanu ārējā procesā, kas, iespējams, nekad netiks pabeigts. Tā vietā, lai izmantotu gaidīt() metodi, es gribētu nodot Būla parametru ar nosaukumu gaidīt iekšā exitValue () metodi, lai noteiktu, vai pašreizējam pavedienam vajadzētu gaidīt. Būla vērtība būtu izdevīgāka, jo exitValue () ir piemērotāks šīs metodes nosaukums, un divām metodēm nav nepieciešams veikt vienu un to pašu funkciju dažādos apstākļos. Šāda vienkārša nosacījumu diskriminācija ir ievades parametra joma.

Tāpēc, lai izvairītos no šī slazda, vai nu noķeriet IllegalThreadStateException vai pagaidiet, kamēr process būs pabeigts.

Tagad novērsīsim problēmu sarakstā 4.1 un gaidīsim procesa pabeigšanu. Sarakstā 4.2 programma atkal mēģina izpildīt javac.exe un pēc tam gaida ārējā procesa pabeigšanu:

Saraksts 4.2 BadExecJavac2.java

importēt java.util. *; importēt java.io. *; public class BadExecJavac2 {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Process proc = rt.exec ("javac"); int exitVal = proc.waitFor (); System.out.println ("Process exitValue:" + exitVal); } noķert (Metams t) {t.printStackTrace (); }}} 

Diemžēl palaist BadExecJavac2 nerada izvadi. Programma uzkaras un nekad nepabeidz. Kāpēc javac process nekad nav pabeigts?

Kāpēc Runtime.exec () karājas

JDK Javadoc dokumentācija sniedz atbildi uz šo jautājumu:

Tā kā dažas vietējās platformas standarta ievades un izvades straumēm nodrošina tikai ierobežotu bufera izmēru, nespēja ātri ierakstīt ievades straumi vai nolasīt apakšprocesa izvades straumi var izraisīt apakšprocesa bloķēšanu un pat strupceļu.

Vai tas ir tikai gadījums, kad programmētāji nelasa dokumentāciju, kā tas norādīts bieži citētajā ieteikumā: izlasiet smalko rokasgrāmatu (RTFM)? Atbilde ir daļēji jā. Šajā gadījumā, lasot Javadoc, jūs nonāktu pusceļā; tas paskaidro, ka jums ir jārīkojas ar sava ārējā procesa straumēm, taču tas nepasaka, kā.

Šeit spēlē vēl vienu mainīgo, par ko liecina lielais programmētāju jautājumu un nepareizo priekšstatu skaits par šo API ziņu grupās: Runtime.exec () un procesa API šķiet ārkārtīgi vienkārši, ka vienkāršība maldina, jo vienkārša vai acīmredzama API izmantošana ir pakļauta kļūdām. Šeit API dizainera mācība ir rezervēt vienkāršas API vienkāršām darbībām. Operācijām, kurām ir tendence uz sarežģītību un platformas specifiskām atkarībām, precīzi jāatspoguļo domēns. Abstrakciju ir iespējams aiznest pārāk tālu. The JConfig bibliotēka sniedz pilnīgākas API piemēru, lai apstrādātu failu un procesu darbības (lai iegūtu papildinformāciju, skatiet zemāk esošos resursus).

Tagad sekosim JDK dokumentācijai un rīkosimies ar javac process. Kad tu skrien javac bez jebkādiem argumentiem tas rada lietojuma paziņojumu kopu, kas apraksta programmas palaišanu un visu pieejamo programmas opciju nozīmi. Zinot, ka tas notiek stderr straumi, jūs varat viegli uzrakstīt programmu, lai šo straumi iztukšotu, pirms gaidāt procesa iziešanu. 4.3 saraksts pabeidz šo uzdevumu. Lai gan šī pieeja darbosies, tā nav labs vispārējs risinājums. Tādējādi Listing 4.3 programma ir nosaukta ViduvējsExecJavac; tas nodrošina tikai viduvēju risinājumu. Labāks risinājums iztukšotu gan standarta kļūdu straumi, gan standarta izvades straumi. Labākais risinājums vienlaikus iztukšotu šīs straumes (es to parādīšu vēlāk).

Saraksts 4.3 MediocreExecJavac.java

importēt java.util. *; importēt java.io. *; public class MediocreExecJavac {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Process proc = rt.exec ("javac"); InputStream stderr = proc.getErrorStream (); InputStreamReader isr = jauns InputStreamReader (stderr); BufferedReader br = jauns BufferedReader (isr); Stīgu līnija = null; System.out.println (""); while ((line = br.readLine ())! = null) System.out.println (līnija); System.out.println (""); int exitVal = proc.waitFor (); System.out.println ("Process exitValue:" + exitVal); } noķert (izmetams t) {t.printStackTrace (); }}} 

Skrējiens ViduvējsExecJavac ģenerē:

E: \ class \ com \ javaworld \ jpitfalls \ article2> java MediocreExecJavac Lietojums: javac kur ietilpst: -g Ģenerē visu atkļūdošanas informāciju -g: neviens Nerada atkļūdošanas informāciju -g: {lines, vars, source} Ģenerē tikai daļu atkļūdošanas informācijas -O Optimizēt; var kavēt atkļūdošanu vai palielināt klases failus -nobrīdināt Nerada nekādus brīdinājumus -pārlocīti Izejas ziņojumi par kompilatora darbību -bootclasspath ignorēt bootstrap klases failu atrašanās vietu -extdirs ignorēt instalēto paplašinājumu atrašanās vietu -d norādiet, kur izvietot ģenerētos klases failus -kodēšana Norādiet avota failu izmantoto rakstzīmju kodējumu -target Ģenerējiet klases failus konkrētai VM versijai Process exitValue: 2 

Tātad, ViduvējsExecJavac strādā un rada izejas vērtību 2. Parasti izejas vērtība ir 0 norāda uz panākumiem; jebkura nenulles vērtība norāda uz kļūdu. Šo izejas vērtību nozīme ir atkarīga no konkrētās operētājsistēmas. Win32 kļūda ar vērtību 2 ir kļūda "fails nav atrasts". Tā ir jēga, jo javac sagaida, ka mēs sekosim programmai ar avota koda failu kompilēšanai.

Tādējādi, lai apietu otro slazdu - uz visiem laikiem karājas Runtime.exec () - ja jūsu palaistā programma rada izeju vai sagaida ievadi, pārliecinieties, ka apstrādājat ievades un izvades straumes.

Pieņemot, ka komanda ir izpildāma programma

Saskaņā ar operētājsistēmu Windows daudzi jaunie programmētāji paklūp Runtime.exec () mēģinot to izmantot tādām neizpildāmām komandām kā rež un kopija. Pēc tam viņi saskaras Runtime.exec ()trešā bedre. 4.4. Saraksts precīzi parāda, ka:

Sarakstā 4.4 BadExecWinDir.java

importēt java.util. *; importēt java.io. *; public class BadExecWinDir {public static void main (String args []) {try {Runtime rt = Runtime.getRuntime (); Process proc = rt.exec ("dir"); InputStream stdin = proc.getInputStream (); InputStreamReader isr = jauns InputStreamReader (stdin); BufferedReader br = jauns BufferedReader (isr); Stīgu līnija = null; System.out.println (""); while ((line = br.readLine ())! = null) System.out.println (līnija); System.out.println (""); int exitVal = proc.waitFor (); System.out.println ("Process exitValue:" + exitVal); } noķert (Metams t) {t.printStackTrace (); }}} 

Skrējiens BadExecWinDir ražo:

E: \ class \ com \ javaworld \ jpitfalls \ article2> java BadExecWinDir java.io.IOException: CreateProcess: dir kļūda = 2 pie java.lang.Win32Process.create (vietējā metode) vietnē java.lang.Win32Process. (Nezināms avots) at java.lang.Runtime.execInternal (vietējā metode) at java.lang.Runtime.exec (nezināms avots) pie java.lang.Runtime.exec (nezināms avots) pie java.lang.Runtime.exec (nezināms avots) java .lang.Runtime.exec (nezināms avots) vietnē BadExecWinDir.main (BadExecWinDir.java:12) 

Kā minēts iepriekš, kļūdas vērtība 2 nozīmē "fails nav atrasts", kas šajā gadījumā nozīmē, ka izpildāmais nosaukums dir.exe nevarēja atrast. Tas ir tāpēc, ka direktorija komanda ir daļa no Windows komandu tulka, nevis atsevišķa izpildāma. Lai palaistu Windows komandu tulku, izpildiet vai nu command.com vai cmd.exe, atkarībā no izmantotās Windows operētājsistēmas. 4.5 saraksts palaiž Windows komandu tulka kopiju un pēc tam izpilda lietotāja sniegto komandu (piem., rež).

Sarakstā 4.5 GoodWindowsExec.java

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