blob: b3974548ffd684646d051edbe3267a43620896ba [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* $Id$
*/
package org.apache.xml.utils.synthetic;
import java.io.IOException;
/**
* <meta name="usage" content="internal"/>
* This class supports invoking Java compilation from within
* a Java program. Recent versions of the Java environment have
* provided such an API (in tools.jar). But that isn't available
* on all platforms, and a fallback to the command line may be needed
* (though this too may not always be available, eg. for security
* reasons).
* <p>
* There's an additional complication in some environments --
* such as Microsoft's VJ++ -- where the classpath as seen in
* the System Properties may not be the one the user expects.
* The code here is parameterized to try to deal with that.
*/
public class JavaUtils
{
// One-time flag for whether we could dynamically load compiler API
private static boolean cantLoadCompiler=false;
// Debug flag - generates debug stuff if true.
private static boolean debug = false;
/** Control whether compilation occurs with the -g option
* (debugging information included in generated classfile).
* This is an attribute, rather than a parameter on the compile
* method, largely because it tends to be an all-or-nothing decision;
* generally you're either doing program development and want it,
* or running in production mode and don't. But that may not match
* the needs of future users...
* <p>
* TODO: Consider whether debug should be a parameter.
*
* @param boolean newDebug True to request debugging data,
* false to request optimized output. (It's uncommon to
* want both or neither!)
*/
public static void setDebug(boolean newDebug)
{
debug=newDebug;
}
/** Try to compile a .java file on disk. This will first attempt to
* use the sun.java.tools.javac() method, then (if that is unavailable)
* fall back upon shelling out to a command line and running javac
* there.
* <p>
* NOTE: This must be _compiled_ with sun.java.tools.* (tools.jar)
* available. We could change that to use reflection instead, if we
* accept some overhead... minor compared to the cost of running the
* compiler!
* <p>
* This has complications on some platforms. For example, under
* Microsoft Visual Java ++ (at least, as installed on my test system),
* I found that I had to specify paths to both javac and xerces.jar
* rather than counting on the shell's path and classpath having
* been set to reach these. For that reason I've parameterized this
* method with a few system properties, so you can adapt it to your
* own system's needs without modifying the code:
* <dl>
* <dt>org.apache.xml.utils.synthetic.javac
* <dd>Command line issued to invoke the compiler. Defaults to "javac",
* which should work in most systems. In VJ++, try setting it to
* "cmd /c %JAVA_HOME%\\bin\javac.exe"
* <dt>org.apache.xml.utils.synthetic.moreclasspath
* <dd>Additional classpath, to be prepended to the one retrieved from
* java.class.path. Defaults to "" (empty). In VJ++, try setting it to
* point to your copy of xerces.jar, which may not be found otherwise.
* TODO: Reconsider prepend versus append!
* </dl>
*
* @param String fileName Which .java file to compile. Note that this may
* be relative to the "current directory".
* @param String classPath Additional places to look for classes that
* this .java file depends upon. Becomes the javac command's
* -classpath parameter value.
* @return boolean True iff compilation succeeded.
*/
public static boolean JDKcompile(String fileName, String classPath)
{
String moreClassPath=
System.getProperty("org.apache.xml.utils.synthetic.moreclasspath","")
.trim();
if(moreClassPath.length()>0)
classPath=moreClassPath+';'+classPath;
if (debug)
{
System.err.println ("JavaEngine: Compiling " + fileName);
System.err.println ("JavaEngine: Classpath is " + classPath);
}
String code_option = debug ? "-g" : "-O";
// Start by trying Sun's compiler API
if(!cantLoadCompiler)
{
String args[] = {
code_option,
"-classpath", classPath,
fileName
};
// try
// {
// return new sun.tools.javac.Main(System.err, "javac").compile(args);
// }
// catch (Throwable th)
// {
// System.err.println("INFORMATIONAL: Unable to load Java compiler API (eg tools.jar).");
// System.err.println("\tSwitching to command-line invocation.");
// cantLoadCompiler=true;
// }
}
// FALLTHRU:
// Can't load javac() method; try shelling out to the command
// line and invoking it via exec().
String javac_command=
System.getProperty("org.apache.xml.utils.synthetic.javac","javac");
String args[] = {
javac_command,
code_option,
"-classpath", classPath,
fileName
};
try
{
Process p=java.lang.Runtime.getRuntime().exec(args);
int compileOK=waitHardFor(p); // pause for debugging...
return compileOK==0; //0 == no error reported
}
catch(IOException e)
{
System.err.println("ERROR: IO exception during exec(javac).");
}
catch(SecurityException e)
{
System.err.println("ERROR: Unable to create subprocess to exec(javac).");
}
// FALLTHRU: All attempts failed.
return false;
}
/** Subroutine: Like p.waitFor, but discards the InterruptedException
* and goes right back into a wait. I don't want to report compiler
* success or failure until it really has succeeded or failed... I think.
* @param Process p to be waited for
* @return the exitValue() of the process.
*/
static int waitHardFor(java.lang.Process p)
{
boolean done=false;
while(!done)
try
{
p.waitFor();
done=true;
}
catch(InterruptedException e)
{
System.err.println("(Compiler process wait interrupted and resumed)");
}
int ev=p.exitValue(); // Pause for debugging...
return ev;
}
}