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