KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > Execute


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
19 package org.apache.tools.ant.taskdefs;
20
21 import java.io.BufferedReader JavaDoc;
22 import java.io.ByteArrayOutputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileWriter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.OutputStream JavaDoc;
27 import java.io.PrintWriter JavaDoc;
28 import java.io.StringReader JavaDoc;
29 import java.lang.reflect.InvocationTargetException JavaDoc;
30 import java.lang.reflect.Method JavaDoc;
31 import java.util.HashMap JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Vector JavaDoc;
34
35 import org.apache.tools.ant.BuildException;
36 import org.apache.tools.ant.MagicNames;
37 import org.apache.tools.ant.Project;
38 import org.apache.tools.ant.Task;
39 import org.apache.tools.ant.taskdefs.condition.Os;
40 import org.apache.tools.ant.types.Commandline;
41 import org.apache.tools.ant.util.FileUtils;
42 import org.apache.tools.ant.util.StringUtils;
43
44 /**
45  * Runs an external program.
46  *
47  * @since Ant 1.2
48  *
49  */

50 public class Execute {
51
52     /** Invalid exit code.
53      * set to {@link Integer#MAX_VALUE}
54      */

55     public static final int INVALID = Integer.MAX_VALUE;
56
57     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
58
59     private String JavaDoc[] cmdl = null;
60     private String JavaDoc[] env = null;
61     private int exitValue = INVALID;
62     private ExecuteStreamHandler streamHandler;
63     private ExecuteWatchdog watchdog;
64     private File JavaDoc workingDirectory = null;
65     private Project project = null;
66     private boolean newEnvironment = false;
67     //TODO: nothing appears to read this but is set using a public setter.
68
private boolean spawn = false;
69
70
71     /** Controls whether the VM is used to launch commands, where possible. */
72     private boolean useVMLauncher = true;
73
74     private static String JavaDoc antWorkingDirectory = System.getProperty("user.dir");
75     private static CommandLauncher vmLauncher = null;
76     private static CommandLauncher shellLauncher = null;
77     private static Vector JavaDoc procEnvironment = null;
78
79     /** Used to destroy processes when the VM exits. */
80     private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
81
82     /** Used for replacing env variables */
83     private static boolean environmentCaseInSensitive = false;
84
85     /*
86      * Builds a command launcher for the OS and JVM we are running under.
87      */

88     static {
89         // Try using a JDK 1.3 launcher
90
try {
91             if (!Os.isFamily("os/2")) {
92                 vmLauncher = new Java13CommandLauncher();
93             }
94         } catch (NoSuchMethodException JavaDoc exc) {
95             // Ignore and keep trying
96
}
97         if (Os.isFamily("mac") && !Os.isFamily("unix")) {
98             // Mac
99
shellLauncher = new MacCommandLauncher(new CommandLauncher());
100         } else if (Os.isFamily("os/2")) {
101             // OS/2
102
shellLauncher = new OS2CommandLauncher(new CommandLauncher());
103         } else if (Os.isFamily("windows")) {
104             environmentCaseInSensitive = true;
105             CommandLauncher baseLauncher = new CommandLauncher();
106
107             if (!Os.isFamily("win9x")) {
108                 // Windows XP/2000/NT
109
shellLauncher = new WinNTCommandLauncher(baseLauncher);
110             } else {
111                 // Windows 98/95 - need to use an auxiliary script
112
shellLauncher
113                     = new ScriptCommandLauncher("bin/antRun.bat", baseLauncher);
114             }
115         } else if (Os.isFamily("netware")) {
116
117             CommandLauncher baseLauncher = new CommandLauncher();
118
119             shellLauncher
120                 = new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher);
121         } else if (Os.isFamily("openvms")) {
122             // OpenVMS
123
try {
124                 shellLauncher = new VmsCommandLauncher();
125             } catch (NoSuchMethodException JavaDoc exc) {
126             // Ignore and keep trying
127
}
128         } else {
129             // Generic
130
shellLauncher = new ScriptCommandLauncher("bin/antRun",
131                 new CommandLauncher());
132         }
133     }
134
135     /**
136      * Set whether or not you want the process to be spawned.
137      * Default is not spawned.
138      *
139      * @param spawn if true you do not want Ant
140      * to wait for the end of the process.
141      *
142      * @since Ant 1.6
143      */

144     public void setSpawn(boolean spawn) {
145         this.spawn = spawn;
146     }
147
148     /**
149      * Find the list of environment variables for this process.
150      *
151      * @return a vector containing the environment variables.
152      * The vector elements are strings formatted like variable = value.
153      */

154     public static synchronized Vector JavaDoc getProcEnvironment() {
155         if (procEnvironment != null) {
156             return procEnvironment;
157         }
158         procEnvironment = new Vector JavaDoc();
159         try {
160             ByteArrayOutputStream JavaDoc out = new ByteArrayOutputStream JavaDoc();
161             Execute exe = new Execute(new PumpStreamHandler(out));
162             exe.setCommandline(getProcEnvCommand());
163             // Make sure we do not recurse forever
164
exe.setNewenvironment(true);
165             int retval = exe.execute();
166             if (retval != 0) {
167                 // Just try to use what we got
168
}
169             BufferedReader JavaDoc in =
170                 new BufferedReader JavaDoc(new StringReader JavaDoc(toString(out)));
171
172             if (Os.isFamily("openvms")) {
173                 procEnvironment = addVMSLogicals(procEnvironment, in);
174                 return procEnvironment;
175             }
176             String JavaDoc var = null;
177             String JavaDoc line, lineSep = StringUtils.LINE_SEP;
178             while ((line = in.readLine()) != null) {
179                 if (line.indexOf('=') == -1) {
180                     // Chunk part of previous env var (UNIX env vars can
181
// contain embedded new lines).
182
if (var == null) {
183                         var = lineSep + line;
184                     } else {
185                         var += lineSep + line;
186                     }
187                 } else {
188                     // New env var...append the previous one if we have it.
189
if (var != null) {
190                         procEnvironment.addElement(var);
191                     }
192                     var = line;
193                 }
194             }
195             // Since we "look ahead" before adding, there's one last env var.
196
if (var != null) {
197                 procEnvironment.addElement(var);
198             }
199         } catch (java.io.IOException JavaDoc exc) {
200             exc.printStackTrace();
201             // Just try to see how much we got
202
}
203         return procEnvironment;
204     }
205
206     /**
207      * This is the operation to get our environment.
208      * It is a notorious troublespot pre-Java1.5, and should be approached
209      * with extreme caution.
210      * @return
211      */

212     private static String JavaDoc[] getProcEnvCommand() {
213         if (Os.isFamily("os/2")) {
214             // OS/2 - use same mechanism as Windows 2000
215
return new String JavaDoc[] {"cmd", "/c", "set" };
216         } else if (Os.isFamily("windows")) {
217             // Determine if we're running under XP/2000/NT or 98/95
218
if (Os.isFamily("win9x")) {
219                 // Windows 98/95
220
return new String JavaDoc[] {"command.com", "/c", "set" };
221             } else {
222                 // Windows XP/2000/NT/2003
223
return new String JavaDoc[] {"cmd", "/c", "set" };
224             }
225         } else if (Os.isFamily("z/os") || Os.isFamily("unix")) {
226             // On most systems one could use: /bin/sh -c env
227

228             // Some systems have /bin/env, others /usr/bin/env, just try
229
String JavaDoc[] cmd = new String JavaDoc[1];
230             if (new File JavaDoc("/bin/env").canRead()) {
231                 cmd[0] = "/bin/env";
232             } else if (new File JavaDoc("/usr/bin/env").canRead()) {
233                 cmd[0] = "/usr/bin/env";
234             } else {
235                 // rely on PATH
236
cmd[0] = "env";
237             }
238             return cmd;
239         } else if (Os.isFamily("netware") || Os.isFamily("os/400")) {
240             // rely on PATH
241
return new String JavaDoc[] {"env"};
242         } else if (Os.isFamily("openvms")) {
243             return new String JavaDoc[] {"show", "logical"};
244         } else {
245             // MAC OS 9 and previous
246
//TODO: I have no idea how to get it, someone must fix it
247
return null;
248         }
249     }
250
251     /**
252      * ByteArrayOutputStream#toString doesn't seem to work reliably on
253      * OS/390, at least not the way we use it in the execution
254      * context.
255      *
256      * @param bos the output stream that one wants to read.
257      * @return the output stream as a string, read with
258      * special encodings in the case of z/os and os/400.
259      *
260      * @since Ant 1.5
261      */

262     public static String JavaDoc toString(ByteArrayOutputStream JavaDoc bos) {
263         if (Os.isFamily("z/os")) {
264             try {
265                 return bos.toString("Cp1047");
266             } catch (java.io.UnsupportedEncodingException JavaDoc e) {
267                 //noop default encoding used
268
}
269         } else if (Os.isFamily("os/400")) {
270             try {
271                 return bos.toString("Cp500");
272             } catch (java.io.UnsupportedEncodingException JavaDoc e) {
273                 //noop default encoding used
274
}
275         }
276         return bos.toString();
277     }
278
279     /**
280      * Creates a new execute object using <code>PumpStreamHandler</code> for
281      * stream handling.
282      */

283     public Execute() {
284         this(new PumpStreamHandler(), null);
285     }
286
287     /**
288      * Creates a new execute object.
289      *
290      * @param streamHandler the stream handler used to handle the input and
291      * output streams of the subprocess.
292      */

293     public Execute(ExecuteStreamHandler streamHandler) {
294         this(streamHandler, null);
295     }
296
297     /**
298      * Creates a new execute object.
299      *
300      * @param streamHandler the stream handler used to handle the input and
301      * output streams of the subprocess.
302      * @param watchdog a watchdog for the subprocess or <code>null</code> to
303      * to disable a timeout for the subprocess.
304      */

305     public Execute(ExecuteStreamHandler streamHandler,
306                    ExecuteWatchdog watchdog) {
307         setStreamHandler(streamHandler);
308         this.watchdog = watchdog;
309         //By default, use the shell launcher for VMS
310
//
311
if (Os.isFamily("openvms")) {
312             useVMLauncher = false;
313         }
314     }
315
316     /**
317      * Set the stream handler to use.
318      * @param streamHandler ExecuteStreamHandler.
319      * @since Ant 1.6
320      */

321     public void setStreamHandler(ExecuteStreamHandler streamHandler) {
322         this.streamHandler = streamHandler;
323     }
324
325     /**
326      * Returns the commandline used to create a subprocess.
327      *
328      * @return the commandline used to create a subprocess.
329      */

330     public String JavaDoc[] getCommandline() {
331         return cmdl;
332     }
333
334     /**
335      * Sets the commandline of the subprocess to launch.
336      *
337      * @param commandline the commandline of the subprocess to launch.
338      */

339     public void setCommandline(String JavaDoc[] commandline) {
340         cmdl = commandline;
341     }
342
343     /**
344      * Set whether to propagate the default environment or not.
345      *
346      * @param newenv whether to propagate the process environment.
347      */

348     public void setNewenvironment(boolean newenv) {
349         newEnvironment = newenv;
350     }
351
352     /**
353      * Returns the environment used to create a subprocess.
354      *
355      * @return the environment used to create a subprocess.
356      */

357     public String JavaDoc[] getEnvironment() {
358         return (env == null || newEnvironment)
359             ? env : patchEnvironment();
360     }
361
362     /**
363      * Sets the environment variables for the subprocess to launch.
364      *
365      * @param env array of Strings, each element of which has
366      * an environment variable settings in format <em>key=value</em>.
367      */

368     public void setEnvironment(String JavaDoc[] env) {
369         this.env = env;
370     }
371
372     /**
373      * Sets the working directory of the process to execute.
374      *
375      * <p>This is emulated using the antRun scripts unless the OS is
376      * Windows NT in which case a cmd.exe is spawned,
377      * or MRJ and setting user.dir works, or JDK 1.3 and there is
378      * official support in java.lang.Runtime.
379      *
380      * @param wd the working directory of the process.
381      */

382     public void setWorkingDirectory(File JavaDoc wd) {
383         workingDirectory =
384             (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory))
385             ? null : wd;
386     }
387
388     /**
389      * Return the working directory.
390      * @return the directory as a File.
391      * @since Ant 1.7
392      */

393     public File JavaDoc getWorkingDirectory() {
394         return workingDirectory == null ? new File JavaDoc(antWorkingDirectory)
395                                         : workingDirectory;
396     }
397
398     /**
399      * Set the name of the antRun script using the project's value.
400      *
401      * @param project the current project.
402      *
403      * @throws BuildException not clear when it is going to throw an exception, but
404      * it is the method's signature.
405      */

406     public void setAntRun(Project project) throws BuildException {
407         this.project = project;
408     }
409
410     /**
411      * Launch this execution through the VM, where possible, rather than through
412      * the OS's shell. In some cases and operating systems using the shell will
413      * allow the shell to perform additional processing such as associating an
414      * executable with a script, etc.
415      *
416      * @param useVMLauncher true if exec should launch through the VM,
417      * false if the shell should be used to launch the
418      * command.
419      */

420     public void setVMLauncher(boolean useVMLauncher) {
421         this.useVMLauncher = useVMLauncher;
422     }
423
424     /**
425      * Creates a process that runs a command.
426      *
427      * @param project the Project, only used for logging purposes, may be null.
428      * @param command the command to run.
429      * @param env the environment for the command.
430      * @param dir the working directory for the command.
431      * @param useVM use the built-in exec command for JDK 1.3 if available.
432      * @return the process started.
433      * @throws IOException forwarded from the particular launcher used.
434      *
435      * @since Ant 1.5
436      */

437     public static Process JavaDoc launch(Project project, String JavaDoc[] command,
438                                  String JavaDoc[] env, File JavaDoc dir, boolean useVM)
439         throws IOException JavaDoc {
440         if (dir != null && !dir.exists()) {
441             throw new BuildException(dir + " doesn't exist.");
442         }
443         CommandLauncher launcher
444             = ((useVM && vmLauncher != null) ? vmLauncher : shellLauncher);
445         return launcher.exec(project, command, env, dir);
446     }
447
448     /**
449      * Runs a process defined by the command line and returns its exit status.
450      *
451      * @return the exit status of the subprocess or <code>INVALID</code>.
452      * @exception java.io.IOException The exception is thrown, if launching
453      * of the subprocess failed.
454      */

455     public int execute() throws IOException JavaDoc {
456         if (workingDirectory != null && !workingDirectory.exists()) {
457             throw new BuildException(workingDirectory + " doesn't exist.");
458         }
459         final Process JavaDoc process = launch(project, getCommandline(),
460                                        getEnvironment(), workingDirectory,
461                                        useVMLauncher);
462         try {
463             streamHandler.setProcessInputStream(process.getOutputStream());
464             streamHandler.setProcessOutputStream(process.getInputStream());
465             streamHandler.setProcessErrorStream(process.getErrorStream());
466         } catch (IOException JavaDoc e) {
467             process.destroy();
468             throw e;
469         }
470         streamHandler.start();
471
472         try {
473             // add the process to the list of those to destroy if the VM exits
474
//
475
processDestroyer.add(process);
476
477             if (watchdog != null) {
478                 watchdog.start(process);
479             }
480             waitFor(process);
481
482             if (watchdog != null) {
483                 watchdog.stop();
484             }
485             streamHandler.stop();
486             closeStreams(process);
487
488             if (watchdog != null) {
489                 watchdog.checkException();
490             }
491             return getExitValue();
492         } catch (ThreadDeath JavaDoc t) {
493             // #31928: forcibly kill it before continuing.
494
process.destroy();
495             throw t;
496         } finally {
497             // remove the process to the list of those to destroy if
498
// the VM exits
499
//
500
processDestroyer.remove(process);
501         }
502     }
503
504     /**
505      * Starts a process defined by the command line.
506      * Ant will not wait for this process, nor log its output.
507      *
508      * @throws java.io.IOException The exception is thrown, if launching
509      * of the subprocess failed.
510      * @since Ant 1.6
511      */

512     public void spawn() throws IOException JavaDoc {
513         if (workingDirectory != null && !workingDirectory.exists()) {
514             throw new BuildException(workingDirectory + " doesn't exist.");
515         }
516         final Process JavaDoc process = launch(project, getCommandline(),
517                                        getEnvironment(), workingDirectory,
518                                        useVMLauncher);
519         if (Os.isFamily("windows")) {
520             try {
521                 Thread.sleep(1000);
522             } catch (InterruptedException JavaDoc e) {
523                 project.log("interruption in the sleep after having spawned a"
524                             + " process", Project.MSG_VERBOSE);
525             }
526         }
527         OutputStream JavaDoc dummyOut = new OutputStream JavaDoc() {
528             public void write(int b) throws IOException JavaDoc {
529             }
530         };
531
532         ExecuteStreamHandler handler = new PumpStreamHandler(dummyOut);
533         handler.setProcessErrorStream(process.getErrorStream());
534         handler.setProcessOutputStream(process.getInputStream());
535         handler.start();
536         process.getOutputStream().close();
537
538         project.log("spawned process " + process.toString(),
539                     Project.MSG_VERBOSE);
540     }
541
542     /**
543      * Wait for a given process.
544      *
545      * @param process the process one wants to wait for.
546      */

547     protected void waitFor(Process JavaDoc process) {
548         try {
549             process.waitFor();
550             setExitValue(process.exitValue());
551         } catch (InterruptedException JavaDoc e) {
552             process.destroy();
553         }
554     }
555
556     /**
557      * Set the exit value.
558      *
559      * @param value exit value of the process.
560      */

561     protected void setExitValue(int value) {
562         exitValue = value;
563     }
564
565     /**
566      * Query the exit value of the process.
567      * @return the exit value or Execute.INVALID if no exit value has
568      * been received.
569      */

570     public int getExitValue() {
571         return exitValue;
572     }
573
574     /**
575      * Checks whether <code>exitValue</code> signals a failure on the current
576      * system (OS specific).
577      *
578      * <p><b>Note</b> that this method relies on the conventions of
579      * the OS, it will return false results if the application you are
580      * running doesn't follow these conventions. One notable
581      * exception is the Java VM provided by HP for OpenVMS - it will
582      * return 0 if successful (like on any other platform), but this
583      * signals a failure on OpenVMS. So if you execute a new Java VM
584      * on OpenVMS, you cannot trust this method.</p>
585      *
586      * @param exitValue the exit value (return code) to be checked.
587      * @return <code>true</code> if <code>exitValue</code> signals a failure.
588      */

589     public static boolean isFailure(int exitValue) {
590         //on openvms even exit value signals failure;
591
// for other platforms nonzero exit value signals failure
592
return Os.isFamily("openvms")
593             ? (exitValue % 2 == 0) : (exitValue != 0);
594     }
595
596     /**
597      * Did this execute return in a failure.
598      * @see #isFailure(int)
599      * @return true if and only if the exit code is interpreted as a failure
600      * @since Ant1.7
601      */

602     public boolean isFailure() {
603         return isFailure(getExitValue());
604     }
605
606     /**
607      * Test for an untimely death of the process.
608      * @return true if a watchdog had to kill the process.
609      * @since Ant 1.5
610      */

611     public boolean killedProcess() {
612         return watchdog != null && watchdog.killedProcess();
613     }
614
615     /**
616      * Patch the current environment with the new values from the user.
617      * @return the patched environment.
618      */

619     private String JavaDoc[] patchEnvironment() {
620         // On OpenVMS Runtime#exec() doesn't support the environment array,
621
// so we only return the new values which then will be set in
622
// the generated DCL script, inheriting the parent process environment
623
if (Os.isFamily("openvms")) {
624             return env;
625         }
626         Vector JavaDoc osEnv = (Vector JavaDoc) getProcEnvironment().clone();
627         for (int i = 0; i < env.length; i++) {
628             String JavaDoc keyValue = env[i];
629             // Get key including "="
630
String JavaDoc key = keyValue.substring(0, keyValue.indexOf('=') + 1);
631             if (environmentCaseInSensitive) {
632                 // Nb: using default locale as key is a env name
633
key = key.toLowerCase();
634             }
635             int size = osEnv.size();
636             // Find the key in the current enviroment copy
637
// and remove it.
638
for (int j = 0; j < size; j++) {
639                 String JavaDoc osEnvItem = (String JavaDoc) osEnv.elementAt(j);
640                 String JavaDoc convertedItem = environmentCaseInSensitive
641                     ? osEnvItem.toLowerCase() : osEnvItem;
642                 if (convertedItem.startsWith(key)) {
643                     osEnv.removeElementAt(j);
644                     if (environmentCaseInSensitive) {
645                         // Use the original casiness of the key
646
keyValue = osEnvItem.substring(0, key.length())
647                             + keyValue.substring(key.length());
648                     }
649                     break;
650                 }
651             }
652             // Add the key to the enviromnent copy
653
osEnv.addElement(keyValue);
654         }
655         return (String JavaDoc[]) (osEnv.toArray(new String JavaDoc[osEnv.size()]));
656     }
657
658     /**
659      * A utility method that runs an external command. Writes the output and
660      * error streams of the command to the project log.
661      *
662      * @param task The task that the command is part of. Used for logging
663      * @param cmdline The command to execute.
664      *
665      * @throws BuildException if the command does not exit successfully.
666      */

667     public static void runCommand(Task task, String JavaDoc[] cmdline)
668         throws BuildException {
669         try {
670             task.log(Commandline.describeCommand(cmdline),
671                      Project.MSG_VERBOSE);
672             Execute exe = new Execute(
673                 new LogStreamHandler(task, Project.MSG_INFO, Project.MSG_ERR));
674             exe.setAntRun(task.getProject());
675             exe.setCommandline(cmdline);
676             int retval = exe.execute();
677             if (isFailure(retval)) {
678                 throw new BuildException(cmdline[0]
679                     + " failed with return code " + retval, task.getLocation());
680             }
681         } catch (java.io.IOException JavaDoc exc) {
682             throw new BuildException("Could not launch " + cmdline[0] + ": "
683                 + exc, task.getLocation());
684         }
685     }
686
687     /**
688      * Close the streams belonging to the given Process.
689      * @param process the <code>Process</code>.
690      */

691     public static void closeStreams(Process JavaDoc process) {
692         FileUtils.close(process.getInputStream());
693         FileUtils.close(process.getOutputStream());
694         FileUtils.close(process.getErrorStream());
695     }
696
697     /**
698      * This method is VMS specific and used by getProcEnvironment().
699      *
700      * Parses VMS logicals from <code>in</code> and adds them to
701      * <code>environment</code>. <code>in</code> is expected to be the
702      * output of "SHOW LOGICAL". The method takes care of parsing the output
703      * correctly as well as making sure that a logical defined in multiple
704      * tables only gets added from the highest order table. Logicals with
705      * multiple equivalence names are mapped to a variable with multiple
706      * values separated by a comma (,).
707      */

708     private static Vector JavaDoc addVMSLogicals(Vector JavaDoc environment, BufferedReader JavaDoc in)
709         throws IOException JavaDoc {
710         HashMap JavaDoc logicals = new HashMap JavaDoc();
711         String JavaDoc logName = null, logValue = null, newLogName;
712         String JavaDoc line = null;
713         while ((line = in.readLine()) != null) {
714             // parse the VMS logicals into required format ("VAR=VAL[,VAL2]")
715
if (line.startsWith("\t=")) {
716                 // further equivalence name of previous logical
717
if (logName != null) {
718                     logValue += "," + line.substring(4, line.length() - 1);
719                 }
720             } else if (line.startsWith(" \"")) {
721                 // new logical?
722
if (logName != null) {
723                     logicals.put(logName, logValue);
724                 }
725                 int eqIndex = line.indexOf('=');
726                 newLogName = line.substring(3, eqIndex - 2);
727                 if (logicals.containsKey(newLogName)) {
728                     // already got this logical from a higher order table
729
logName = null;
730                 } else {
731                     logName = newLogName;
732                     logValue = line.substring(eqIndex + 3, line.length() - 1);
733                 }
734             }
735         }
736         // Since we "look ahead" before adding, there's one last env var.
737
if (logName != null) {
738             logicals.put(logName, logValue);
739         }
740         for (Iterator JavaDoc i = logicals.keySet().iterator(); i.hasNext();) {
741             String JavaDoc logical = (String JavaDoc) i.next();
742             environment.add(logical + "=" + logicals.get(logical));
743         }
744         return environment;
745     }
746
747     /**
748      * A command launcher for a particular JVM/OS platform. This class is
749      * a general purpose command launcher which can only launch commands in
750      * the current working directory.
751      */

752     private static class CommandLauncher {
753         /**
754          * Launches the given command in a new process.
755          *
756          * @param project The project that the command is part of.
757          * @param cmd The command to execute.
758          * @param env The environment for the new process. If null,
759          * the environment of the current process is used.
760          * @return the created Process.
761          * @throws IOException if attempting to run a command in a
762          * specific directory.
763          */

764         public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env)
765              throws IOException JavaDoc {
766             if (project != null) {
767                 project.log("Execute:CommandLauncher: "
768                     + Commandline.describeCommand(cmd), Project.MSG_DEBUG);
769             }
770             return Runtime.getRuntime().exec(cmd, env);
771         }
772
773         /**
774          * Launches the given command in a new process, in the given working
775          * directory.
776          *
777          * @param project The project that the command is part of.
778          * @param cmd The command to execute.
779          * @param env The environment for the new process. If null,
780          * the environment of the current process is used.
781          * @param workingDir The directory to start the command in. If null,
782          * the current directory is used.
783          * @return the created Process.
784          * @throws IOException if trying to change directory.
785          */

786         public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
787                             File JavaDoc workingDir) throws IOException JavaDoc {
788             if (workingDir == null) {
789                 return exec(project, cmd, env);
790             }
791             throw new IOException JavaDoc("Cannot execute a process in different "
792                 + "directory under this JVM");
793         }
794     }
795
796     /**
797      * A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
798      * Runtime.exec() command.
799      */

800     private static class Java13CommandLauncher extends CommandLauncher {
801         private Method JavaDoc myExecWithCWD;
802
803         public Java13CommandLauncher() throws NoSuchMethodException JavaDoc {
804             // Locate method Runtime.exec(String[] cmdarray,
805
// String[] envp, File dir)
806
myExecWithCWD = Runtime JavaDoc.class.getMethod("exec",
807                 new Class JavaDoc[] {String JavaDoc[].class, String JavaDoc[].class, File JavaDoc.class});
808         }
809
810         /**
811          * Launches the given command in a new process, in the given working
812          * directory.
813          * @param project the Ant project.
814          * @param cmd the command line to execute as an array of strings.
815          * @param env the environment to set as an array of strings.
816          * @param workingDir the working directory where the command
817          * should run.
818          * @return the created Process.
819          * @throws IOException probably forwarded from Runtime#exec.
820          */

821         public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
822                             File JavaDoc workingDir) throws IOException JavaDoc {
823             try {
824                 if (project != null) {
825                     project.log("Execute:Java13CommandLauncher: "
826                         + Commandline.describeCommand(cmd), Project.MSG_DEBUG);
827                 }
828                 return (Process JavaDoc) myExecWithCWD.invoke(Runtime.getRuntime(),
829                    /* the arguments: */ new Object JavaDoc[] {cmd, env, workingDir});
830             } catch (InvocationTargetException JavaDoc exc) {
831                 Throwable JavaDoc realexc = exc.getTargetException();
832                 if (realexc instanceof ThreadDeath JavaDoc) {
833                     throw (ThreadDeath JavaDoc) realexc;
834                 } else if (realexc instanceof IOException JavaDoc) {
835                     throw (IOException JavaDoc) realexc;
836                 } else {
837                     throw new BuildException("Unable to execute command",
838                                              realexc);
839                 }
840             } catch (Exception JavaDoc exc) {
841                 // IllegalAccess, IllegalArgument, ClassCast
842
throw new BuildException("Unable to execute command", exc);
843             }
844         }
845     }
846
847     /**
848      * A command launcher that proxies another command launcher.
849      *
850      * Sub-classes override exec(args, env, workdir).
851      */

852     private static class CommandLauncherProxy extends CommandLauncher {
853         private CommandLauncher myLauncher;
854
855         CommandLauncherProxy(CommandLauncher launcher) {
856             myLauncher = launcher;
857         }
858
859         /**
860          * Launches the given command in a new process. Delegates this
861          * method to the proxied launcher.
862          * @param project the Ant project.
863          * @param cmd the command line to execute as an array of strings.
864          * @param env the environment to set as an array of strings.
865          * @return the created Process.
866          * @throws IOException forwarded from the exec method of the
867          * command launcher.
868          */

869         public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env)
870             throws IOException JavaDoc {
871             return myLauncher.exec(project, cmd, env);
872         }
873     }
874
875     /**
876      * A command launcher for OS/2 that uses 'cmd.exe' when launching
877      * commands in directories other than the current working
878      * directory.
879      *
880      * <p>Unlike Windows NT and friends, OS/2's cd doesn't support the
881      * /d switch to change drives and directories in one go.</p>
882      */

883     private static class OS2CommandLauncher extends CommandLauncherProxy {
884         OS2CommandLauncher(CommandLauncher launcher) {
885             super(launcher);
886         }
887
888         /**
889          * Launches the given command in a new process, in the given working
890          * directory.
891          * @param project the Ant project.
892          * @param cmd the command line to execute as an array of strings.
893          * @param env the environment to set as an array of strings.
894          * @param workingDir working directory where the command should run.
895          * @return the created Process.
896          * @throws IOException forwarded from the exec method of the
897          * command launcher.
898          */

899         public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
900                             File JavaDoc workingDir) throws IOException JavaDoc {
901             File JavaDoc commandDir = workingDir;
902             if (workingDir == null) {
903                 if (project != null) {
904                     commandDir = project.getBaseDir();
905                 } else {
906                     return exec(project, cmd, env);
907                 }
908             }
909             // Use cmd.exe to change to the specified drive and
910
// directory before running the command
911
final int preCmdLength = 7;
912             final String JavaDoc cmdDir = commandDir.getAbsolutePath();
913             String JavaDoc[] newcmd = new String JavaDoc[cmd.length + preCmdLength];
914             newcmd[0] = "cmd";
915             newcmd[1] = "/c";
916             newcmd[2] = cmdDir.substring(0, 2);
917             newcmd[3] = "&&";
918             newcmd[4] = "cd";
919             newcmd[5] = cmdDir.substring(2);
920             newcmd[6] = "&&";
921             System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
922
923             return exec(project, newcmd, env);
924         }
925     }
926
927     /**
928      * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when
929      * launching commands in directories other than the current working
930      * directory.
931      */

932     private static class WinNTCommandLauncher extends CommandLauncherProxy {
933         WinNTCommandLauncher(CommandLauncher launcher) {
934             super(launcher);
935         }
936
937         /**
938          * Launches the given command in a new process, in the given working
939          * directory.
940          * @param project the Ant project.
941          * @param cmd the command line to execute as an array of strings.
942          * @param env the environment to set as an array of strings.
943          * @param workingDir working directory where the command should run.
944          * @return the created Process.
945          * @throws IOException forwarded from the exec method of the
946          * command launcher.
947          */

948         public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
949                             File JavaDoc workingDir) throws IOException JavaDoc {
950             File JavaDoc commandDir = workingDir;
951             if (workingDir == null) {
952                 if (project != null) {
953                     commandDir = project.getBaseDir();
954                 } else {
955                     return exec(project, cmd, env);
956                 }
957             }
958             // Use cmd.exe to change to the specified directory before running
959
// the command
960
final int preCmdLength = 6;
961             String JavaDoc[] newcmd = new String JavaDoc[cmd.length + preCmdLength];
962             newcmd[0] = "cmd";
963             newcmd[1] = "/c";
964             newcmd[2] = "cd";
965             newcmd[3] = "/d";
966             newcmd[4] = commandDir.getAbsolutePath();
967             newcmd[5] = "&&";
968             System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
969
970             return exec(project, newcmd, env);
971         }
972     }
973
974     /**
975      * A command launcher for Mac that uses a dodgy mechanism to change
976      * working directory before launching commands.
977      */

978     private static class MacCommandLauncher extends CommandLauncherProxy {
979         MacCommandLauncher(CommandLauncher launcher) {
980             super(launcher);
981         }
982
983         /**
984          * Launches the given command in a new process, in the given working
985          * directory.
986          * @param project the Ant project.
987          * @param cmd the command line to execute as an array of strings.
988          * @param env the environment to set as an array of strings.
989          * @param workingDir working directory where the command should run.
990          * @return the created Process.
991          * @throws IOException forwarded from the exec method of the
992          * command launcher.
993          */

994         public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
995                             File JavaDoc workingDir) throws IOException JavaDoc {
996             if (workingDir == null) {
997                 return exec(project, cmd, env);
998             }
999             System.getProperties().put("user.dir", workingDir.getAbsolutePath());
1000            try {
1001                return exec(project, cmd, env);
1002            } finally {
1003                System.getProperties().put("user.dir", antWorkingDirectory);
1004            }
1005        }
1006    }
1007
1008    /**
1009     * A command launcher that uses an auxiliary script to launch commands
1010     * in directories other than the current working directory.
1011     */

1012    private static class ScriptCommandLauncher extends CommandLauncherProxy {
1013        ScriptCommandLauncher(String JavaDoc script, CommandLauncher launcher) {
1014            super(launcher);
1015            myScript = script;
1016        }
1017
1018        /**
1019         * Launches the given command in a new process, in the given working
1020         * directory.
1021         * @param project the Ant project.
1022         * @param cmd the command line to execute as an array of strings.
1023         * @param env the environment to set as an array of strings.
1024         * @param workingDir working directory where the command should run.
1025         * @return the created Process.
1026         * @throws IOException forwarded from the exec method of the
1027         * command launcher.
1028         */

1029        public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
1030                            File JavaDoc workingDir) throws IOException JavaDoc {
1031            if (project == null) {
1032                if (workingDir == null) {
1033                    return exec(project, cmd, env);
1034                }
1035                throw new IOException JavaDoc("Cannot locate antRun script: "
1036                    + "No project provided");
1037            }
1038            // Locate the auxiliary script
1039
String JavaDoc antHome = project.getProperty(MagicNames.ANT_HOME);
1040            if (antHome == null) {
1041                throw new IOException JavaDoc("Cannot locate antRun script: "
1042                    + "Property '" + MagicNames.ANT_HOME + "' not found");
1043            }
1044            String JavaDoc antRun =
1045                FILE_UTILS.resolveFile(project.getBaseDir(),
1046                        antHome + File.separator + myScript).toString();
1047
1048            // Build the command
1049
File JavaDoc commandDir = workingDir;
1050            if (workingDir == null && project != null) {
1051                commandDir = project.getBaseDir();
1052            }
1053            String JavaDoc[] newcmd = new String JavaDoc[cmd.length + 2];
1054            newcmd[0] = antRun;
1055            newcmd[1] = commandDir.getAbsolutePath();
1056            System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
1057
1058            return exec(project, newcmd, env);
1059        }
1060
1061        private String JavaDoc myScript;
1062    }
1063
1064    /**
1065     * A command launcher that uses an auxiliary perl script to launch commands
1066     * in directories other than the current working directory.
1067     */

1068    private static class PerlScriptCommandLauncher
1069        extends CommandLauncherProxy {
1070        private String JavaDoc myScript;
1071
1072        PerlScriptCommandLauncher(String JavaDoc script, CommandLauncher launcher) {
1073            super(launcher);
1074            myScript = script;
1075        }
1076
1077        /**
1078         * Launches the given command in a new process, in the given working
1079         * directory.
1080         * @param project the Ant project.
1081         * @param cmd the command line to execute as an array of strings.
1082         * @param env the environment to set as an array of strings.
1083         * @param workingDir working directory where the command should run.
1084         * @return the created Process.
1085         * @throws IOException forwarded from the exec method of the
1086         * command launcher.
1087         */

1088        public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
1089                            File JavaDoc workingDir) throws IOException JavaDoc {
1090            if (project == null) {
1091                if (workingDir == null) {
1092                    return exec(project, cmd, env);
1093                }
1094                throw new IOException JavaDoc("Cannot locate antRun script: "
1095                    + "No project provided");
1096            }
1097            // Locate the auxiliary script
1098
String JavaDoc antHome = project.getProperty(MagicNames.ANT_HOME);
1099            if (antHome == null) {
1100                throw new IOException JavaDoc("Cannot locate antRun script: "
1101                    + "Property '" + MagicNames.ANT_HOME + "' not found");
1102            }
1103            String JavaDoc antRun =
1104                FILE_UTILS.resolveFile(project.getBaseDir(),
1105                        antHome + File.separator + myScript).toString();
1106
1107            // Build the command
1108
File JavaDoc commandDir = workingDir;
1109            if (workingDir == null && project != null) {
1110                commandDir = project.getBaseDir();
1111            }
1112            String JavaDoc[] newcmd = new String JavaDoc[cmd.length + 3];
1113            newcmd[0] = "perl";
1114            newcmd[1] = antRun;
1115            newcmd[2] = commandDir.getAbsolutePath();
1116            System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
1117
1118            return exec(project, newcmd, env);
1119        }
1120    }
1121
1122    /**
1123     * A command launcher for VMS that writes the command to a temporary DCL
1124     * script before launching commands. This is due to limitations of both
1125     * the DCL interpreter and the Java VM implementation.
1126     */

1127    private static class VmsCommandLauncher extends Java13CommandLauncher {
1128
1129        public VmsCommandLauncher() throws NoSuchMethodException JavaDoc {
1130            super();
1131        }
1132
1133        /**
1134         * Launches the given command in a new process.
1135         * @param project the Ant project.
1136         * @param cmd the command line to execute as an array of strings.
1137         * @param env the environment to set as an array of strings.
1138         * @return the created Process.
1139         * @throws IOException forwarded from the exec method of the
1140         * command launcher.
1141         */

1142        public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env)
1143            throws IOException JavaDoc {
1144            File JavaDoc cmdFile = createCommandFile(cmd, env);
1145            Process JavaDoc p
1146                = super.exec(project, new String JavaDoc[] {cmdFile.getPath()}, env);
1147            deleteAfter(cmdFile, p);
1148            return p;
1149        }
1150
1151        /**
1152         * Launches the given command in a new process, in the given working
1153         * directory. Note that under Java 1.3.1, 1.4.0 and 1.4.1 on VMS this
1154         * method only works if <code>workingDir</code> is null or the logical
1155         * JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
1156         * @param project the Ant project.
1157         * @param cmd the command line to execute as an array of strings.
1158         * @param env the environment to set as an array of strings.
1159         * @param workingDir working directory where the command should run.
1160         * @return the created Process.
1161         * @throws IOException forwarded from the exec method of the
1162         * command launcher.
1163         */

1164        public Process JavaDoc exec(Project project, String JavaDoc[] cmd, String JavaDoc[] env,
1165                            File JavaDoc workingDir) throws IOException JavaDoc {
1166            File JavaDoc cmdFile = createCommandFile(cmd, env);
1167            Process JavaDoc p = super.exec(project, new String JavaDoc[] {cmdFile.getPath()},
1168                                   env, workingDir);
1169            deleteAfter(cmdFile, p);
1170            return p;
1171        }
1172
1173        /*
1174         * Writes the command into a temporary DCL script and returns the
1175         * corresponding File object. The script will be deleted on exit.
1176         * @param cmd the command line to execute as an array of strings.
1177         * @param env the environment to set as an array of strings.
1178         * @return the command File.
1179         * @throws IOException if errors are encountered creating the file.
1180         */

1181        private File JavaDoc createCommandFile(String JavaDoc[] cmd, String JavaDoc[] env)
1182            throws IOException JavaDoc {
1183            File JavaDoc script = FILE_UTILS.createTempFile("ANT", ".COM", null);
1184            script.deleteOnExit();
1185            PrintWriter JavaDoc out = null;
1186            try {
1187                out = new PrintWriter JavaDoc(new FileWriter JavaDoc(script));
1188
1189                // add the environment as logicals to the DCL script
1190
if (env != null) {
1191                    int eqIndex;
1192                    for (int i = 0; i < env.length; i++) {
1193                        eqIndex = env[i].indexOf('=');
1194                        if (eqIndex != -1) {
1195                            out.print("$ DEFINE/NOLOG ");
1196                            out.print(env[i].substring(0, eqIndex));
1197                            out.print(" \"");
1198                            out.print(env[i].substring(eqIndex + 1));
1199                            out.println('\"');
1200                        }
1201                    }
1202                }
1203                out.print("$ " + cmd[0]);
1204                for (int i = 1; i < cmd.length; i++) {
1205                    out.println(" -");
1206                    out.print(cmd[i]);
1207                }
1208            } finally {
1209                if (out != null) {
1210                    out.close();
1211                }
1212            }
1213            return script;
1214        }
1215
1216        private void deleteAfter(final File JavaDoc f, final Process JavaDoc p) {
1217            new Thread JavaDoc() {
1218                public void run() {
1219                    try {
1220                        p.waitFor();
1221                    } catch (InterruptedException JavaDoc e) {
1222                        //ignore
1223
}
1224                    FileUtils.delete(f);
1225                }
1226            }
1227            .start();
1228        }
1229    }
1230}
1231
Popular Tags