Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
LaunchTask |
|
| 4.064516129032258;4.065 |
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 | 0 | 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 | 0 | private static ArrayList childProcesses = new ArrayList(); |
81 | ||
82 | //------------------------------------------------------------------ Fields | |
83 | ||
84 | /** | |
85 | * Cached appendOutput flag. | |
86 | */ | |
87 | 0 | private boolean appendOutput = false; |
88 | ||
89 | /** | |
90 | * Cached synchronously executing child process. | |
91 | */ | |
92 | 0 | private Process childProc = null; |
93 | ||
94 | /** | |
95 | * Cached classpath. | |
96 | */ | |
97 | 0 | private Path classpath = null; |
98 | ||
99 | /** | |
100 | * Cached debug flag. | |
101 | */ | |
102 | 0 | private boolean debug = false; |
103 | ||
104 | /** | |
105 | * Cached displayMinimizedWindow flag. | |
106 | */ | |
107 | 0 | private boolean displayMinimizedWindow = false; |
108 | ||
109 | /** | |
110 | * Cached disposeMinimizedWindow flag. | |
111 | */ | |
112 | 0 | private boolean disposeMinimizedWindow = true; |
113 | ||
114 | /** | |
115 | * Cached failOnError flag. | |
116 | */ | |
117 | 0 | private boolean failOnError = false; |
118 | ||
119 | /** | |
120 | * Cached filter instance. | |
121 | */ | |
122 | 0 | private LaunchFilter filter = null; |
123 | ||
124 | /** | |
125 | * Cached filterClassName. | |
126 | */ | |
127 | 0 | private String filterClassName = null; |
128 | ||
129 | /** | |
130 | * Cached filterClasspath. | |
131 | */ | |
132 | 0 | private Path filterClasspath = null; |
133 | ||
134 | /** | |
135 | * Cached main class name. | |
136 | */ | |
137 | 0 | private String mainClassName = null; |
138 | ||
139 | /** | |
140 | * Cached minimizedWindowIcon. | |
141 | */ | |
142 | 0 | private File minimizedWindowIcon = null; |
143 | ||
144 | /** | |
145 | * Cached minimizedWindowTitle. | |
146 | */ | |
147 | 0 | private String minimizedWindowTitle = null; |
148 | ||
149 | /** | |
150 | * Cached output file. | |
151 | */ | |
152 | 0 | private File outputFile = null; |
153 | ||
154 | /** | |
155 | * Cached print flag. | |
156 | */ | |
157 | 0 | private boolean print = false; |
158 | ||
159 | /** | |
160 | * Cached redirect flag. | |
161 | */ | |
162 | 0 | private boolean redirect = false; |
163 | ||
164 | /** | |
165 | * Cached requireTools flag. | |
166 | */ | |
167 | 0 | private boolean requireTools = false; |
168 | ||
169 | /** | |
170 | * Cached arg elements | |
171 | */ | |
172 | 0 | private ArgumentSet taskArgumentSet = new ArgumentSet(); |
173 | ||
174 | /** | |
175 | * Cached jvmarg elements | |
176 | */ | |
177 | 0 | private JVMArgumentSet taskJVMArgumentSet = new JVMArgumentSet(); |
178 | ||
179 | /** | |
180 | * Cached sysproperty elements | |
181 | */ | |
182 | 0 | private SysPropertySet taskSysPropertySet = new SysPropertySet(); |
183 | ||
184 | /** | |
185 | * Cached useArgs flag. | |
186 | */ | |
187 | 0 | private boolean useArgs = true; |
188 | ||
189 | /** | |
190 | * Cached useSystemIn flag. | |
191 | */ | |
192 | 0 | private boolean useSystemIn = true; |
193 | ||
194 | /** | |
195 | * Cached waitForChild flag. | |
196 | */ | |
197 | 0 | 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 | 0 | 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 | 0 | taskArgumentSet.addArg(arg); |
225 | ||
226 | 0 | } |
227 | ||
228 | /** | |
229 | * Add a nested argset element. | |
230 | * | |
231 | * @param set the argset element | |
232 | */ | |
233 | public void addArgset(ArgumentSet set) { | |
234 | ||
235 | 0 | taskArgumentSet.addArgset(set); |
236 | ||
237 | 0 | } |
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 | 0 | taskJVMArgumentSet.addJvmarg(jvmArg); |
250 | ||
251 | 0 | } |
252 | ||
253 | /** | |
254 | * Add a nested jvmargset element. | |
255 | * | |
256 | * @param set the jvmargset element | |
257 | */ | |
258 | public void addJvmargset(JVMArgumentSet set) { | |
259 | ||
260 | 0 | taskJVMArgumentSet.addJvmargset(set); |
261 | ||
262 | 0 | } |
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 | 0 | taskSysPropertySet.addSysproperty(var); |
275 | ||
276 | 0 | } |
277 | ||
278 | /** | |
279 | * Add a nested syspropertyset element. | |
280 | * | |
281 | * @param set the syspropertyset element | |
282 | */ | |
283 | public void addSyspropertyset(SysPropertySet set) { | |
284 | ||
285 | 0 | taskSysPropertySet.addSyspropertyset(set); |
286 | ||
287 | 0 | } |
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 | 0 | if (classpath == null) |
297 | 0 | classpath = new Path(project); |
298 | 0 | 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 | 0 | if (filterClasspath == null) |
311 | 0 | filterClasspath = new Path(project); |
312 | 0 | 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 | 0 | if (!Launcher.isStarted()) |
330 | 0 | 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 | 0 | if (Launcher.isStopped()) |
334 | 0 | throw new BuildException(); |
335 | ||
336 | 0 | if (mainClassName == null) |
337 | 0 | throw new BuildException(Launcher.getLocalizedString("classname.null", this.getClass().getName())); |
338 | ||
339 | // Copy all of the nested jvmarg elements into the jvmArgs object | |
340 | 0 | ArrayList taskJVMArgs = taskJVMArgumentSet.getList(); |
341 | 0 | ArrayList jvmArgs = new ArrayList(taskJVMArgs.size()); |
342 | 0 | for (int i = 0; i < taskJVMArgs.size(); i++) { |
343 | 0 | ConditionalArgument value = (ConditionalArgument)taskJVMArgs.get(i); |
344 | // Test "if" and "unless" conditions | |
345 | 0 | if (testIfCondition(value.getIf()) && testUnlessCondition(value.getUnless())) { |
346 | 0 | String[] list = value.getParts(); |
347 | 0 | for (int j = 0; j < list.length; j++) |
348 | 0 | jvmArgs.add(list[j]); |
349 | } | |
350 | } | |
351 | ||
352 | // Copy all of the nested sysproperty elements into the sysProps | |
353 | // object | |
354 | 0 | ArrayList taskSysProps = taskSysPropertySet.getList(); |
355 | 0 | HashMap sysProps = new HashMap(taskSysProps.size()); |
356 | 0 | for (int i = 0; i < taskSysProps.size(); i++) { |
357 | 0 | ConditionalVariable variable = (ConditionalVariable)taskSysProps.get(i); |
358 | // Test "if" and "unless" conditions | |
359 | 0 | if (testIfCondition(variable.getIf()) && testUnlessCondition(variable.getUnless())) |
360 | 0 | sysProps.put(variable.getKey(), variable.getValue()); |
361 | } | |
362 | ||
363 | // Copy all of the nested arg elements into the appArgs object | |
364 | 0 | ArrayList taskArgs = taskArgumentSet.getList(); |
365 | 0 | ArrayList appArgs = new ArrayList(taskArgs.size()); |
366 | 0 | for (int i = 0; i < taskArgs.size(); i++) { |
367 | 0 | ConditionalArgument value = (ConditionalArgument)taskArgs.get(i); |
368 | // Test "if" and "unless" conditions | |
369 | 0 | if (testIfCondition(value.getIf()) && testUnlessCondition(value.getUnless())) { |
370 | 0 | String[] list = value.getParts(); |
371 | 0 | for (int j = 0; j < list.length; j++) |
372 | 0 | appArgs.add(list[j]); |
373 | } | |
374 | } | |
375 | ||
376 | // Add the Launcher's command line arguments to the appArgs object | |
377 | 0 | if (useArgs) { |
378 | 0 | int currentArg = 0; |
379 | 0 | String arg = null; |
380 | 0 | while ((arg = project.getUserProperty(LaunchTask.ARG_PROP_NAME + Integer.toString(currentArg++))) != null) |
381 | 0 | 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 | 0 | String filteredClasspath = null; |
387 | 0 | if (classpath != null) |
388 | 0 | filteredClasspath = classpath.toString(); |
389 | 0 | String filteredMainClassName = mainClassName; |
390 | 0 | boolean filteredRedirect = redirect; |
391 | 0 | File filteredOutputFile = outputFile; |
392 | 0 | boolean filteredAppendOutput = appendOutput; |
393 | 0 | boolean filteredDebug = debug; |
394 | 0 | boolean filteredDisplayMinimizedWindow = displayMinimizedWindow; |
395 | 0 | boolean filteredDisposeMinimizedWindow = disposeMinimizedWindow; |
396 | 0 | boolean filteredFailOnError = failOnError; |
397 | 0 | String filteredMinimizedWindowTitle = minimizedWindowTitle; |
398 | 0 | File filteredMinimizedWindowIcon = minimizedWindowIcon; |
399 | 0 | boolean filteredPrint = print; |
400 | 0 | boolean filteredRequireTools = requireTools; |
401 | 0 | boolean filteredUseSystemIn = useSystemIn; |
402 | 0 | 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 | 0 | if (filterClassName != null) { |
408 | 0 | if (filter == null) { |
409 | try { | |
410 | 0 | ClassLoader loader = this.getClass().getClassLoader(); |
411 | 0 | if (filterClasspath != null) { |
412 | // Construct a class loader to load the class | |
413 | 0 | String[] fileList = filterClasspath.list(); |
414 | 0 | URL[] urls = new URL[fileList.length]; |
415 | 0 | for (int i = 0; i < fileList.length; i++) |
416 | 0 | urls[i] = new File(fileList[i]).toURL(); |
417 | 0 | loader = new URLClassLoader(urls, loader); |
418 | } | |
419 | 0 | Class filterClass = loader.loadClass(filterClassName); |
420 | 0 | filter = (LaunchFilter)filterClass.newInstance(); |
421 | // Execute filter and save any changes | |
422 | 0 | LaunchCommand command = new LaunchCommand(); |
423 | 0 | command.setJvmargs(jvmArgs); |
424 | 0 | command.setSysproperties(sysProps); |
425 | 0 | command.setArgs(appArgs); |
426 | 0 | command.setClasspath(filteredClasspath); |
427 | 0 | command.setClassname(filteredMainClassName); |
428 | 0 | command.setRedirectoutput(filteredRedirect); |
429 | 0 | command.setOutput(filteredOutputFile); |
430 | 0 | command.setAppendoutput(filteredAppendOutput); |
431 | 0 | command.setDebug(filteredDebug); |
432 | 0 | command.setDisplayminimizedwindow(filteredDisplayMinimizedWindow); |
433 | 0 | command.setDisposeminimizedwindow(filteredDisposeMinimizedWindow); |
434 | 0 | command.setFailonerror(filteredFailOnError); |
435 | 0 | command.setMinimizedwindowtitle(filteredMinimizedWindowTitle); |
436 | 0 | command.setMinimizedwindowicon(filteredMinimizedWindowIcon); |
437 | 0 | command.setPrint(filteredPrint); |
438 | 0 | command.setRequiretools(filteredRequireTools); |
439 | 0 | command.setUsesystemin(filteredUseSystemIn); |
440 | 0 | command.setWaitforchild(filteredWaitForChild); |
441 | 0 | filter.filter(command); |
442 | 0 | jvmArgs = command.getJvmargs(); |
443 | 0 | sysProps = command.getSysproperties(); |
444 | 0 | appArgs = command.getArgs(); |
445 | 0 | filteredClasspath = command.getClasspath(); |
446 | 0 | filteredMainClassName = command.getClassname(); |
447 | 0 | filteredRedirect = command.getRedirectoutput(); |
448 | 0 | filteredOutputFile = command.getOutput(); |
449 | 0 | filteredAppendOutput = command.getAppendoutput(); |
450 | 0 | filteredDebug = command.getDebug(); |
451 | 0 | filteredDisplayMinimizedWindow = command.getDisplayminimizedwindow(); |
452 | 0 | filteredDisposeMinimizedWindow = command.getDisposeminimizedwindow(); |
453 | 0 | filteredFailOnError = command.getFailonerror(); |
454 | 0 | filteredMinimizedWindowTitle = command.getMinimizedwindowtitle(); |
455 | 0 | filteredMinimizedWindowIcon = command.getMinimizedwindowicon(); |
456 | 0 | filteredPrint = command.getPrint(); |
457 | 0 | filteredRequireTools = command.getRequiretools(); |
458 | 0 | filteredUseSystemIn = command.getUsesystemin(); |
459 | 0 | filteredWaitForChild = command.getWaitforchild(); |
460 | // Check changes | |
461 | 0 | if (filteredMainClassName == null) |
462 | 0 | throw new BuildException(Launcher.getLocalizedString("classname.null", this.getClass().getName())); |
463 | 0 | if (jvmArgs == null) |
464 | 0 | jvmArgs = new ArrayList(); |
465 | 0 | if (sysProps == null) |
466 | 0 | sysProps = new HashMap(); |
467 | 0 | if (appArgs == null) |
468 | 0 | appArgs = new ArrayList(); |
469 | 0 | } catch (BuildException be) { |
470 | 0 | throw new BuildException(filterClassName + " " + Launcher.getLocalizedString("filter.exception", this.getClass().getName()), be); |
471 | 0 | } catch (ClassCastException cce) { |
472 | 0 | throw new BuildException(filterClassName + " " + Launcher.getLocalizedString("filter.not.filter", this.getClass().getName())); |
473 | 0 | } catch (Exception e) { |
474 | 0 | throw new BuildException(e); |
475 | 0 | } |
476 | } | |
477 | } | |
478 | ||
479 | // Force child JVM into foreground if running using JDB | |
480 | 0 | if (filteredDebug) { |
481 | 0 | filteredWaitForChild = true; |
482 | 0 | filteredUseSystemIn = true; |
483 | } | |
484 | ||
485 | // Prepend standard paths to classpath | |
486 | 0 | StringBuffer fullClasspath = new StringBuffer(Launcher.getBootstrapFile().getPath()); |
487 | 0 | if (filteredRequireTools) { |
488 | 0 | fullClasspath.append(File.pathSeparator); |
489 | 0 | fullClasspath.append(Launcher.getToolsClasspath()); |
490 | } | |
491 | 0 | if (filteredClasspath != null) { |
492 | 0 | fullClasspath.append(File.pathSeparator); |
493 | 0 | fullClasspath.append(filteredClasspath); |
494 | } | |
495 | ||
496 | // Set ChildMain.WAIT_FOR_CHILD_PROP_NAME property for child JVM | |
497 | 0 | sysProps.remove(ChildMain.WAIT_FOR_CHILD_PROP_NAME); |
498 | 0 | if (filteredWaitForChild) |
499 | 0 | sysProps.put(ChildMain.WAIT_FOR_CHILD_PROP_NAME, ""); |
500 | ||
501 | // Set minimized window properties for child JVM | |
502 | 0 | sysProps.remove(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME); |
503 | 0 | sysProps.remove(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME); |
504 | 0 | sysProps.remove(ChildMain.MINIMIZED_WINDOW_ICON_PROP_NAME); |
505 | 0 | sysProps.remove(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME); |
506 | 0 | if (!filteredWaitForChild && filteredDisplayMinimizedWindow) { |
507 | 0 | sysProps.put(ChildMain.DISPLAY_MINIMIZED_WINDOW_PROP_NAME, ""); |
508 | 0 | if (filteredMinimizedWindowTitle != null) |
509 | 0 | sysProps.put(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME, filteredMinimizedWindowTitle); |
510 | else | |
511 | 0 | sysProps.put(ChildMain.MINIMIZED_WINDOW_TITLE_PROP_NAME, getOwningTarget().getName()); |
512 | 0 | if (filteredMinimizedWindowIcon != null) |
513 | 0 | sysProps.put(ChildMain.MINIMIZED_WINDOW_ICON_PROP_NAME, filteredMinimizedWindowIcon.getCanonicalPath()); |
514 | // Set ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME property | |
515 | 0 | if (filteredDisposeMinimizedWindow) |
516 | 0 | sysProps.put(ChildMain.DISPOSE_MINIMIZED_WINDOW_PROP_NAME, ""); |
517 | } | |
518 | ||
519 | // Set ChildMain.OUTPUT_FILE_PROP_NAME property for child JVM | |
520 | 0 | sysProps.remove(ChildMain.OUTPUT_FILE_PROP_NAME); |
521 | 0 | if (!filteredWaitForChild && filteredRedirect) { |
522 | 0 | if (filteredOutputFile != null) { |
523 | 0 | String outputFilePath = filteredOutputFile.getCanonicalPath(); |
524 | // Verify that we can write to the output file | |
525 | try { | |
526 | 0 | File parentFile = new File(filteredOutputFile.getParent()); |
527 | // To take care of non-existent log directories | |
528 | 0 | if ( !parentFile.exists() ) { |
529 | //Trying to create non-existent parent directories | |
530 | 0 | parentFile.mkdirs(); |
531 | //If this fails createNewFile also fails | |
532 | //We can give more exact error message, if we choose | |
533 | } | |
534 | 0 | filteredOutputFile.createNewFile(); |
535 | 0 | } catch (IOException ioe) { |
536 | 0 | throw new BuildException(outputFilePath + " " + Launcher.getLocalizedString("output.file.not.creatable", this.getClass().getName())); |
537 | 0 | } |
538 | 0 | if (!filteredOutputFile.canWrite()) |
539 | 0 | throw new BuildException(outputFilePath + " " + Launcher.getLocalizedString("output.file.not.writable", this.getClass().getName())); |
540 | 0 | sysProps.put(ChildMain.OUTPUT_FILE_PROP_NAME, outputFilePath); |
541 | 0 | if (filteredAppendOutput) |
542 | 0 | sysProps.put(ChildMain.APPEND_OUTPUT_PROP_NAME, ""); |
543 | 0 | Launcher.getLog().println(Launcher.getLocalizedString("redirect.notice", this.getClass().getName()) + " " + outputFilePath); |
544 | 0 | } else { |
545 | 0 | 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 | 0 | File heartbeatFile = null; |
554 | 0 | FileOutputStream heartbeatOutputStream = null; |
555 | 0 | if (filteredWaitForChild) { |
556 | 0 | File tmpDir = null; |
557 | 0 | String tmpDirName = (String)sysProps.get("java.io.tmpdir"); |
558 | 0 | if (tmpDirName != null) |
559 | 0 | tmpDir = new File(tmpDirName); |
560 | 0 | 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 | 0 | heartbeatOutputStream = new FileOutputStream(heartbeatFile); |
564 | 0 | sysProps.put(ChildMain.HEARTBEAT_FILE_PROP_NAME, heartbeatFile.getCanonicalPath()); |
565 | } | |
566 | ||
567 | // Assemble child command | |
568 | 0 | String[] cmd = new String[5 + jvmArgs.size() + sysProps.size() + appArgs.size()]; |
569 | 0 | int nextCmdArg = 0; |
570 | 0 | if (filteredDebug) |
571 | 0 | cmd[nextCmdArg++] = Launcher.getJDBCommand(); |
572 | else | |
573 | 0 | cmd[nextCmdArg++] = Launcher.getJavaCommand(); |
574 | // Add jvmArgs to command | |
575 | 0 | for (int i = 0; i < jvmArgs.size(); i++) |
576 | 0 | cmd[nextCmdArg++] = (String)jvmArgs.get(i); |
577 | // Add properties to command | |
578 | 0 | Iterator sysPropsKeys = sysProps.keySet().iterator(); |
579 | 0 | while (sysPropsKeys.hasNext()) { |
580 | 0 | String key = (String)sysPropsKeys.next(); |
581 | 0 | if (key == null) |
582 | 0 | continue; |
583 | 0 | String value = (String)sysProps.get(key); |
584 | 0 | if (value == null) |
585 | 0 | value = ""; |
586 | 0 | cmd[nextCmdArg++] = "-D" + key + "=" + value; |
587 | 0 | } |
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 | 0 | cmd[nextCmdArg++] = "-classpath"; |
592 | 0 | cmd[nextCmdArg++] = fullClasspath.toString(); |
593 | // Add main class to command | |
594 | 0 | int mainClassArg = nextCmdArg; |
595 | 0 | cmd[nextCmdArg++] = ChildMain.class.getName(); |
596 | 0 | cmd[nextCmdArg++] = filteredMainClassName; |
597 | // Add args to command | |
598 | 0 | for (int i = 0; i < appArgs.size(); i++) |
599 | { | |
600 | 0 | cmd[nextCmdArg++] = (String)appArgs.get(i); |
601 | } | |
602 | // Print command | |
603 | 0 | if (filteredPrint) { |
604 | // Quote the command arguments | |
605 | 0 | String osname = System.getProperty("os.name").toLowerCase(); |
606 | 0 | StringBuffer buf = new StringBuffer(cmd.length * 100); |
607 | 0 | String quote = null; |
608 | 0 | String replaceQuote = null; |
609 | 0 | if (osname.indexOf("windows") >= 0) { |
610 | // Use double-quotes to quote on Windows | |
611 | 0 | quote = "\""; |
612 | 0 | replaceQuote = quote + quote + quote; |
613 | } else { | |
614 | // Use single-quotes to quote on Unix | |
615 | 0 | quote = "'"; |
616 | 0 | replaceQuote = quote + "\\" + quote + quote; |
617 | } | |
618 | 0 | 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 | 0 | if (i == mainClassArg) |
622 | 0 | continue; |
623 | 0 | if (i > 0) |
624 | 0 | buf.append(" "); |
625 | 0 | buf.append(quote); |
626 | 0 | StringTokenizer tokenizer = new StringTokenizer(cmd[i], quote, true); |
627 | 0 | while (tokenizer.hasMoreTokens()) { |
628 | 0 | String token = tokenizer.nextToken(); |
629 | 0 | if (quote.equals(token)) |
630 | 0 | buf.append(replaceQuote); |
631 | else | |
632 | 0 | buf.append(token); |
633 | 0 | } |
634 | 0 | buf.append(quote); |
635 | } | |
636 | // Print the quoted command | |
637 | 0 | System.err.println(Launcher.getLocalizedString("executing.child.command", this.getClass().getName()) + ":"); |
638 | 0 | System.err.println(buf.toString()); |
639 | } | |
640 | ||
641 | // Create a child JVM | |
642 | 0 | if (Launcher.isStopped()) |
643 | 0 | throw new BuildException(); |
644 | 0 | Process proc = null; |
645 | 0 | synchronized (LaunchTask.childProcesses) { |
646 | 0 | proc = Runtime.getRuntime().exec(cmd); |
647 | // Add the synchronous child process | |
648 | 0 | if (filteredWaitForChild) { |
649 | 0 | childProc = proc; |
650 | 0 | LaunchTask.childProcesses.add(proc); |
651 | } | |
652 | 0 | } |
653 | 0 | if (filteredWaitForChild) { |
654 | 0 | StreamConnector stdout = |
655 | new StreamConnector(proc.getInputStream(), System.out); | |
656 | 0 | StreamConnector stderr = |
657 | new StreamConnector(proc.getErrorStream(), System.err); | |
658 | 0 | stdout.start(); |
659 | 0 | stderr.start(); |
660 | 0 | if (filteredUseSystemIn) { |
661 | 0 | StreamConnector stdin = |
662 | new StreamConnector(System.in, proc.getOutputStream()); | |
663 | 0 | stdin.start(); |
664 | } | |
665 | 0 | proc.waitFor(); |
666 | // Let threads flush any unflushed output | |
667 | 0 | stdout.join(); |
668 | 0 | stderr.join(); |
669 | 0 | if (heartbeatOutputStream != null) |
670 | 0 | heartbeatOutputStream.close(); |
671 | 0 | if (heartbeatFile != null) |
672 | 0 | heartbeatFile.delete(); |
673 | 0 | int exitValue = proc.exitValue(); |
674 | 0 | if (filteredFailOnError && exitValue != 0) |
675 | 0 | 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 | 0 | if (Launcher.isStopped()) |
680 | 0 | throw new BuildException(); |
681 | ||
682 | 0 | } catch (BuildException be) { |
683 | 0 | throw be; |
684 | 0 | } catch (Exception e) { |
685 | 0 | if (Launcher.isStopped()) |
686 | 0 | throw new BuildException(Launcher.getLocalizedString("launch.task.stopped", this.getClass().getName())); |
687 | else | |
688 | 0 | throw new BuildException(e); |
689 | 0 | } |
690 | ||
691 | 0 | } |
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 | 0 | this.useArgs = useArgs; |
704 | ||
705 | 0 | } |
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 | 0 | this.useSystemIn = useSystemIn; |
719 | ||
720 | 0 | } |
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 | 0 | this.waitForChild = waitForChild; |
735 | ||
736 | 0 | } |
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 | 0 | this.mainClassName = mainClassName; |
746 | ||
747 | 0 | } |
748 | ||
749 | /** | |
750 | * Set the classpath. | |
751 | * | |
752 | * @param classpath the classpath | |
753 | */ | |
754 | public void setClasspath(Path classpath) { | |
755 | ||
756 | 0 | createClasspath().append(classpath); |
757 | ||
758 | 0 | } |
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 | 0 | createClasspath().setRefid(ref); |
768 | ||
769 | 0 | } |
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 | 0 | this.debug = debug; |
780 | ||
781 | 0 | } |
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 | 0 | this.displayMinimizedWindow = displayMinimizedWindow; |
797 | ||
798 | 0 | } |
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 | 0 | this.disposeMinimizedWindow = disposeMinimizedWindow; |
819 | ||
820 | 0 | } |
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 | 0 | this.failOnError = failOnError; |
831 | ||
832 | 0 | } |
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 | 0 | this.filterClassName = filterClassName; |
842 | ||
843 | 0 | } |
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 | 0 | createFilterclasspath().append(filterClasspath); |
853 | ||
854 | 0 | } |
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 | 0 | this.minimizedWindowTitle = minimizedWindowTitle; |
867 | ||
868 | 0 | } |
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 | 0 | this.minimizedWindowIcon = minimizedWindowIcon; |
881 | ||
882 | 0 | } |
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 | 0 | this.outputFile = outputFile; |
894 | ||
895 | 0 | } |
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 | 0 | this.print = print; |
906 | ||
907 | 0 | } |
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 | 0 | this.appendOutput = appendOutput; |
920 | ||
921 | 0 | } |
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 | 0 | this.redirect = redirect; |
934 | ||
935 | 0 | } |
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 | 0 | this.requireTools = requireTools; |
950 | ||
951 | 0 | } |
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 | 0 | if (ifCondition == null || "".equals(ifCondition)) |
964 | 0 | return true; |
965 | 0 | 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 | 0 | if (unlessCondition == null || "".equals(unlessCondition)) |
980 | 0 | return true; |
981 | 0 | return project.getProperty(unlessCondition) == null; |
982 | ||
983 | } | |
984 | ||
985 | } |