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