/* * 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; } }