You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ExecuteJava.java 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.tools.ant.taskdefs;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.io.PrintStream;
  22. import java.lang.reflect.InvocationTargetException;
  23. import java.lang.reflect.Method;
  24. import java.lang.reflect.Modifier;
  25. import org.apache.tools.ant.AntClassLoader;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.Project;
  28. import org.apache.tools.ant.ProjectComponent;
  29. import org.apache.tools.ant.Task;
  30. import org.apache.tools.ant.taskdefs.condition.Os;
  31. import org.apache.tools.ant.types.Commandline;
  32. import org.apache.tools.ant.types.CommandlineJava;
  33. import org.apache.tools.ant.types.Path;
  34. import org.apache.tools.ant.types.Permissions;
  35. import org.apache.tools.ant.util.JavaEnvUtils;
  36. import org.apache.tools.ant.util.TimeoutObserver;
  37. import org.apache.tools.ant.util.Watchdog;
  38. /**
  39. * Execute a Java class.
  40. * @since Ant 1.2
  41. */
  42. public class ExecuteJava implements Runnable, TimeoutObserver {
  43. private Commandline javaCommand = null;
  44. private Path classpath = null;
  45. private CommandlineJava.SysProperties sysProperties = null;
  46. private Permissions perm = null;
  47. private Method main = null;
  48. private Long timeout = null;
  49. private volatile Throwable caught = null;
  50. private volatile boolean timedOut = false;
  51. private Thread thread = null;
  52. /**
  53. * Set the Java "command" for this ExecuteJava.
  54. * @param javaCommand the classname and arguments in a Commandline.
  55. */
  56. public void setJavaCommand(Commandline javaCommand) {
  57. this.javaCommand = javaCommand;
  58. }
  59. /**
  60. * Set the classpath to be used when running the Java class.
  61. *
  62. * @param p an Ant Path object containing the classpath.
  63. */
  64. public void setClasspath(Path p) {
  65. classpath = p;
  66. }
  67. /**
  68. * Set the system properties to use when running the Java class.
  69. * @param s CommandlineJava system properties.
  70. */
  71. public void setSystemProperties(CommandlineJava.SysProperties s) {
  72. sysProperties = s;
  73. }
  74. /**
  75. * Set the permissions for the application run.
  76. * @param permissions the Permissions to use.
  77. * @since Ant 1.6
  78. */
  79. public void setPermissions(Permissions permissions) {
  80. perm = permissions;
  81. }
  82. /**
  83. * Set the stream to which all output (System.out as well as System.err)
  84. * will be written.
  85. * @param out the PrintStream where output should be sent.
  86. * @deprecated since 1.4.x.
  87. * manage output at the task level.
  88. */
  89. public void setOutput(PrintStream out) {
  90. }
  91. /**
  92. * Set the timeout for this ExecuteJava.
  93. * @param timeout timeout as Long.
  94. * @since Ant 1.5
  95. */
  96. public void setTimeout(Long timeout) {
  97. this.timeout = timeout;
  98. }
  99. /**
  100. * Execute the Java class against the specified Ant Project.
  101. * @param project the Project to use.
  102. * @throws BuildException on error.
  103. */
  104. public void execute(Project project) throws BuildException {
  105. final String classname = javaCommand.getExecutable();
  106. AntClassLoader loader = null;
  107. try {
  108. if (sysProperties != null) {
  109. sysProperties.setSystem();
  110. }
  111. Class target = null;
  112. try {
  113. if (classpath == null) {
  114. target = Class.forName(classname);
  115. } else {
  116. loader = project.createClassLoader(classpath);
  117. loader.setParent(project.getCoreLoader());
  118. loader.setParentFirst(false);
  119. loader.addJavaLibraries();
  120. loader.setIsolated(true);
  121. loader.setThreadContextLoader();
  122. loader.forceLoadClass(classname);
  123. target = Class.forName(classname, true, loader);
  124. }
  125. } catch (ClassNotFoundException e) {
  126. throw new BuildException("Could not find " + classname + "."
  127. + " Make sure you have it in your"
  128. + " classpath");
  129. }
  130. main = target.getMethod("main", new Class[] {String[].class});
  131. if (main == null) {
  132. throw new BuildException("Could not find main() method in "
  133. + classname);
  134. }
  135. if ((main.getModifiers() & Modifier.STATIC) == 0) {
  136. throw new BuildException("main() method in " + classname
  137. + " is not declared static");
  138. }
  139. if (timeout == null) {
  140. run();
  141. } else {
  142. thread = new Thread(this, "ExecuteJava");
  143. Task currentThreadTask
  144. = project.getThreadTask(Thread.currentThread());
  145. // XXX is the following really necessary? it is in the same thread group...
  146. project.registerThreadTask(thread, currentThreadTask);
  147. // if we run into a timeout, the run-away thread shall not
  148. // make the VM run forever - if no timeout occurs, Ant's
  149. // main thread will still be there to let the new thread
  150. // finish
  151. thread.setDaemon(true);
  152. Watchdog w = new Watchdog(timeout.longValue());
  153. w.addTimeoutObserver(this);
  154. synchronized (this) {
  155. thread.start();
  156. w.start();
  157. try {
  158. wait();
  159. } catch (InterruptedException e) {
  160. // ignore
  161. }
  162. if (timedOut) {
  163. project.log("Timeout: sub-process interrupted",
  164. Project.MSG_WARN);
  165. } else {
  166. thread = null;
  167. w.stop();
  168. }
  169. }
  170. }
  171. if (caught != null) {
  172. throw caught;
  173. }
  174. } catch (BuildException e) {
  175. throw e;
  176. } catch (SecurityException e) {
  177. throw e;
  178. } catch (ThreadDeath e) {
  179. // XXX could perhaps also call thread.stop(); not sure if anyone cares
  180. throw e;
  181. } catch (Throwable e) {
  182. throw new BuildException(e);
  183. } finally {
  184. if (loader != null) {
  185. loader.resetThreadContextLoader();
  186. loader.cleanup();
  187. loader = null;
  188. }
  189. if (sysProperties != null) {
  190. sysProperties.restoreSystem();
  191. }
  192. }
  193. }
  194. /**
  195. * Run this ExecuteJava in a Thread.
  196. * @since Ant 1.5
  197. */
  198. public void run() {
  199. final Object[] argument = {javaCommand.getArguments()};
  200. try {
  201. if (perm != null) {
  202. perm.setSecurityManager();
  203. }
  204. main.invoke(null, argument);
  205. } catch (InvocationTargetException e) {
  206. Throwable t = e.getTargetException();
  207. if (!(t instanceof InterruptedException)) {
  208. caught = t;
  209. } /* else { swallow, probably due to timeout } */
  210. } catch (Throwable t) {
  211. caught = t;
  212. } finally {
  213. if (perm != null) {
  214. perm.restoreSecurityManager();
  215. }
  216. synchronized (this) {
  217. notifyAll();
  218. }
  219. }
  220. }
  221. /**
  222. * Mark timeout as having occurred.
  223. * @param w the responsible Watchdog.
  224. * @since Ant 1.5
  225. */
  226. public synchronized void timeoutOccured(Watchdog w) {
  227. if (thread != null) {
  228. timedOut = true;
  229. thread.interrupt();
  230. }
  231. notifyAll();
  232. }
  233. /**
  234. * Get whether the process was killed.
  235. * @return <code>true</code> if the process was killed, false otherwise.
  236. * @since 1.19, Ant 1.5
  237. */
  238. public synchronized boolean killedProcess() {
  239. return timedOut;
  240. }
  241. /**
  242. * Run the Java command in a separate VM, this does not give you
  243. * the full flexibility of the Java task, but may be enough for
  244. * simple needs.
  245. * @param pc the ProjectComponent to use for logging, etc.
  246. * @return the exit status of the subprocess.
  247. * @throws BuildException on error.
  248. * @since Ant 1.6.3
  249. */
  250. public int fork(ProjectComponent pc) throws BuildException {
  251. CommandlineJava cmdl = new CommandlineJava();
  252. cmdl.setClassname(javaCommand.getExecutable());
  253. String[] args = javaCommand.getArguments();
  254. for (int i = 0; i < args.length; i++) {
  255. cmdl.createArgument().setValue(args[i]);
  256. }
  257. if (classpath != null) {
  258. cmdl.createClasspath(pc.getProject()).append(classpath);
  259. }
  260. if (sysProperties != null) {
  261. cmdl.addSysproperties(sysProperties);
  262. }
  263. Redirector redirector = new Redirector(pc);
  264. Execute exe
  265. = new Execute(redirector.createHandler(),
  266. timeout == null
  267. ? null
  268. : new ExecuteWatchdog(timeout.longValue()));
  269. exe.setAntRun(pc.getProject());
  270. if (Os.isFamily("openvms")) {
  271. setupCommandLineForVMS(exe, cmdl.getCommandline());
  272. } else {
  273. exe.setCommandline(cmdl.getCommandline());
  274. }
  275. try {
  276. int rc = exe.execute();
  277. redirector.complete();
  278. return rc;
  279. } catch (IOException e) {
  280. throw new BuildException(e);
  281. } finally {
  282. timedOut = exe.killedProcess();
  283. }
  284. }
  285. /**
  286. * On VMS platform, we need to create a special java options file
  287. * containing the arguments and classpath for the java command.
  288. * The special file is supported by the "-V" switch on the VMS JVM.
  289. *
  290. * @param exe the Execute instance to alter.
  291. * @param command the command-line.
  292. */
  293. public static void setupCommandLineForVMS(Execute exe, String[] command) {
  294. //Use the VM launcher instead of shell launcher on VMS
  295. exe.setVMLauncher(true);
  296. File vmsJavaOptionFile = null;
  297. try {
  298. String [] args = new String[command.length - 1];
  299. System.arraycopy(command, 1, args, 0, command.length - 1);
  300. vmsJavaOptionFile = JavaEnvUtils.createVmsJavaOptionFile(args);
  301. //we mark the file to be deleted on exit.
  302. //the alternative would be to cache the filename and delete
  303. //after execution finished, which is much better for long-lived runtimes
  304. //though spawning complicates things...
  305. vmsJavaOptionFile.deleteOnExit();
  306. String [] vmsCmd = {command[0], "-V", vmsJavaOptionFile.getPath()};
  307. exe.setCommandline(vmsCmd);
  308. } catch (IOException e) {
  309. throw new BuildException("Failed to create a temporary file for \"-V\" switch");
  310. }
  311. }
  312. }