Udførelse af eksterne processer
1. Eksempel: Compilering og udførelse af Java-program
Simpel IDE Vi vil i det følgende se hvordan man fra et Java-program kan få oversat og udført et andet Java-program. Dette kunne f.eks. bruges i en simpel IDE, man selv ville udvikle — eller måske mere avanceret, af en udviklingsserver.
Path For at forstå eksempel, bør man have læst kapitlet: "Udførelse af Java-programmer", og sikre sig at Path er sat som anført i kapitlet, da følgende eksempel ellers ikke vil virke.
1.1 Simple service-metoder
  Inden vi for alvor går igang, vil vi have nogle simple service-metoder, som vil blive anvendt i vores eksempel:
Source 1:
Tre service­metoder
            private BufferedReader createReader( InputStream stream ) {
              return new BufferedReader( new InputStreamReader( stream ) );
            }
            
            private void writeTextToFile( String path, String text ) throws IOException {
              FileWriter writer = new FileWriter ( path );
              writer.write( text );
              writer.close();
            }
            
            private boolean deleteFile( String path ) {
              File file = new File( path );
            
              return file.delete();
            }
          
createReader-metoden gør det lidt nemmere (og kortere) at lave en BufferedReader på grundlag af en InputStream. writeTextToFile-metoden gør det enklere at lave en tekstfile, når hele dens tekstindhold er kendt. Endelig gør deleteFile-metoden det en smule enklere at slette en file. Beskedne service-metoder, hvoraf writeTextToFile-metoden nok er den eneste der gør en nævneværdig forskel.
1.2 Generel udførelse af ekstern process
Det næste er egentlig også en service-metode, men det er en der retter sig mere direkte mod hvad dette eksempel gør ud på — det udfører en ekstern process:
Source 2:
Udførelse af ekstern proces
(JavaProxy.java)
private void execute( String command ) throws IOException {
  Runtime runtime = Runtime.getRuntime();
  
  Process process = runtime.exec( command );
  
  BufferedReader outputReader = createReader( process.getInputStream() );
  BufferedReader errorReader = createReader( process.getErrorStream() );

  String line;
  
  while ( (line = outputReader.readLine()) != null )
    System.out.println( line );
  
  while ( (line = errorReader.readLine()) != null )
    System.out.println( line );
  
  try {
    process.waitFor();
  }
  catch ( InterruptedException e ) {
    // ignorer
  }
}
execute-metoden udfører et eksternt program i form af den tekststreng command, den modtager som parameter. Dette kunne f.eks. være: "javac HelloWorld.java", som vi skal se senere.
Efter at have modtaget Runtime-objektet, og kaldt exec-metoden med command som parameter, udføres denne teksstreng som havde man skrevet den i en Command Prompt. Læsningen af output (såvel det fra System.out, som det fra System.err) kunne udemærket gøres asynkront, men da vi ikke har behov for at gøre det så avanceret i dette eksempel (vi er reelt kun interesseret i læse det samlede output — ikke i at føre en dialog med processen), gøres det her ved blot at læse (og udskrive) hele tekst-indholdet, uden at vi i øvrigt forholder os til det.
Bemærk, at vi lader metoden kaste en evt. IOException videre, da den kaldende metode må forventes at være interesseret i denne, mens vi ikke selv kan gøre noget ved det. Derimod griber vi en evt. InterruptedException fra waitFor-metoden, da den næppe kan interessere nogen.
1.3 Compilering og udførelse af Java-program
Lad os først se en metode, der oversætter et Java-program bestående af en enkelt kildetekstfile:
Source 3:
Compile­ring af kilde­tekst­file
(JavaProxy.java)
private void compileProgram( String path ) throws IOException {
  System.out.println( "Compiling: " + path );
  
  // '-verbose' giver os mere at se på!
  //execute( "javac -verbose \"" + path + "\"" );
  execute( "javac \"" + path + "\"" );
}
Compilerer i stilhed Stien til den tekstfile, der indeholder kildeteksten gives med som parameter til metoden. Vi opbygger dernæst den kommando som udført fra en Command Prompt vil compilere kildeteksten. Hvis kildeteksten f.eks. var gemt i en file ved navn: "HelloWorld.java", vil kommandoen blive: javac "HelloWorld.java". Compileren vil default udføre sin opgave i stilhed (i.e. uden nogen udskrifter), såfremt den kan compilere kildeteksten uden fejl. Vi har indledningsvis indsat en enkelt udskrift, der bekræfter at vi forsøger at compilere den pågældende file, men ønsker man endnu flere udskrifter, kan man slå verbose til. Det giver der dog en hel del (uønsket) "støj"!
Bemærk, at vi sætter anførselstegn ("...") udenom stien (e.g. "c:\java eksempel\HelloWorld.java") for at undgå problemer med mellemrum i directory-navne.
Efter at have compileret kildeteksten, er vi nu klar til at udføre det oversatte program:
Source 4:
Udførelse af oversat program
(JavaProxy.java)
private void executeProgram( String path ) throws IOException {
  System.out.println( "Executing: " + path );
  
  execute( "java \"" + path + "\"" );
}
Ligner meget det foregående, idet vi her opbygger en kommando, der kalder java.exe. Hvis det oversatte program befinder sig i: HelloWorld.class, vil det blive: java "HelloWorld". Bemærk at Java-fortolkeren ikke vil have file-extension .class.
Vi kan samle compilering og efterfølgende udførelse i én metode:
Source 5:
Samlet metode
(JavaProxy.java)
public boolean compileAndExecute( String className, String source ) {
  try {
    // file-navnet skal være det samme som klasse-navnet
    String javaFile = className + ".java";
    String classFile = className + ".class";
    
    // ryd op efter evt. tidligere compilering
    deleteFile( javaFile );
    deleteFile( classFile );
    
    writeTextToFile( javaFile, source );
  
    compileProgram( javaFile );
    
    executeProgram( className ); // ikke '.class' efter denne
    
    return true;
  }
  catch ( IOException e ) {
    return false;
  }
}
Her får vi såvel klassenavn, som kildetekst, som parameter. Vi kunne godt udlede klassenavnet af kildeteksten, men det er nemmere at få den direkte — specielt, da dette kun er et eksempel. Vi opbygger dernæst navnene på de involverede filer, og sletter dem for at undgå problemer med rester fra en tidligere compilering. Dernæst skriver den nye kildetekstfile, oversætter den og udfører den. Metoden returnerer boolsk om compilering og eksekvering forløb uden fejl (mht. eksekvering, dog kun om det lykkedes at starte programmet).
1.4 Testanvendelse
Lad os set et eksempel, hvor vi anvender JavaProxy-klassen til at compilere og udføre det velkendt HelloWorld-eksempel:
Source 6:
Compile­ring og udførelse af "Hello World"
Main.java
public class Main {

  public static void main( String[] args ) {
    StringBuffer sb = new StringBuffer();

    sb.append( "public class Main {" );
    sb.append( "" );
    sb.append( "  public static void main( String[] args ) {" );
    sb.append( "    System.out.println( \"Hello World!\" );" );
    sb.append( "  }" );
    sb.append( "}" );
    
    JavaProxy proxy = new JavaProxy();
    
    if ( !proxy.compileAndExecute( "HelloWorld", sb.toString() ) )
      System.out.println( "Failed to compile and/or execute program!" );
  }
}
Compiling: HelloWorld.java
Executing: HelloWorld
Hello World!
Den sidste af de tre udskrifter stammer fra det udførte Java-program.