1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * $Id: JavaUtils.java,v 1.7 2004/02/17 04:23:24 minchau Exp $
  18. */
  19. package com.sun.org.apache.xml.internal.utils.synthetic;
  20. import java.io.IOException;
  21. /**
  22. * This class supports invoking Java compilation from within
  23. * a Java program. Recent versions of the Java environment have
  24. * provided such an API (in tools.jar). But that isn't available
  25. * on all platforms, and a fallback to the command line may be needed
  26. * (though this too may not always be available, eg. for security
  27. * reasons).
  28. * <p>
  29. * There's an additional complication in some environments --
  30. * such as Microsoft's VJ++ -- where the classpath as seen in
  31. * the System Properties may not be the one the user expects.
  32. * The code here is parameterized to try to deal with that.
  33. * @xsl.usage internal
  34. */
  35. public class JavaUtils
  36. {
  37. // One-time flag for whether we could dynamically load compiler API
  38. private static boolean cantLoadCompiler=false;
  39. // Debug flag - generates debug stuff if true.
  40. private static boolean debug = false;
  41. /** Control whether compilation occurs with the -g option
  42. * (debugging information included in generated classfile).
  43. * This is an attribute, rather than a parameter on the compile
  44. * method, largely because it tends to be an all-or-nothing decision;
  45. * generally you're either doing program development and want it,
  46. * or running in production mode and don't. But that may not match
  47. * the needs of future users...
  48. * <p>
  49. * TODO: Consider whether debug should be a parameter.
  50. *
  51. * @param boolean newDebug True to request debugging data,
  52. * false to request optimized output. (It's uncommon to
  53. * want both or neither!)
  54. */
  55. public static void setDebug(boolean newDebug)
  56. {
  57. debug=newDebug;
  58. }
  59. /** Try to compile a .java file on disk. This will first attempt to
  60. * use the sun.java.tools.javac() method, then (if that is unavailable)
  61. * fall back upon shelling out to a command line and running javac
  62. * there.
  63. * <p>
  64. * NOTE: This must be _compiled_ with sun.java.tools.* (tools.jar)
  65. * available. We could change that to use reflection instead, if we
  66. * accept some overhead... minor compared to the cost of running the
  67. * compiler!
  68. * <p>
  69. * This has complications on some platforms. For example, under
  70. * Microsoft Visual Java ++ (at least, as installed on my test system),
  71. * I found that I had to specify paths to both javac and xerces.jar
  72. * rather than counting on the shell's path and classpath having
  73. * been set to reach these. For that reason I've parameterized this
  74. * method with a few system properties, so you can adapt it to your
  75. * own system's needs without modifying the code:
  76. * <dl>
  77. * <dt>com.sun.org.apache.xml.internal.utils.synthetic.javac
  78. * <dd>Command line issued to invoke the compiler. Defaults to "javac",
  79. * which should work in most systems. In VJ++, try setting it to
  80. * "cmd /c %JAVA_HOME%\\bin\javac.exe"
  81. * <dt>com.sun.org.apache.xml.internal.utils.synthetic.moreclasspath
  82. * <dd>Additional classpath, to be prepended to the one retrieved from
  83. * java.class.path. Defaults to "" (empty). In VJ++, try setting it to
  84. * point to your copy of xerces.jar, which may not be found otherwise.
  85. * TODO: Reconsider prepend versus append!
  86. * </dl>
  87. *
  88. * @param String fileName Which .java file to compile. Note that this may
  89. * be relative to the "current directory".
  90. * @param String classPath Additional places to look for classes that
  91. * this .java file depends upon. Becomes the javac command's
  92. * -classpath parameter value.
  93. * @return boolean True iff compilation succeeded.
  94. */
  95. public static boolean JDKcompile(String fileName, String classPath)
  96. {
  97. String moreClassPath=
  98. System.getProperty("com.sun.org.apache.xml.internal.utils.synthetic.moreclasspath","")
  99. .trim();
  100. if(moreClassPath.length()>0)
  101. classPath=moreClassPath+';'+classPath;
  102. if (debug)
  103. {
  104. System.err.println ("JavaEngine: Compiling " + fileName);
  105. System.err.println ("JavaEngine: Classpath is " + classPath);
  106. }
  107. String code_option = debug ? "-g" : "-O";
  108. // Start by trying Sun's compiler API
  109. if(!cantLoadCompiler)
  110. {
  111. String args[] = {
  112. code_option,
  113. "-classpath", classPath,
  114. fileName
  115. };
  116. // try
  117. // {
  118. // return new sun.tools.javac.Main(System.err, "javac").compile(args);
  119. // }
  120. // catch (Throwable th)
  121. // {
  122. // System.err.println("INFORMATIONAL: Unable to load Java compiler API (eg tools.jar).");
  123. // System.err.println("\tSwitching to command-line invocation.");
  124. // cantLoadCompiler=true;
  125. // }
  126. }
  127. // FALLTHRU:
  128. // Can't load javac() method; try shelling out to the command
  129. // line and invoking it via exec().
  130. String javac_command=
  131. System.getProperty("com.sun.org.apache.xml.internal.utils.synthetic.javac","javac");
  132. String args[] = {
  133. javac_command,
  134. code_option,
  135. "-classpath", classPath,
  136. fileName
  137. };
  138. try
  139. {
  140. Process p=java.lang.Runtime.getRuntime().exec(args);
  141. int compileOK=waitHardFor(p); // pause for debugging...
  142. return compileOK==0; //0 == no error reported
  143. }
  144. catch(IOException e)
  145. {
  146. System.err.println("ERROR: IO exception during exec(javac).");
  147. }
  148. catch(SecurityException e)
  149. {
  150. System.err.println("ERROR: Unable to create subprocess to exec(javac).");
  151. }
  152. // FALLTHRU: All attempts failed.
  153. return false;
  154. }
  155. /** Subroutine: Like p.waitFor, but discards the InterruptedException
  156. * and goes right back into a wait. I don't want to report compiler
  157. * success or failure until it really has succeeded or failed... I think.
  158. * @param Process p to be waited for
  159. * @return the exitValue() of the process.
  160. */
  161. static int waitHardFor(java.lang.Process p)
  162. {
  163. boolean done=false;
  164. while(!done)
  165. try
  166. {
  167. p.waitFor();
  168. done=true;
  169. }
  170. catch(InterruptedException e)
  171. {
  172. System.err.println("(Compiler process wait interrupted and resumed)");
  173. }
  174. int ev=p.exitValue(); // Pause for debugging...
  175. return ev;
  176. }
  177. }