001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.web.page;
007    
008    import java.io.*;
009    import java.util.*;
010    import fc.util.*;
011    
012    /**
013    Used to compile generated pages by invoking the java compiler.
014    */
015    public class PageCompiler
016    {
017    private static final boolean dbg = false;
018    
019    String  error;
020    File  javafile;
021    String  classpath;
022    String  encoding;
023    
024    /**
025    Creates a new page compiler that will use the default (system) classpath
026    as seen by 'javac' when it is invoked from the command line. No
027    seperate "encoding" flag will be specified to the javac.
028    
029    */
030    public PageCompiler(File javafile)
031      {
032      this.javafile = javafile;
033      }
034    
035    /**
036    Creates a new page compiler with the specified classpath. This is useful
037    when the classpath must contain some directories within the servlet web
038    application, just as <tt>WEB-INF/classes</tt>, <tt>WEB-INF/lib</tt> etc.
039    
040    @param  javafile  the source java file to compile
041    @param  classpath classpath to use when compiling
042    @param  encoding  the encoding of the java source file (example
043              ISO-8859-1, UTF-8 etc.). Used by the -encoding
044              flag passed to javac. Specify <tt>null</tt>
045              for no specific encoding.
046    */
047    public PageCompiler(File javafile, String classpath, String encoding)
048      {
049      this.javafile = javafile;
050      this.classpath  = classpath;
051      this.encoding = encoding;
052      }
053      
054    public String getError()
055      {
056      return error;
057      }
058      
059    public boolean compile() throws IOException
060      {
061      //javac 1.5.x does not crap out if a 0 byte source file is fed to it
062      //it simply returns 0 (implying success) but does NOT generate a classfile. so this hack.
063      if (javafile.length() == 0)
064        {
065        error = "Java source file [" + 
066            javafile.getCanonicalPath() + 
067            "] size was 0 bytes. This file cannot be compiled";
068        
069        return false;
070        }
071      
072      //this is ok too: {"bash", "-c", "javac -nowarn /tmp/c.java"});
073      List javac_cmd = new ArrayList();
074      javac_cmd.add("javac");
075      javac_cmd.add("-nowarn");
076      
077      if (encoding != null) {
078        javac_cmd.add("-encoding");
079        javac_cmd.add(encoding);
080        }
081      
082      if (classpath != null) {
083        javac_cmd.add("-classpath");
084        javac_cmd.add(classpath);
085        }
086      javac_cmd.add(javafile.getCanonicalPath()); 
087    
088      if (dbg) System.out.println("Compile command: " + javac_cmd);
089      
090      ProcessBuilder pb = new ProcessBuilder(javac_cmd);
091      
092      Process p = pb.start();
093        
094      int exit_val = 0;
095      
096      /*
097      have to read out/err from the process otherwise it may hang if
098      javac has lots of output (in case of errors)...waitFor will not
099      return since the process is hung.
100      See: jdk 1.5 docs and 
101      http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
102      
103      try 
104        {
105        exit_val = p.waitFor();
106        }
107      catch (InterruptedException e)
108        {
109        throw new IOException(e.toString());
110        }
111      */  
112    
113      CharArrayWriter buf = new CharArrayWriter(1024);
114      
115      InputStream stderr = new BufferedInputStream(p.getErrorStream());
116      InputStream stdout  = new BufferedInputStream(p.getInputStream());
117    
118      int c = stderr.read();
119      while (c != -1) {
120        buf.write((char)c);
121        c = stderr.read();
122        }
123    
124      c = stdout.read();
125      if (c != -1)
126        buf.append("-------------------------------------------");
127      
128      while (c != -1) {
129        buf.write((char)c);
130        c = stdout.read();
131        }
132      
133      error = buf.toString();
134    
135      try 
136        {
137        exit_val = p.waitFor();
138        }
139      catch (InterruptedException e)
140        {
141        throw new IOException(e.toString());
142        }
143    
144      if (dbg) System.out.println("Exit value: " + exit_val);
145        
146      return exit_val == 0;
147      }
148    
149    public static void main (String args[]) throws Exception
150      {
151      Args myargs = new Args(args);
152      myargs.setUsage("java " + ClassUtil.getClassName() + " -file path-to-file-to-compile [-classpath <class-path> -encoding encoding]");
153      
154      PageCompiler pc = new PageCompiler(
155        new File(myargs.getRequired("file")),
156        myargs.get("classpath", System.getProperty("java.class.path")),
157        myargs.get("encoding", null)
158        );
159      
160      if (! pc.compile())
161        System.out.println(pc.getError());
162      } 
163    }