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. package org.apache.commons.launcher;
  17. import java.io.File;
  18. import java.io.FileOutputStream;
  19. import java.io.IOException;
  20. import java.net.URL;
  21. import java.net.URLClassLoader;
  22. import java.util.ArrayList;
  23. import java.util.HashMap;
  24. import java.util.Iterator;
  25. import java.util.StringTokenizer;
  26. import org.apache.commons.launcher.types.ArgumentSet;
  27. import org.apache.commons.launcher.types.ConditionalArgument;
  28. import org.apache.commons.launcher.types.ConditionalVariable;
  29. import org.apache.commons.launcher.types.JVMArgumentSet;
  30. import org.apache.commons.launcher.types.SysPropertySet;
  31. import org.apache.tools.ant.BuildException;
  32. import org.apache.tools.ant.Task;
  33. import org.apache.tools.ant.types.Path;
  34. import org.apache.tools.ant.types.Reference;
  35. /**
  36. * A class that eliminates the need for a batch or shell script to launch a Java
  37. * class. Some situations where elimination of a batch or shell script may be
  38. * desirable are:
  39. * <ul>
  40. * <li>You want to avoid having to determining where certain application paths
  41. * are e.g. your application's home directory, etc. Determining this
  42. * dynamically in a Windows batch scripts is very tricky on some versions of
  43. * Windows or when softlinks are used on Unix platforms.
  44. * <li>You want to avoid having to handle native file and path separators or
  45. * native path quoting issues.
  46. * <li>You need to enforce certain system properties e.g.
  47. * <code>java.endorsed.dirs</code> when running with JDK 1.4.
  48. * <li>You want to allow users to pass in custom JVM arguments or system
  49. * properties without having to parse and reorder arguments in your script.
  50. * This can be tricky and/or messy in batch and shell scripts.
  51. * <li>You want to bootstrap system properties from a configuration file instead
  52. * hard-coding them in your batch and shell scripts.
  53. * <li>You want to provide localized error messages which is very tricky to do
  54. * in batch and shell scripts.
  55. * </ul>
  56. *
  57. * @author Patrick Luby
  58. */
  59. public class LaunchTask extends Task {
  60. //----------------------------------------------------------- Static Fields
  61. /**
  62. * The argument property name.
  63. */
  64. public final static String ARG_PROP_NAME = "launch.arg.";
  65. /**
  66. * The name of this task.
  67. */
  68. public final static String TASK_NAME = "launch";
  69. /**
  70. * Cached synchronous child processes for all instances of this class.
  71. */
  72. private static ArrayList childProcesses = new ArrayList();
  73. //------------------------------------------------------------------ Fields
  74. /**
  75. * Cached appendOutput flag.
  76. */
  77. private boolean appendOutput = false;
  78. /**
  79. * Cached synchronously executing child process.
  80. */
  81. private Process childProc = null;
  82. /**
  83. * Cached classpath.
  84. */
  85. private Path classpath = null;
  86. /**
  87. * Cached debug flag.
  88. */
  89. private boolean debug = false;
  90. /**
  91. * Cached displayMinimizedWindow flag.
  92. */
  93. private boolean displayMinimizedWindow = false;
  94. /**
  95. * Cached disposeMinimizedWindow flag.
  96. */
  97. private boolean disposeMinimizedWindow = true;
  98. /**
  99. * Cached failOnError flag.
  100. */
  101. private boolean failOnError = false;
  102. /**
  103. * Cached filter instance.
  104. */
  105. private LaunchFilter filter = null;
  106. /**
  107. * Cached filterClassName.
  108. */
  109. private String filterClassName = null;
  110. /**
  111. * Cached filterClasspath.
  112. */
  113. private Path filterClasspath = null;
  114. /**
  115. * Cached main class name.
  116. */
  117. private String mainClassName = null;
  118. /**
  119. * Cached minimizedWindowIcon.
  120. */
  121. private File minimizedWindowIcon = null;
  122. /**
  123. * Cached minimizedWindowTitle.
  124. */
  125. private String minimizedWindowTitle = null;
  126. /**
  127. * Cached output file.
  128. */
  129. private File outputFile = null;
  130. /**
  131. * Cached print flag.
  132. */
  133. private boolean print = false;
  134. /**
  135. * Cached redirect flag.
  136. */
  137. private boolean redirect = false;
  138. /**
  139. * Cached requireTools flag.
  140. */
  141. private boolean requireTools = false;
  142. /**
  143. * Cached arg elements
  144. */
  145. private ArgumentSet taskArgumentSet = new ArgumentSet();
  146. /**
  147. * Cached jvmarg elements
  148. */
  149. private JVMArgumentSet taskJVMArgumentSet = new JVMArgumentSet();
  150. /**
  151. * Cached sysproperty elements
  152. */
  153. private SysPropertySet taskSysPropertySet = new SysPropertySet();
  154. /**
  155. * Cached useArgs flag.
  156. */
  157. private boolean useArgs = true;
  158. /**
  159. * Cached useSystemIn flag.
  160. */
  161. private boolean useSystemIn = true;
  162. /**
  163. * Cached waitForChild flag.
  164. */
  165. private boolean waitForChild = true;
  166. //---------------------------------------------------------- Static Methods
  167. /**
  168. * Get the synchronous child processes for all instances of this class.
  169. *
  170. * @return the instances of this class.
  171. */
  172. public static Process[] getChildProcesses() {
  173. return (Process[])childProcesses.toArray(new Process[childProcesses.size()]);
  174. }
  175. //----------------------------------------------------------------- Methods
  176. /**
  177. * Add a nested arg element. Note that Ant will not invoke the specified
  178. * arg object's setter methods until after Ant invokes this method so
  179. * processing of the specified arg object is handled in the
  180. * {@link #execute()} method.
  181. *
  182. * @param arg the arg element
  183. */
  184. public void addArg(ConditionalArgument arg) {
  185. taskArgumentSet.addArg(arg);
  186. }
  187. /**
  188. * Add a nested argset element.
  189. *
  190. * @param set the argset element
  191. */
  192. public void addArgset(ArgumentSet set) {
  193. taskArgumentSet.addArgset(set);
  194. }
  195. /**
  196. * Add a nested jvmarg element. Note that Ant will not invoke the specified
  197. * jvmarg object's setter methods until after Ant invokes this method so
  198. * processing of the specified jvmarg object is handled in the
  199. * {@link #execute()} method.
  200. *
  201. * @param jvmArg the jvmarg element
  202. */
  203. public void addJvmarg(ConditionalArgument jvmArg) {
  204. taskJVMArgumentSet.addJvmarg(jvmArg);
  205. }
  206. /**
  207. * Add a nested jvmargset element.
  208. *
  209. * @param set the jvmargset element
  210. */
  211. public void addJvmargset(JVMArgumentSet set) {
  212. taskJVMArgumentSet.addJvmargset(set);
  213. }
  214. /**
  215. * Add a nested sysproperty element. Note that Ant will not invoke the
  216. * specified sysproperty object's setter methods until after Ant invokes
  217. * this method so processing of the specified sysproperty object is handled
  218. * in the {@link #execute()} method.
  219. *
  220. * @param var the sysproperty element
  221. */
  222. public void addSysproperty(ConditionalVariable var) {
  223. taskSysPropertySet.addSysproperty(var);
  224. }
  225. /**
  226. * Add a nested syspropertyset element.
  227. *
  228. * @param set the syspropertyset element
  229. */
  230. public void addSyspropertyset(SysPropertySet set) {
  231. taskSysPropertySet.addSyspropertyset(set);
  232. }
  233. /**
  234. * Create a nested classpath element.
  235. *
  236. * @return the Path object that contains all nested classpath elements
  237. */
  238. public Path createClasspath() {
  239. if (classpath == null)
  240. classpath = new Path(project);
  241. return classpath;
  242. }
  243. /**
  244. * Create a nested filter classpath element.
  245. *
  246. * @return the Path object that contains all nested filter classpath
  247. * elements
  248. */
  249. public Path createFilterclasspath() {
  250. if (filterClasspath == null)
  251. filterClasspath = new Path(project);
  252. return filterClasspath;
  253. }
  254. /**
  255. * Construct a Java command and execute it using the settings that Ant
  256. * parsed from the Launcher's XML file. This method is called by the Ant
  257. * classes.
  258. *
  259. * @throws BuildException if there is a configuration or other error
  260. */
  261. public void execute() throws BuildException {
  262. try {
  263. // Check that the Launcher class was used to start Ant as this
  264. // task is not designed to use in a standalone Ant installation
  265. if (!Launcher.isStarted())
  266. throw new BuildException(Launcher.getLocalizedString("no.run.standalone", this.getClass().getName()));
  267. // Don't do anything if the launching process has been stopped
  268. if (Launcher.isStopped())
  269. throw new BuildException();
  270. if (mainClassName == null)
  271. throw new BuildException(Launcher.getLocalizedString("classname.null", this.getClass().getName()));
  272. // Copy all of the nested jvmarg elements into the jvmArgs object
  273. ArrayList taskJVMArgs = taskJVMArgumentSet.getList();
  274. ArrayList jvmArgs = new ArrayList(taskJVMArgs.size());
  275. for (int i = 0; i < taskJVMArgs.size(); i++) {
  276. ConditionalArgument value = (ConditionalArgument)taskJVMArgs.get(i);
  277. // Test "if" and "unless" conditions
  278. if (testIfCondition(value.getIf()) && testUnlessCondition(value.getUnless())) {
  279. String[] list = value.getParts();
  280. for (int j = 0; j < list.length; j++)
  281. jvmArgs.add(list[j]);
  282. }
  283. }
  284. // Copy all of the nested sysproperty elements into the sysProps
  285. // object
  286. ArrayList taskSysProps = taskSysPropertySet.getList();
  287. HashMap sysProps = new HashMap(taskSysProps.size());
  288. for (int i = 0; i < taskSysProps.size(); i++) {
  289. ConditionalVariable variable = (ConditionalVariable)taskSysProps.get(i);
  290. // Test "if" and "unless" conditions
  291. if (testIfCondition(variable.getIf()) && testUnlessCondition(variable.getUnless()))
  292. sysProps.put(variable.getKey(), variable.getValue());
  293. }
  294. // Copy all of the nested arg elements into the appArgs object
  295. ArrayList taskArgs = taskArgumentSet.getList();
  296. ArrayList appArgs = new ArrayList(taskArgs.size());
  297. for (int i = 0; i < taskArgs.size(); i++) {
  298. ConditionalArgument value = (ConditionalArgument)taskArgs.get(i);
  299. // Test "if" and "unless" conditions
  300. if (testIfCondition(value.getIf()) && testUnlessCondition(value.getUnless())) {
  301. String[] list = value.getParts();
  302. for (int j = 0; j < list.length; j++)
  303. appArgs.add(list[j]);
  304. }
  305. }
  306. // Add the Launcher's command line arguments to the appArgs object
  307. if (useArgs) {
  308. int currentArg = 0;
  309. String arg = null;
  310. while ((arg = project.getUserProperty(LaunchTask.ARG_PROP_NAME + Integer.toString(currentArg++))) != null)
  311. appArgs.add(arg);
  312. }
  313. // Make working copies of some of the flags since they may get
  314. // changed by a filter class
  315. String filteredClasspath = null;
  316. if (classpath != null)
  317. filteredClasspath = classpath.toString();
  318. String filteredMainClassName = mainClassName;
  319. boolean filteredRedirect = redirect;
  320. File filteredOutputFile = outputFile;
  321. boolean filteredAppendOutput = appendOutput;
  322. boolean filteredDebug = debug;
  323. boolean filteredDisplayMinimizedWindow = displayMinimizedWindow;
  324. boolean filteredDisposeMinimizedWindow = disposeMinimizedWindow;
  325. boolean filteredFailOnError = failOnError;
  326. String filteredMinimizedWindowTitle = minimizedWindowTitle;
  327. File filteredMinimizedWindowIcon = minimizedWindowIcon;
  328. boolean filteredPrint = print;
  329. boolean filteredRequireTools = requireTools;
  330. boolean filteredUseSystemIn = useSystemIn;
  331. boolean filteredWaitForChild = waitForChild;
  332. // If there is a filter in the filterclassname attribute, let it
  333. // evaluate and edit the attributes and nested elements before we
  334. // start evaluating them
  335. if (filterClassName != null) {
  336. if (filter == null) {
  337. try {
  338. ClassLoader loader = this.getClass().getClassLoader();
  339. if (filterClasspath != null) {
  340. // Construct a class loader to load the class
  341. String[] fileList = filterClasspath.list();
  342. URL[] urls = new URL[fileList.length];
  343. for (int i = 0; i < fileList.length; i++)
  344. urls[i] = new File(fileList[i]).toURL();
  345. loader = new URLClassLoader(urls, loader);
  346. }
  347. Class filterClass = loader.loadClass(filterClassName);
  348. filter = (LaunchFilter)filterClass.newInstance();
  349. // Execute filter and save any changes
  350. LaunchCommand command = new LaunchCommand();
  351. command.setJvmargs(jvmArgs);
  352. command.setSysproperties(sysProps);
  353. command.setArgs(appArgs);
  354. command.setClasspath(filteredClasspath);
  355. command.setClassname(filteredMainClassName);
  356. command.setRedirectoutput(filteredRedirect);
  357. command.setOutput(filteredOutputFile);
  358. command.setAppendoutput(filteredAppendOutput);
  359. command.setDebug(filteredDebug);
  360. command.setDisplayminimizedwindow(filteredDisplayMinimizedWindow);
  361. command.setDisposeminimizedwindow(filteredDisposeMinimizedWindow);
  362. command.setFailonerror(filteredFailOnError);
  363. command.setMinimizedwindowtitle(filteredMinimizedWindowTitle);
  364. command.setMinimizedwindowicon(filteredMinimizedWindowIcon);
  365. command.setPrint(filteredPrint);
  366. command.setRequiretools(filteredRequireTools);
  367. command.setUsesystemin(filteredUseSystemIn);
  368. command.setWaitforchild(filteredWaitForChild);
  369. filter.filter(command);
  370. jvmArgs = command.getJvmargs();
  371. sysProps = command.getSysproperties();
  372. appArgs = command.getArgs();
  373. filteredClasspath = command.getClasspath();
  374. filteredMainClassName = command.getClassname();
  375. filteredRedirect = command.getRedirectoutput();
  376. filteredOutputFile = command.getOutput();
  377. filteredAppendOutput = command.getAppendoutput();
  378. filteredDebug = command.getDebug();
  379. filteredDisplayMinimizedWindow = command.getDisplayminimizedwindow();
  380. filteredDisposeMinimizedWindow = command.getDisposeminimizedwindow();
  381. filteredFailOnError = command.getFailonerror();
  382. filteredMinimizedWindowTitle = command.getMinimizedwindowtitle();
  383. filteredMinimizedWindowIcon = command.getMinimizedwindowicon();
  384. filteredPrint = command.getPrint();
  385. filteredRequireTools = command.getRequiretools();
  386. filteredUseSystemIn = command.getUsesystemin();
  387. filteredWaitForChild = command.getWaitforchild();
  388. // Check changes
  389. if (filteredMainClassName == null)
  390. throw new BuildException(Launcher.getLocalizedString("classname.null", this.getClass().getName()));
  391. if (jvmArgs == null)
  392. jvmArgs = new ArrayList();
  393. if (sysProps == null)
  394. sysProps = new HashMap();
  395. if (appArgs == null)
  396. appArgs = new ArrayList();
  397. } catch (BuildException be) {
  398. throw new BuildException(filterClassName + " " + Launcher.getLocalizedString("filter.exception", this.getClass().getName()), be);
  399. } catch (ClassCastException cce) {
  400. throw new BuildException(filterClassName + " " + Launcher.getLocalizedString("filter.not.filter", this.getClass().getName()));
  401. } catch (Exception e) {
  402. throw new BuildException(e);
  403. }
  404. }
  405. }
  406. // Force child JVM into foreground if running using JDB
  407. if (filteredDebug) {
  408. filteredWaitForChild = true;
  409. filteredUseSystemIn = true;
  410. }
  411. // Prepend standard paths to classpath
  412. StringBuffer fullClasspath = new StringBuffer(Launcher.getBootstrapFile().getPath());
  413. if (filteredRequireTools) {
  414. fullClasspath.append(File.pathSeparator);
  415. fullClasspath.append(Launcher.getToolsClasspath());
  416. }
  417. if (filteredClasspath != null) {
  418. fullClasspath.append(File.pathSeparator);
  419. fullClasspath.append(filteredClasspath);
  420. }
  421. // Set ChildMain.WAIT_FOR_CHILD_PROP_NAME property for child JVM
  422. sysProps.remove(ChildMain.WAIT_FOR_CHILD_PROP_NAME);
  423. if (filteredWaitForChild)
  424. sysProps.put(ChildMain.WAIT_FOR_CHILD_PROP_NAME, "");
  425. // Set minimized window properties for child JVM
  426. sysProps.remove(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME);
  427. sysProps.remove(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME);
  428. sysProps.remove(ChildMain.MINIMIZED_WINDOW_ICON_PROP_NAME);
  429. sysProps.remove(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME);
  430. if (!filteredWaitForChild && filteredDisplayMinimizedWindow) {
  431. sysProps.put(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME, "");
  432. if (filteredMinimizedWindowTitle != null)
  433. sysProps.put(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME, filteredMinimizedWindowTitle);
  434. else
  435. sysProps.put(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME, getOwningTarget().getName());
  436. if (filteredMinimizedWindowIcon != null)
  437. sysProps.put(ChildMain.MINIMIZED_WINDOW_ICON_PROP_NAME, filteredMinimizedWindowIcon.getCanonicalPath());
  438. // Set ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME property
  439. if (filteredDisposeMinimizedWindow)
  440. sysProps.put(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME, "");
  441. }
  442. // Set ChildMain.OUTPUT_FILE_PROP_NAME property for child JVM
  443. sysProps.remove(ChildMain.OUTPUT_FILE_PROP_NAME);
  444. if (!filteredWaitForChild && filteredRedirect) {
  445. if (filteredOutputFile != null) {
  446. String outputFilePath = filteredOutputFile.getCanonicalPath();
  447. // Verify that we can write to the output file
  448. try {
  449. File parentFile = new File(filteredOutputFile.getParent());
  450. // To take care of non-existent log directories
  451. if ( !parentFile.exists() ) {
  452. //Trying to create non-existent parent directories
  453. parentFile.mkdirs();
  454. //If this fails createNewFile also fails
  455. //We can give more exact error message, if we choose
  456. }
  457. filteredOutputFile.createNewFile();
  458. } catch (IOException ioe) {
  459. throw new BuildException(outputFilePath + " " + Launcher.getLocalizedString("output.file.not.creatable", this.getClass().getName()));
  460. }
  461. if (!filteredOutputFile.canWrite())
  462. throw new BuildException(outputFilePath + " " + Launcher.getLocalizedString("output.file.not.writable", this.getClass().getName()));
  463. sysProps.put(ChildMain.OUTPUT_FILE_PROP_NAME, outputFilePath);
  464. if (filteredAppendOutput)
  465. sysProps.put(ChildMain.APPEND_OUTPUT_PROP_NAME, "");
  466. Launcher.getLog().println(Launcher.getLocalizedString("redirect.notice", this.getClass().getName()) + " " + outputFilePath);
  467. } else {
  468. throw new BuildException(Launcher.getLocalizedString("output.file.null", this.getClass().getName()));
  469. }
  470. }
  471. // Create the heartbeatFile. This file is needed by the
  472. // ParentListener class on Windows since the entire child JVM
  473. // process will block on Windows machines using some versions of
  474. // Unix shells such as MKS, etc.
  475. File heartbeatFile = null;
  476. FileOutputStream heartbeatOutputStream = null;
  477. if (filteredWaitForChild) {
  478. File tmpDir = null;
  479. String tmpDirName = (String)sysProps.get("java.io.tmpdir");
  480. if (tmpDirName != null)
  481. tmpDir = new File(tmpDirName);
  482. heartbeatFile = File.createTempFile(ChildMain.HEARTBEAT_FILE_PROP_NAME + ".", "", tmpDir);
  483. // Open the heartbeat file for writing so that it the child JVM
  484. // will not be able to delete it while this process is running
  485. heartbeatOutputStream = new FileOutputStream(heartbeatFile);
  486. sysProps.put(ChildMain.HEARTBEAT_FILE_PROP_NAME, heartbeatFile.getCanonicalPath());
  487. }
  488. // Assemble child command
  489. String[] cmd = new String[5 + jvmArgs.size() + sysProps.size() + appArgs.size()];
  490. int nextCmdArg = 0;
  491. if (filteredDebug)
  492. cmd[nextCmdArg++] = Launcher.getJDBCommand();
  493. else
  494. cmd[nextCmdArg++] = Launcher.getJavaCommand();
  495. // Add jvmArgs to command
  496. for (int i = 0; i < jvmArgs.size(); i++)
  497. cmd[nextCmdArg++] = (String)jvmArgs.get(i);
  498. // Add properties to command
  499. Iterator sysPropsKeys = sysProps.keySet().iterator();
  500. while (sysPropsKeys.hasNext()) {
  501. String key = (String)sysPropsKeys.next();
  502. if (key == null)
  503. continue;
  504. String value = (String)sysProps.get(key);
  505. if (value == null)
  506. value = "";
  507. cmd[nextCmdArg++] = "-D" + key + "=" + value;
  508. }
  509. // Add classpath to command. Note that it is after the jvmArgs
  510. // and system properties to prevent the user from sneaking in an
  511. // alterate classpath through the jvmArgs.
  512. cmd[nextCmdArg++] = "-classpath";
  513. cmd[nextCmdArg++] = fullClasspath.toString();
  514. // Add main class to command
  515. int mainClassArg = nextCmdArg;
  516. cmd[nextCmdArg++] = ChildMain.class.getName();
  517. cmd[nextCmdArg++] = filteredMainClassName;
  518. // Add args to command
  519. for (int i = 0; i < appArgs.size(); i++)
  520. {
  521. cmd[nextCmdArg++] = (String)appArgs.get(i);
  522. }
  523. // Print command
  524. if (filteredPrint) {
  525. // Quote the command arguments
  526. String osname = System.getProperty("os.name").toLowerCase();
  527. StringBuffer buf = new StringBuffer(cmd.length * 100);
  528. String quote = null;
  529. String replaceQuote = null;
  530. if (osname.indexOf("windows") >= 0) {
  531. // Use double-quotes to quote on Windows
  532. quote = "\"";
  533. replaceQuote = quote + quote + quote;
  534. } else {
  535. // Use single-quotes to quote on Unix
  536. quote = "'";
  537. replaceQuote = quote + "\\" + quote + quote;
  538. }
  539. for (int i = 0; i < cmd.length; i++) {
  540. // Pull ChildMain out of command as we want to print the
  541. // real JVM command that can be executed by the user
  542. if (i == mainClassArg)
  543. continue;
  544. if (i > 0)
  545. buf.append(" ");
  546. buf.append(quote);
  547. StringTokenizer tokenizer = new StringTokenizer(cmd[i], quote, true);
  548. while (tokenizer.hasMoreTokens()) {
  549. String token = tokenizer.nextToken();
  550. if (quote.equals(token))
  551. buf.append(replaceQuote);
  552. else
  553. buf.append(token);
  554. }
  555. buf.append(quote);
  556. }
  557. // Print the quoted command
  558. System.err.println(Launcher.getLocalizedString("executing.child.command", this.getClass().getName()) + ":");
  559. System.err.println(buf.toString());
  560. }
  561. // Create a child JVM
  562. if (Launcher.isStopped())
  563. throw new BuildException();
  564. Process proc = null;
  565. synchronized (LaunchTask.childProcesses) {
  566. proc = Runtime.getRuntime().exec(cmd);
  567. // Add the synchronous child process
  568. if (filteredWaitForChild) {
  569. childProc = proc;
  570. LaunchTask.childProcesses.add(proc);
  571. }
  572. }
  573. if (filteredWaitForChild) {
  574. StreamConnector stdout =
  575. new StreamConnector(proc.getInputStream(), System.out);
  576. StreamConnector stderr =
  577. new StreamConnector(proc.getErrorStream(), System.err);
  578. stdout.start();
  579. stderr.start();
  580. if (filteredUseSystemIn) {
  581. StreamConnector stdin =
  582. new StreamConnector(System.in, proc.getOutputStream());
  583. stdin.start();
  584. }
  585. proc.waitFor();
  586. // Let threads flush any unflushed output
  587. stdout.join();
  588. stderr.join();
  589. if (heartbeatOutputStream != null)
  590. heartbeatOutputStream.close();
  591. if (heartbeatFile != null)
  592. heartbeatFile.delete();
  593. int exitValue = proc.exitValue();
  594. if (filteredFailOnError && exitValue != 0)
  595. throw new BuildException(Launcher.getLocalizedString("child.failed", this.getClass().getName()) + " " + exitValue);
  596. }
  597. // Need to check if the launching process has stopped because
  598. // processes don't throw exceptions when they are terminated
  599. if (Launcher.isStopped())
  600. throw new BuildException();
  601. } catch (BuildException be) {
  602. throw be;
  603. } catch (Exception e) {
  604. if (Launcher.isStopped())
  605. throw new BuildException(Launcher.getLocalizedString("launch.task.stopped", this.getClass().getName()));
  606. else
  607. throw new BuildException(e);
  608. }
  609. }
  610. /**
  611. * Set the useArgs flag. Setting this flag to true will cause this
  612. * task to append all of the command line arguments used to start the
  613. * {@link Launcher#start(String[])} method to the arguments
  614. * passed to the child JVM.
  615. *
  616. * @param useArgs the useArgs flag
  617. */
  618. public void setUseargs(boolean useArgs) {
  619. this.useArgs = useArgs;
  620. }
  621. /**
  622. * Set the useSystemIn flag. Setting this flag to false will cause this
  623. * task to not read System.in. This will cause the child JVM to never
  624. * receive any bytes when it reads System.in. Setting this flag to false
  625. * is useful in some Unix environments where processes cannot be put in
  626. * the background when they read System.in.
  627. *
  628. * @param useSystemIn the useSystemIn flag
  629. */
  630. public void setUsesystemin(boolean useSystemIn) {
  631. this.useSystemIn = useSystemIn;
  632. }
  633. /**
  634. * Set the waitForChild flag. Setting this flag to true will cause this
  635. * task to wait for the child JVM to finish executing before the task
  636. * completes. Setting this flag to false will cause this task to complete
  637. * immediately after it starts the execution of the child JVM. Setting it
  638. * false emulates the "&" background operator in most Unix shells and is
  639. * most of set to false when launching server or GUI applications.
  640. *
  641. * @param waitForChild the waitForChild flag
  642. */
  643. public void setWaitforchild(boolean waitForChild) {
  644. this.waitForChild = waitForChild;
  645. }
  646. /**
  647. * Set the class name.
  648. *
  649. * @param mainClassName the class to execute <code>main(String[])</code>
  650. */
  651. public void setClassname(String mainClassName) {
  652. this.mainClassName = mainClassName;
  653. }
  654. /**
  655. * Set the classpath.
  656. *
  657. * @param classpath the classpath
  658. */
  659. public void setClasspath(Path classpath) {
  660. createClasspath().append(classpath);
  661. }
  662. /**
  663. * Adds a reference to a classpath defined elsewhere.
  664. *
  665. * @param ref reference to the classpath
  666. */
  667. public void setClasspathref(Reference ref) {
  668. createClasspath().setRefid(ref);
  669. }
  670. /**
  671. * Set the debug flag. Setting this flag to true will cause this
  672. * task to run the child JVM using the JDB debugger.
  673. *
  674. * @param debug the debug flag
  675. */
  676. public void setDebug(boolean debug) {
  677. this.debug = debug;
  678. }
  679. /**
  680. * Set the displayMinimizedWindow flag. Note that this flag has no effect
  681. * on non-Windows platforms. On Windows platform, setting this flag to true
  682. * will cause a minimized window to be displayed in the Windows task bar
  683. * while the child process is executing. This flag is usually set to true
  684. * for server applications that also have their "waitForChild" attribute
  685. * set to false via the {@link #setWaitforchild(boolean)} method.
  686. *
  687. * @param displayMinimizedWindow true if a minimized window should be
  688. * displayed in the Windows task bar while the child process is executing
  689. */
  690. public void setDisplayminimizedwindow(boolean displayMinimizedWindow) {
  691. this.displayMinimizedWindow = displayMinimizedWindow;
  692. }
  693. /**
  694. * Set the disposeMinimizedWindow flag. Note that this flag has no effect
  695. * on non-Windows platforms. On Windows platform, setting this flag to true
  696. * will cause any minimized window that is display by setting the
  697. * "displayMinimizedWindow" attribute to true via the
  698. * {@link #setDisplayminimizedwindow(boolean)} to be automatically
  699. * disposed of when the child JVM's <code>main(String[])</code> returns.
  700. * This flag is normally used for applications that don't explicitly call
  701. * {@link System#exit(int)}. If an application does not explicitly call
  702. * {@link System#exit(int)}, an minimized windows need to be disposed of
  703. * for the child JVM to exit.
  704. *
  705. * @param disposeMinimizedWindow true if a minimized window in the Windows
  706. * taskbar should be automatically disposed of after the child JVM's
  707. * <code>main(String[])</code> returns
  708. */
  709. public void setDisposeminimizedwindow(boolean disposeMinimizedWindow) {
  710. this.disposeMinimizedWindow = disposeMinimizedWindow;
  711. }
  712. /**
  713. * Set the failOnError flag.
  714. *
  715. * @param failOnError true if the launch process should stop if the child
  716. * JVM returns an exit value other than 0
  717. */
  718. public void setFailonerror(boolean failOnError) {
  719. this.failOnError = failOnError;
  720. }
  721. /**
  722. * Set the filter class name.
  723. *
  724. * @param filterClassName the class that implements the
  725. * {@link LaunchFilter} interface
  726. */
  727. public void setFilterclassname(String filterClassName) {
  728. this.filterClassName = filterClassName;
  729. }
  730. /**
  731. * Set the filter class' classpath.
  732. *
  733. * @param classpath the classpath for the filter class
  734. */
  735. public void setFilterclasspath(Path filterClasspath) {
  736. createFilterclasspath().append(filterClasspath);
  737. }
  738. /**
  739. * Set the title for the minimized window that will be displayed in the
  740. * Windows taskbar. Note that this property has no effect on non-Windows
  741. * platforms.
  742. *
  743. * @param minimizedWindowTitle the title to set for any minimized window
  744. * that is displayed in the Windows taskbar
  745. */
  746. public void setMinimizedwindowtitle(String minimizedWindowTitle) {
  747. this.minimizedWindowTitle = minimizedWindowTitle;
  748. }
  749. /**
  750. * Set the icon file for the minimized window that will be displayed in the
  751. * Windows taskbar. Note that this property has no effect on non-Windows
  752. * platforms.
  753. *
  754. * @param minimizedWindowIcon the icon file to use for any minimized window
  755. * that is displayed in the Windows taskbar
  756. */
  757. public void setMinimizedwindowicon(File minimizedWindowIcon) {
  758. this.minimizedWindowIcon = minimizedWindowIcon;
  759. }
  760. /**
  761. * Set the file that the child JVM's System.out and System.err will be
  762. * redirected to. Output will only be redirected if the redirect flag
  763. * is set to true via the {@link #setRedirectoutput(boolean)} method.
  764. *
  765. * @param outputFile a File to redirect System.out and System.err to
  766. */
  767. public void setOutput(File outputFile) {
  768. this.outputFile = outputFile;
  769. }
  770. /**
  771. * Set the print flag. Setting this flag to true will cause the full child
  772. * JVM command to be printed to {@link System#out}.
  773. *
  774. * @param print the print flag
  775. */
  776. public void setPrint(boolean print) {
  777. this.print = print;
  778. }
  779. /**
  780. * Set the appendOutput flag. Setting this flag to true will cause the child
  781. * JVM to append System.out and System.err to the file specified by the
  782. * {@link #setOutput(File)} method. Setting this flag to false will cause
  783. * the child to overwrite the file.
  784. *
  785. * @param appendOutput true if output should be appended to the output file
  786. */
  787. public void setAppendoutput(boolean appendOutput) {
  788. this.appendOutput = appendOutput;
  789. }
  790. /**
  791. * Set the redirect flag. Setting this flag to true will cause the child
  792. * JVM's System.out and System.err to be redirected to file set using the
  793. * {@link #setOutput(File)} method. Setting this flag to false will
  794. * cause no redirection.
  795. *
  796. * @param redirect true if System.out and System.err should be redirected
  797. */
  798. public void setRedirectoutput(boolean redirect) {
  799. this.redirect = redirect;
  800. }
  801. /**
  802. * Set the requireTools flag. Setting this flag to true will cause the
  803. * JVM's tools.jar to be added to the child JVM's classpath. This
  804. * sets an explicit requirement that the user use a JDK instead of a
  805. * JRE. Setting this flag to false explicitly allows the user to use
  806. * a JRE.
  807. *
  808. * @param redirect true if a JDK is required and false if only a JRE
  809. * is required
  810. */
  811. public void setRequiretools(boolean requireTools) {
  812. this.requireTools = requireTools;
  813. }
  814. /**
  815. * Determine if the "if" condition flag for a nested element meets all
  816. * criteria for use.
  817. *
  818. * @param ifCondition the "if" condition flag for a nested element
  819. * @return true if the nested element should be process and false if it
  820. * should be ignored
  821. */
  822. private boolean testIfCondition(String ifCondition) {
  823. if (ifCondition == null || "".equals(ifCondition))
  824. return true;
  825. return project.getProperty(ifCondition) != null;
  826. }
  827. /**
  828. * Determine if the "unless" condition flag for a nested element meets all
  829. * criteria for use.
  830. *
  831. * @param unlessCondition the "unless" condition flag for a nested element
  832. * @return true if the nested element should be process and false if it
  833. * should be ignored
  834. */
  835. private boolean testUnlessCondition(String unlessCondition) {
  836. if (unlessCondition == null || "".equals(unlessCondition))
  837. return true;
  838. return project.getProperty(unlessCondition) == null;
  839. }
  840. }