View Javadoc

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.commons.launcher;
19  
20  import java.io.File;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.net.URL;
24  import java.net.URLClassLoader;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.StringTokenizer;
29  import org.apache.commons.launcher.types.ArgumentSet;
30  import org.apache.commons.launcher.types.ConditionalArgument;
31  import org.apache.commons.launcher.types.ConditionalVariable;
32  import org.apache.commons.launcher.types.JVMArgumentSet;
33  import org.apache.commons.launcher.types.SysPropertySet;
34  import org.apache.tools.ant.BuildException;
35  import org.apache.tools.ant.Task;
36  import org.apache.tools.ant.types.Path;
37  import org.apache.tools.ant.types.Reference;
38  
39  /**
40   * A class that eliminates the need for a batch or shell script to launch a Java
41   * class. Some situations where elimination of a batch or shell script may be 
42   * desirable are:
43   * <ul>
44   * <li>You want to avoid having to determining where certain application paths
45   *  are e.g. your application's home directory, etc. Determining this
46   *  dynamically in a Windows batch scripts is very tricky on some versions of
47   *  Windows or when softlinks are used on Unix platforms.
48   * <li>You want to avoid having to handle native file and path separators or
49   *  native path quoting issues.
50   * <li>You need to enforce certain system properties e.g.
51   *  <code>java.endorsed.dirs</code> when running with JDK 1.4.
52   * <li>You want to allow users to pass in custom JVM arguments or system
53   *  properties without having to parse and reorder arguments in your script.
54   *  This can be tricky and/or messy in batch and shell scripts.
55   * <li>You want to bootstrap system properties from a configuration file instead
56   *  hard-coding them in your batch and shell scripts.
57   * <li>You want to provide localized error messages which is very tricky to do
58   *  in batch and shell scripts.
59   * </ul>
60   *
61   * @author Patrick Luby
62   */
63  public class LaunchTask extends Task {
64  
65      //----------------------------------------------------------- Static Fields
66  
67      /**
68       * The argument property name.
69       */
70      public final static String ARG_PROP_NAME = "launch.arg.";
71  
72      /**
73       * The name of this task.
74       */
75      public final static String TASK_NAME = "launch";
76  
77      /**
78       * Cached synchronous child processes for all instances of this class.
79       */
80      private static ArrayList childProcesses = new ArrayList();
81  
82      //------------------------------------------------------------------ Fields
83  
84      /**
85       * Cached appendOutput flag.
86       */
87      private boolean appendOutput = false;
88  
89      /**
90       * Cached synchronously executing child process.
91       */
92      private Process childProc = null;
93  
94      /**
95       * Cached classpath.
96       */
97      private Path classpath = null;
98  
99      /**
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 }