001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.launcher; 019 020import java.io.File; 021import java.io.FileOutputStream; 022import java.io.IOException; 023import java.net.URL; 024import java.net.URLClassLoader; 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.Iterator; 028import java.util.StringTokenizer; 029import org.apache.commons.launcher.types.ArgumentSet; 030import org.apache.commons.launcher.types.ConditionalArgument; 031import org.apache.commons.launcher.types.ConditionalVariable; 032import org.apache.commons.launcher.types.JVMArgumentSet; 033import org.apache.commons.launcher.types.SysPropertySet; 034import org.apache.tools.ant.BuildException; 035import org.apache.tools.ant.Task; 036import org.apache.tools.ant.types.Path; 037import org.apache.tools.ant.types.Reference; 038 039/** 040 * A class that eliminates the need for a batch or shell script to launch a Java 041 * class. Some situations where elimination of a batch or shell script may be 042 * desirable are: 043 * <ul> 044 * <li>You want to avoid having to determining where certain application paths 045 * are e.g. your application's home directory, etc. Determining this 046 * dynamically in a Windows batch scripts is very tricky on some versions of 047 * Windows or when softlinks are used on Unix platforms. 048 * <li>You want to avoid having to handle native file and path separators or 049 * native path quoting issues. 050 * <li>You need to enforce certain system properties e.g. 051 * <code>java.endorsed.dirs</code> when running with JDK 1.4. 052 * <li>You want to allow users to pass in custom JVM arguments or system 053 * properties without having to parse and reorder arguments in your script. 054 * This can be tricky and/or messy in batch and shell scripts. 055 * <li>You want to bootstrap system properties from a configuration file instead 056 * hard-coding them in your batch and shell scripts. 057 * <li>You want to provide localized error messages which is very tricky to do 058 * in batch and shell scripts. 059 * </ul> 060 * 061 * @author Patrick Luby 062 */ 063public class LaunchTask extends Task { 064 065 //----------------------------------------------------------- Static Fields 066 067 /** 068 * The argument property name. 069 */ 070 public final static String ARG_PROP_NAME = "launch.arg."; 071 072 /** 073 * The name of this task. 074 */ 075 public final static String TASK_NAME = "launch"; 076 077 /** 078 * Cached synchronous child processes for all instances of this class. 079 */ 080 private static ArrayList childProcesses = new ArrayList(); 081 082 //------------------------------------------------------------------ Fields 083 084 /** 085 * Cached appendOutput flag. 086 */ 087 private boolean appendOutput = false; 088 089 /** 090 * Cached synchronously executing child process. 091 */ 092 private Process childProc = null; 093 094 /** 095 * Cached classpath. 096 */ 097 private Path classpath = null; 098 099 /** 100 * Cached debug flag. 101 */ 102 private boolean debug = false; 103 104 /** 105 * Cached displayMinimizedWindow flag. 106 */ 107 private boolean displayMinimizedWindow = false; 108 109 /** 110 * Cached disposeMinimizedWindow flag. 111 */ 112 private boolean disposeMinimizedWindow = true; 113 114 /** 115 * Cached failOnError flag. 116 */ 117 private boolean failOnError = false; 118 119 /** 120 * Cached filter instance. 121 */ 122 private LaunchFilter filter = null; 123 124 /** 125 * Cached filterClassName. 126 */ 127 private String filterClassName = null; 128 129 /** 130 * Cached filterClasspath. 131 */ 132 private Path filterClasspath = null; 133 134 /** 135 * Cached main class name. 136 */ 137 private String mainClassName = null; 138 139 /** 140 * Cached minimizedWindowIcon. 141 */ 142 private File minimizedWindowIcon = null; 143 144 /** 145 * Cached minimizedWindowTitle. 146 */ 147 private String minimizedWindowTitle = null; 148 149 /** 150 * Cached output file. 151 */ 152 private File outputFile = null; 153 154 /** 155 * Cached print flag. 156 */ 157 private boolean print = false; 158 159 /** 160 * Cached redirect flag. 161 */ 162 private boolean redirect = false; 163 164 /** 165 * Cached requireTools flag. 166 */ 167 private boolean requireTools = false; 168 169 /** 170 * Cached arg elements 171 */ 172 private ArgumentSet taskArgumentSet = new ArgumentSet(); 173 174 /** 175 * Cached jvmarg elements 176 */ 177 private JVMArgumentSet taskJVMArgumentSet = new JVMArgumentSet(); 178 179 /** 180 * Cached sysproperty elements 181 */ 182 private SysPropertySet taskSysPropertySet = new SysPropertySet(); 183 184 /** 185 * Cached useArgs flag. 186 */ 187 private boolean useArgs = true; 188 189 /** 190 * Cached useSystemIn flag. 191 */ 192 private boolean useSystemIn = true; 193 194 /** 195 * Cached waitForChild flag. 196 */ 197 private boolean waitForChild = true; 198 199 //---------------------------------------------------------- Static Methods 200 201 /** 202 * Get the synchronous child processes for all instances of this class. 203 * 204 * @return the instances of this class. 205 */ 206 public static Process[] getChildProcesses() { 207 208 return (Process[])childProcesses.toArray(new Process[childProcesses.size()]); 209 210 } 211 212 //----------------------------------------------------------------- Methods 213 214 /** 215 * Add a nested arg element. Note that Ant will not invoke the specified 216 * arg object's setter methods until after Ant invokes this method so 217 * processing of the specified arg object is handled in the 218 * {@link #execute()} method. 219 * 220 * @param arg the arg element 221 */ 222 public void addArg(ConditionalArgument arg) { 223 224 taskArgumentSet.addArg(arg); 225 226 } 227 228 /** 229 * Add a nested argset element. 230 * 231 * @param set the argset element 232 */ 233 public void addArgset(ArgumentSet set) { 234 235 taskArgumentSet.addArgset(set); 236 237 } 238 239 /** 240 * Add a nested jvmarg element. Note that Ant will not invoke the specified 241 * jvmarg object's setter methods until after Ant invokes this method so 242 * processing of the specified jvmarg object is handled in the 243 * {@link #execute()} method. 244 * 245 * @param jvmArg the jvmarg element 246 */ 247 public void addJvmarg(ConditionalArgument jvmArg) { 248 249 taskJVMArgumentSet.addJvmarg(jvmArg); 250 251 } 252 253 /** 254 * Add a nested jvmargset element. 255 * 256 * @param set the jvmargset element 257 */ 258 public void addJvmargset(JVMArgumentSet set) { 259 260 taskJVMArgumentSet.addJvmargset(set); 261 262 } 263 264 /** 265 * Add a nested sysproperty element. Note that Ant will not invoke the 266 * specified sysproperty object's setter methods until after Ant invokes 267 * this method so processing of the specified sysproperty object is handled 268 * in the {@link #execute()} method. 269 * 270 * @param var the sysproperty element 271 */ 272 public void addSysproperty(ConditionalVariable var) { 273 274 taskSysPropertySet.addSysproperty(var); 275 276 } 277 278 /** 279 * Add a nested syspropertyset element. 280 * 281 * @param set the syspropertyset element 282 */ 283 public void addSyspropertyset(SysPropertySet set) { 284 285 taskSysPropertySet.addSyspropertyset(set); 286 287 } 288 289 /** 290 * Create a nested classpath element. 291 * 292 * @return the Path object that contains all nested classpath elements 293 */ 294 public Path createClasspath() { 295 296 if (classpath == null) 297 classpath = new Path(project); 298 return classpath; 299 300 } 301 302 /** 303 * Create a nested filter classpath element. 304 * 305 * @return the Path object that contains all nested filter classpath 306 * elements 307 */ 308 public Path createFilterclasspath() { 309 310 if (filterClasspath == null) 311 filterClasspath = new Path(project); 312 return filterClasspath; 313 314 } 315 316 /** 317 * Construct a Java command and execute it using the settings that Ant 318 * parsed from the Launcher's XML file. This method is called by the Ant 319 * classes. 320 * 321 * @throws BuildException if there is a configuration or other error 322 */ 323 public void execute() throws BuildException { 324 325 try { 326 327 // Check that the Launcher class was used to start Ant as this 328 // task is not designed to use in a standalone Ant installation 329 if (!Launcher.isStarted()) 330 throw new BuildException(Launcher.getLocalizedString("no.run.standalone", this.getClass().getName())); 331 332 // Don't do anything if the launching process has been stopped 333 if (Launcher.isStopped()) 334 throw new BuildException(); 335 336 if (mainClassName == null) 337 throw new BuildException(Launcher.getLocalizedString("classname.null", this.getClass().getName())); 338 339 // Copy all of the nested jvmarg elements into the jvmArgs object 340 ArrayList taskJVMArgs = taskJVMArgumentSet.getList(); 341 ArrayList jvmArgs = new ArrayList(taskJVMArgs.size()); 342 for (int i = 0; i < taskJVMArgs.size(); i++) { 343 ConditionalArgument value = (ConditionalArgument)taskJVMArgs.get(i); 344 // Test "if" and "unless" conditions 345 if (testIfCondition(value.getIf()) && testUnlessCondition(value.getUnless())) { 346 String[] list = value.getParts(); 347 for (int j = 0; j < list.length; j++) 348 jvmArgs.add(list[j]); 349 } 350 } 351 352 // Copy all of the nested sysproperty elements into the sysProps 353 // object 354 ArrayList taskSysProps = taskSysPropertySet.getList(); 355 HashMap sysProps = new HashMap(taskSysProps.size()); 356 for (int i = 0; i < taskSysProps.size(); i++) { 357 ConditionalVariable variable = (ConditionalVariable)taskSysProps.get(i); 358 // Test "if" and "unless" conditions 359 if (testIfCondition(variable.getIf()) && testUnlessCondition(variable.getUnless())) 360 sysProps.put(variable.getKey(), variable.getValue()); 361 } 362 363 // Copy all of the nested arg elements into the appArgs object 364 ArrayList taskArgs = taskArgumentSet.getList(); 365 ArrayList appArgs = new ArrayList(taskArgs.size()); 366 for (int i = 0; i < taskArgs.size(); i++) { 367 ConditionalArgument value = (ConditionalArgument)taskArgs.get(i); 368 // Test "if" and "unless" conditions 369 if (testIfCondition(value.getIf()) && testUnlessCondition(value.getUnless())) { 370 String[] list = value.getParts(); 371 for (int j = 0; j < list.length; j++) 372 appArgs.add(list[j]); 373 } 374 } 375 376 // Add the Launcher's command line arguments to the appArgs object 377 if (useArgs) { 378 int currentArg = 0; 379 String arg = null; 380 while ((arg = project.getUserProperty(LaunchTask.ARG_PROP_NAME + Integer.toString(currentArg++))) != null) 381 appArgs.add(arg); 382 } 383 384 // Make working copies of some of the flags since they may get 385 // changed by a filter class 386 String filteredClasspath = null; 387 if (classpath != null) 388 filteredClasspath = classpath.toString(); 389 String filteredMainClassName = mainClassName; 390 boolean filteredRedirect = redirect; 391 File filteredOutputFile = outputFile; 392 boolean filteredAppendOutput = appendOutput; 393 boolean filteredDebug = debug; 394 boolean filteredDisplayMinimizedWindow = displayMinimizedWindow; 395 boolean filteredDisposeMinimizedWindow = disposeMinimizedWindow; 396 boolean filteredFailOnError = failOnError; 397 String filteredMinimizedWindowTitle = minimizedWindowTitle; 398 File filteredMinimizedWindowIcon = minimizedWindowIcon; 399 boolean filteredPrint = print; 400 boolean filteredRequireTools = requireTools; 401 boolean filteredUseSystemIn = useSystemIn; 402 boolean filteredWaitForChild = waitForChild; 403 404 // If there is a filter in the filterclassname attribute, let it 405 // evaluate and edit the attributes and nested elements before we 406 // start evaluating them 407 if (filterClassName != null) { 408 if (filter == null) { 409 try { 410 ClassLoader loader = this.getClass().getClassLoader(); 411 if (filterClasspath != null) { 412 // Construct a class loader to load the class 413 String[] fileList = filterClasspath.list(); 414 URL[] urls = new URL[fileList.length]; 415 for (int i = 0; i < fileList.length; i++) 416 urls[i] = new File(fileList[i]).toURL(); 417 loader = new URLClassLoader(urls, loader); 418 } 419 Class filterClass = loader.loadClass(filterClassName); 420 filter = (LaunchFilter)filterClass.newInstance(); 421 // Execute filter and save any changes 422 LaunchCommand command = new LaunchCommand(); 423 command.setJvmargs(jvmArgs); 424 command.setSysproperties(sysProps); 425 command.setArgs(appArgs); 426 command.setClasspath(filteredClasspath); 427 command.setClassname(filteredMainClassName); 428 command.setRedirectoutput(filteredRedirect); 429 command.setOutput(filteredOutputFile); 430 command.setAppendoutput(filteredAppendOutput); 431 command.setDebug(filteredDebug); 432 command.setDisplayminimizedwindow(filteredDisplayMinimizedWindow); 433 command.setDisposeminimizedwindow(filteredDisposeMinimizedWindow); 434 command.setFailonerror(filteredFailOnError); 435 command.setMinimizedwindowtitle(filteredMinimizedWindowTitle); 436 command.setMinimizedwindowicon(filteredMinimizedWindowIcon); 437 command.setPrint(filteredPrint); 438 command.setRequiretools(filteredRequireTools); 439 command.setUsesystemin(filteredUseSystemIn); 440 command.setWaitforchild(filteredWaitForChild); 441 filter.filter(command); 442 jvmArgs = command.getJvmargs(); 443 sysProps = command.getSysproperties(); 444 appArgs = command.getArgs(); 445 filteredClasspath = command.getClasspath(); 446 filteredMainClassName = command.getClassname(); 447 filteredRedirect = command.getRedirectoutput(); 448 filteredOutputFile = command.getOutput(); 449 filteredAppendOutput = command.getAppendoutput(); 450 filteredDebug = command.getDebug(); 451 filteredDisplayMinimizedWindow = command.getDisplayminimizedwindow(); 452 filteredDisposeMinimizedWindow = command.getDisposeminimizedwindow(); 453 filteredFailOnError = command.getFailonerror(); 454 filteredMinimizedWindowTitle = command.getMinimizedwindowtitle(); 455 filteredMinimizedWindowIcon = command.getMinimizedwindowicon(); 456 filteredPrint = command.getPrint(); 457 filteredRequireTools = command.getRequiretools(); 458 filteredUseSystemIn = command.getUsesystemin(); 459 filteredWaitForChild = command.getWaitforchild(); 460 // Check changes 461 if (filteredMainClassName == null) 462 throw new BuildException(Launcher.getLocalizedString("classname.null", this.getClass().getName())); 463 if (jvmArgs == null) 464 jvmArgs = new ArrayList(); 465 if (sysProps == null) 466 sysProps = new HashMap(); 467 if (appArgs == null) 468 appArgs = new ArrayList(); 469 } catch (BuildException be) { 470 throw new BuildException(filterClassName + " " + Launcher.getLocalizedString("filter.exception", this.getClass().getName()), be); 471 } catch (ClassCastException cce) { 472 throw new BuildException(filterClassName + " " + Launcher.getLocalizedString("filter.not.filter", this.getClass().getName())); 473 } catch (Exception e) { 474 throw new BuildException(e); 475 } 476 } 477 } 478 479 // Force child JVM into foreground if running using JDB 480 if (filteredDebug) { 481 filteredWaitForChild = true; 482 filteredUseSystemIn = true; 483 } 484 485 // Prepend standard paths to classpath 486 StringBuffer fullClasspath = new StringBuffer(Launcher.getBootstrapFile().getPath()); 487 if (filteredRequireTools) { 488 fullClasspath.append(File.pathSeparator); 489 fullClasspath.append(Launcher.getToolsClasspath()); 490 } 491 if (filteredClasspath != null) { 492 fullClasspath.append(File.pathSeparator); 493 fullClasspath.append(filteredClasspath); 494 } 495 496 // Set ChildMain.WAIT_FOR_CHILD_PROP_NAME property for child JVM 497 sysProps.remove(ChildMain.WAIT_FOR_CHILD_PROP_NAME); 498 if (filteredWaitForChild) 499 sysProps.put(ChildMain.WAIT_FOR_CHILD_PROP_NAME, ""); 500 501 // Set minimized window properties for child JVM 502 sysProps.remove(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME); 503 sysProps.remove(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME); 504 sysProps.remove(ChildMain.MINIMIZED_WINDOW_ICON_PROP_NAME); 505 sysProps.remove(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME); 506 if (!filteredWaitForChild && filteredDisplayMinimizedWindow) { 507 sysProps.put(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME, ""); 508 if (filteredMinimizedWindowTitle != null) 509 sysProps.put(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME, filteredMinimizedWindowTitle); 510 else 511 sysProps.put(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME, getOwningTarget().getName()); 512 if (filteredMinimizedWindowIcon != null) 513 sysProps.put(ChildMain.MINIMIZED_WINDOW_ICON_PROP_NAME, filteredMinimizedWindowIcon.getCanonicalPath()); 514 // Set ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME property 515 if (filteredDisposeMinimizedWindow) 516 sysProps.put(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME, ""); 517 } 518 519 // Set ChildMain.OUTPUT_FILE_PROP_NAME property for child JVM 520 sysProps.remove(ChildMain.OUTPUT_FILE_PROP_NAME); 521 if (!filteredWaitForChild && filteredRedirect) { 522 if (filteredOutputFile != null) { 523 String outputFilePath = filteredOutputFile.getCanonicalPath(); 524 // Verify that we can write to the output file 525 try { 526 File parentFile = new File(filteredOutputFile.getParent()); 527 // To take care of non-existent log directories 528 if ( !parentFile.exists() ) { 529 //Trying to create non-existent parent directories 530 parentFile.mkdirs(); 531 //If this fails createNewFile also fails 532 //We can give more exact error message, if we choose 533 } 534 filteredOutputFile.createNewFile(); 535 } catch (IOException ioe) { 536 throw new BuildException(outputFilePath + " " + Launcher.getLocalizedString("output.file.not.creatable", this.getClass().getName())); 537 } 538 if (!filteredOutputFile.canWrite()) 539 throw new BuildException(outputFilePath + " " + Launcher.getLocalizedString("output.file.not.writable", this.getClass().getName())); 540 sysProps.put(ChildMain.OUTPUT_FILE_PROP_NAME, outputFilePath); 541 if (filteredAppendOutput) 542 sysProps.put(ChildMain.APPEND_OUTPUT_PROP_NAME, ""); 543 Launcher.getLog().println(Launcher.getLocalizedString("redirect.notice", this.getClass().getName()) + " " + outputFilePath); 544 } else { 545 throw new BuildException(Launcher.getLocalizedString("output.file.null", this.getClass().getName())); 546 } 547 } 548 549 // Create the heartbeatFile. This file is needed by the 550 // ParentListener class on Windows since the entire child JVM 551 // process will block on Windows machines using some versions of 552 // Unix shells such as MKS, etc. 553 File heartbeatFile = null; 554 FileOutputStream heartbeatOutputStream = null; 555 if (filteredWaitForChild) { 556 File tmpDir = null; 557 String tmpDirName = (String)sysProps.get("java.io.tmpdir"); 558 if (tmpDirName != null) 559 tmpDir = new File(tmpDirName); 560 heartbeatFile = File.createTempFile(ChildMain.HEARTBEAT_FILE_PROP_NAME + ".", "", tmpDir); 561 // Open the heartbeat file for writing so that it the child JVM 562 // will not be able to delete it while this process is running 563 heartbeatOutputStream = new FileOutputStream(heartbeatFile); 564 sysProps.put(ChildMain.HEARTBEAT_FILE_PROP_NAME, heartbeatFile.getCanonicalPath()); 565 } 566 567 // Assemble child command 568 String[] cmd = new String[5 + jvmArgs.size() + sysProps.size() + appArgs.size()]; 569 int nextCmdArg = 0; 570 if (filteredDebug) 571 cmd[nextCmdArg++] = Launcher.getJDBCommand(); 572 else 573 cmd[nextCmdArg++] = Launcher.getJavaCommand(); 574 // Add jvmArgs to command 575 for (int i = 0; i < jvmArgs.size(); i++) 576 cmd[nextCmdArg++] = (String)jvmArgs.get(i); 577 // Add properties to command 578 Iterator sysPropsKeys = sysProps.keySet().iterator(); 579 while (sysPropsKeys.hasNext()) { 580 String key = (String)sysPropsKeys.next(); 581 if (key == null) 582 continue; 583 String value = (String)sysProps.get(key); 584 if (value == null) 585 value = ""; 586 cmd[nextCmdArg++] = "-D" + key + "=" + value; 587 } 588 // Add classpath to command. Note that it is after the jvmArgs 589 // and system properties to prevent the user from sneaking in an 590 // alterate classpath through the jvmArgs. 591 cmd[nextCmdArg++] = "-classpath"; 592 cmd[nextCmdArg++] = fullClasspath.toString(); 593 // Add main class to command 594 int mainClassArg = nextCmdArg; 595 cmd[nextCmdArg++] = ChildMain.class.getName(); 596 cmd[nextCmdArg++] = filteredMainClassName; 597 // Add args to command 598 for (int i = 0; i < appArgs.size(); i++) 599 { 600 cmd[nextCmdArg++] = (String)appArgs.get(i); 601 } 602 // Print command 603 if (filteredPrint) { 604 // Quote the command arguments 605 String osname = System.getProperty("os.name").toLowerCase(); 606 StringBuffer buf = new StringBuffer(cmd.length * 100); 607 String quote = null; 608 String replaceQuote = null; 609 if (osname.indexOf("windows") >= 0) { 610 // Use double-quotes to quote on Windows 611 quote = "\""; 612 replaceQuote = quote + quote + quote; 613 } else { 614 // Use single-quotes to quote on Unix 615 quote = "'"; 616 replaceQuote = quote + "\\" + quote + quote; 617 } 618 for (int i = 0; i < cmd.length; i++) { 619 // Pull ChildMain out of command as we want to print the 620 // real JVM command that can be executed by the user 621 if (i == mainClassArg) 622 continue; 623 if (i > 0) 624 buf.append(" "); 625 buf.append(quote); 626 StringTokenizer tokenizer = new StringTokenizer(cmd[i], quote, true); 627 while (tokenizer.hasMoreTokens()) { 628 String token = tokenizer.nextToken(); 629 if (quote.equals(token)) 630 buf.append(replaceQuote); 631 else 632 buf.append(token); 633 } 634 buf.append(quote); 635 } 636 // Print the quoted command 637 System.err.println(Launcher.getLocalizedString("executing.child.command", this.getClass().getName()) + ":"); 638 System.err.println(buf.toString()); 639 } 640 641 // Create a child JVM 642 if (Launcher.isStopped()) 643 throw new BuildException(); 644 Process proc = null; 645 synchronized (LaunchTask.childProcesses) { 646 proc = Runtime.getRuntime().exec(cmd); 647 // Add the synchronous child process 648 if (filteredWaitForChild) { 649 childProc = proc; 650 LaunchTask.childProcesses.add(proc); 651 } 652 } 653 if (filteredWaitForChild) { 654 StreamConnector stdout = 655 new StreamConnector(proc.getInputStream(), System.out); 656 StreamConnector stderr = 657 new StreamConnector(proc.getErrorStream(), System.err); 658 stdout.start(); 659 stderr.start(); 660 if (filteredUseSystemIn) { 661 StreamConnector stdin = 662 new StreamConnector(System.in, proc.getOutputStream()); 663 stdin.start(); 664 } 665 proc.waitFor(); 666 // Let threads flush any unflushed output 667 stdout.join(); 668 stderr.join(); 669 if (heartbeatOutputStream != null) 670 heartbeatOutputStream.close(); 671 if (heartbeatFile != null) 672 heartbeatFile.delete(); 673 int exitValue = proc.exitValue(); 674 if (filteredFailOnError && exitValue != 0) 675 throw new BuildException(Launcher.getLocalizedString("child.failed", this.getClass().getName()) + " " + exitValue); 676 } 677 // Need to check if the launching process has stopped because 678 // processes don't throw exceptions when they are terminated 679 if (Launcher.isStopped()) 680 throw new BuildException(); 681 682 } catch (BuildException be) { 683 throw be; 684 } catch (Exception e) { 685 if (Launcher.isStopped()) 686 throw new BuildException(Launcher.getLocalizedString("launch.task.stopped", this.getClass().getName())); 687 else 688 throw new BuildException(e); 689 } 690 691 } 692 693 /** 694 * Set the useArgs flag. Setting this flag to true will cause this 695 * task to append all of the command line arguments used to start the 696 * {@link Launcher#start(String[])} method to the arguments 697 * passed to the child JVM. 698 * 699 * @param useArgs the useArgs flag 700 */ 701 public void setUseargs(boolean useArgs) { 702 703 this.useArgs = useArgs; 704 705 } 706 707 /** 708 * Set the useSystemIn flag. Setting this flag to false will cause this 709 * task to not read System.in. This will cause the child JVM to never 710 * receive any bytes when it reads System.in. Setting this flag to false 711 * is useful in some Unix environments where processes cannot be put in 712 * the background when they read System.in. 713 * 714 * @param useSystemIn the useSystemIn flag 715 */ 716 public void setUsesystemin(boolean useSystemIn) { 717 718 this.useSystemIn = useSystemIn; 719 720 } 721 722 /** 723 * Set the waitForChild flag. Setting this flag to true will cause this 724 * task to wait for the child JVM to finish executing before the task 725 * completes. Setting this flag to false will cause this task to complete 726 * immediately after it starts the execution of the child JVM. Setting it 727 * false emulates the "&" background operator in most Unix shells and is 728 * most of set to false when launching server or GUI applications. 729 * 730 * @param waitForChild the waitForChild flag 731 */ 732 public void setWaitforchild(boolean waitForChild) { 733 734 this.waitForChild = waitForChild; 735 736 } 737 738 /** 739 * Set the class name. 740 * 741 * @param mainClassName the class to execute <code>main(String[])</code> 742 */ 743 public void setClassname(String mainClassName) { 744 745 this.mainClassName = mainClassName; 746 747 } 748 749 /** 750 * Set the classpath. 751 * 752 * @param classpath the classpath 753 */ 754 public void setClasspath(Path classpath) { 755 756 createClasspath().append(classpath); 757 758 } 759 760 /** 761 * Adds a reference to a classpath defined elsewhere. 762 * 763 * @param ref reference to the classpath 764 */ 765 public void setClasspathref(Reference ref) { 766 767 createClasspath().setRefid(ref); 768 769 } 770 771 /** 772 * Set the debug flag. Setting this flag to true will cause this 773 * task to run the child JVM using the JDB debugger. 774 * 775 * @param debug the debug flag 776 */ 777 public void setDebug(boolean debug) { 778 779 this.debug = debug; 780 781 } 782 783 /** 784 * Set the displayMinimizedWindow flag. Note that this flag has no effect 785 * on non-Windows platforms. On Windows platform, setting this flag to true 786 * will cause a minimized window to be displayed in the Windows task bar 787 * while the child process is executing. This flag is usually set to true 788 * for server applications that also have their "waitForChild" attribute 789 * set to false via the {@link #setWaitforchild(boolean)} method. 790 * 791 * @param displayMinimizedWindow true if a minimized window should be 792 * displayed in the Windows task bar while the child process is executing 793 */ 794 public void setDisplayminimizedwindow(boolean displayMinimizedWindow) { 795 796 this.displayMinimizedWindow = displayMinimizedWindow; 797 798 } 799 800 /** 801 * Set the disposeMinimizedWindow flag. Note that this flag has no effect 802 * on non-Windows platforms. On Windows platform, setting this flag to true 803 * will cause any minimized window that is display by setting the 804 * "displayMinimizedWindow" attribute to true via the 805 * {@link #setDisplayminimizedwindow(boolean)} to be automatically 806 * disposed of when the child JVM's <code>main(String[])</code> returns. 807 * This flag is normally used for applications that don't explicitly call 808 * {@link System#exit(int)}. If an application does not explicitly call 809 * {@link System#exit(int)}, an minimized windows need to be disposed of 810 * for the child JVM to exit. 811 * 812 * @param disposeMinimizedWindow true if a minimized window in the Windows 813 * taskbar should be automatically disposed of after the child JVM's 814 * <code>main(String[])</code> returns 815 */ 816 public void setDisposeminimizedwindow(boolean disposeMinimizedWindow) { 817 818 this.disposeMinimizedWindow = disposeMinimizedWindow; 819 820 } 821 822 /** 823 * Set the failOnError flag. 824 * 825 * @param failOnError true if the launch process should stop if the child 826 * JVM returns an exit value other than 0 827 */ 828 public void setFailonerror(boolean failOnError) { 829 830 this.failOnError = failOnError; 831 832 } 833 /** 834 * Set the filter class name. 835 * 836 * @param filterClassName the class that implements the 837 * {@link LaunchFilter} interface 838 */ 839 public void setFilterclassname(String filterClassName) { 840 841 this.filterClassName = filterClassName; 842 843 } 844 845 /** 846 * Set the filter class' classpath. 847 * 848 * @param filterClasspath the classpath for the filter class 849 */ 850 public void setFilterclasspath(Path filterClasspath) { 851 852 createFilterclasspath().append(filterClasspath); 853 854 } 855 856 /** 857 * Set the title for the minimized window that will be displayed in the 858 * Windows taskbar. Note that this property has no effect on non-Windows 859 * platforms. 860 * 861 * @param minimizedWindowTitle the title to set for any minimized window 862 * that is displayed in the Windows taskbar 863 */ 864 public void setMinimizedwindowtitle(String minimizedWindowTitle) { 865 866 this.minimizedWindowTitle = minimizedWindowTitle; 867 868 } 869 870 /** 871 * Set the icon file for the minimized window that will be displayed in the 872 * Windows taskbar. Note that this property has no effect on non-Windows 873 * platforms. 874 * 875 * @param minimizedWindowIcon the icon file to use for any minimized window 876 * that is displayed in the Windows taskbar 877 */ 878 public void setMinimizedwindowicon(File minimizedWindowIcon) { 879 880 this.minimizedWindowIcon = minimizedWindowIcon; 881 882 } 883 884 /** 885 * Set the file that the child JVM's System.out and System.err will be 886 * redirected to. Output will only be redirected if the redirect flag 887 * is set to true via the {@link #setRedirectoutput(boolean)} method. 888 * 889 * @param outputFile a File to redirect System.out and System.err to 890 */ 891 public void setOutput(File outputFile) { 892 893 this.outputFile = outputFile; 894 895 } 896 897 /** 898 * Set the print flag. Setting this flag to true will cause the full child 899 * JVM command to be printed to {@link System#out}. 900 * 901 * @param print the print flag 902 */ 903 public void setPrint(boolean print) { 904 905 this.print = print; 906 907 } 908 909 /** 910 * Set the appendOutput flag. Setting this flag to true will cause the child 911 * JVM to append System.out and System.err to the file specified by the 912 * {@link #setOutput(File)} method. Setting this flag to false will cause 913 * the child to overwrite the file. 914 * 915 * @param appendOutput true if output should be appended to the output file 916 */ 917 public void setAppendoutput(boolean appendOutput) { 918 919 this.appendOutput = appendOutput; 920 921 } 922 923 /** 924 * Set the redirect flag. Setting this flag to true will cause the child 925 * JVM's System.out and System.err to be redirected to file set using the 926 * {@link #setOutput(File)} method. Setting this flag to false will 927 * cause no redirection. 928 * 929 * @param redirect true if System.out and System.err should be redirected 930 */ 931 public void setRedirectoutput(boolean redirect) { 932 933 this.redirect = redirect; 934 935 } 936 937 /** 938 * Set the requireTools flag. Setting this flag to true will cause the 939 * JVM's tools.jar to be added to the child JVM's classpath. This 940 * sets an explicit requirement that the user use a JDK instead of a 941 * JRE. Setting this flag to false explicitly allows the user to use 942 * a JRE. 943 * 944 * @param requireTools true if a JDK is required and false if only a JRE 945 * is required 946 */ 947 public void setRequiretools(boolean requireTools) { 948 949 this.requireTools = requireTools; 950 951 } 952 953 /** 954 * Determine if the "if" condition flag for a nested element meets all 955 * criteria for use. 956 * 957 * @param ifCondition the "if" condition flag for a nested element 958 * @return true if the nested element should be process and false if it 959 * should be ignored 960 */ 961 private boolean testIfCondition(String ifCondition) { 962 963 if (ifCondition == null || "".equals(ifCondition)) 964 return true; 965 return project.getProperty(ifCondition) != null; 966 967 } 968 969 /** 970 * Determine if the "unless" condition flag for a nested element meets all 971 * criteria for use. 972 * 973 * @param unlessCondition the "unless" condition flag for a nested element 974 * @return true if the nested element should be process and false if it 975 * should be ignored 976 */ 977 private boolean testUnlessCondition(String unlessCondition) { 978 979 if (unlessCondition == null || "".equals(unlessCondition)) 980 return true; 981 return project.getProperty(unlessCondition) == null; 982 983 } 984 985}