diff --git a/WHATSNEW b/WHATSNEW index 3a962376f..26cbb4eb2 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -48,6 +48,9 @@ Other changes: * New attribute "negate" on to invert selection criteria. +* New type introduced to provide extreme I/O flexibility. + Initial support for , , and tasks. + Changes from Ant 1.6.1 to current Ant 1.6 CVS version ============================================= diff --git a/docs/manual/CoreTasks/apply.html b/docs/manual/CoreTasks/apply.html index a05e833f3..2662398fc 100644 --- a/docs/manual/CoreTasks/apply.html +++ b/docs/manual/CoreTasks/apply.html @@ -290,6 +290,19 @@ attribute.

It is possible to specify environment variables to pass to the system command via nested <env> elements. See the description in the section about exec

+

redirector

+Since Ant 1.6.2 +

A nested I/O Redirector +can be specified. <apply>'s behavior is like that of +exec with regard to +redirectors, with the exception that, in non-parallel mode, +file mapping will take place with each iteration. This grants the +user the capacity to receive input from, and send output to, different +files for each sourcefile. In this context it may be apparent +that no provision has been made whereby output/error properties +can be mapped per sourcefile; we humbly accept your gratitude for +having denied you "Enough Rope To Hang Yourself With." +

Examples

 <apply executable="ls">
@@ -334,6 +347,29 @@ of all files separated by spaces.

.o, replacing TARGETFILE with the absolute filename of the .o and SOURCEFILE with the absolute name of the .c file.

+
+<mapper id="out" type="glob"
+           from="src${file.separator}*.file"
+           to="dest${file.separator}*.out" />
+
+<apply executable="processfile" dest="dest">
+  <fileset dir="src" includes="*.file"/>
+  <mapper refid="out" />
+  <redirector>
+    <outputmapper refid="out" />
+  </redirector>
+</apply>
+
+Applies the fictitious "processfile" executable to all +files matching *.file in the src directory. +The out <mapper> has been set up to map +*.file to *.out, then this <mapper> +is used to specify targetfiles for this <apply> +task. A reference to out is then used as an +<outputmapper> nested in a <redirector>, which in turn is +nested beneath this <apply> instance. This allows us to perform +dependency checking against output files--the target files in this case. +

Copyright © 2000-2004 The Apache Software Foundation. All rights Reserved.

diff --git a/docs/manual/CoreTasks/exec.html b/docs/manual/CoreTasks/exec.html index 77ea868d5..36a65bc03 100644 --- a/docs/manual/CoreTasks/exec.html +++ b/docs/manual/CoreTasks/exec.html @@ -241,7 +241,19 @@ system command via nested <env> elements.

replaced by the absolute filename of the file by Ant. - +

redirector

+Since Ant 1.6.2 +

A nested I/O Redirector +can be specified. In general, the attributes of the redirector behave +as the corresponding attributes available at the task level. The most +notable peculiarity stems from the retention of the <exec> +attributes for backwards compatibility. Any file mapping is done +using a null sourcefile; therefore not all +Mapper types will return +results. When no results are returned, redirection specifications +will fall back to the task level attributes. In practice this means that +defaults can be specified for input, output, and error output files. +

Errors and return codes

By default the return code of a <exec> is ignored; when you set failonerror="true" then any return code signaling failure @@ -287,6 +299,27 @@ system command.

Starts the ${browser} with the specified ${file} and end the ant process. The browser will let be open.

+
+<exec executable="cat">
+    <redirector outputproperty="redirector.out"
+                errorproperty="redirector.err"
+                inputstring="blah before blah">
+        <inputfilterchain>
+            <replacestring from="before" to="after" />
+        </inputfilterchain>
+        <outputmapper type="merge" to="redirector.out" />
+        <errormapper type="merge" to="redirector.err" />
+    </redirector>
+</exec>
+
+ +Sends the string "blah before blah" to the "cat" executable, +using an <inputfilterchain> +to replace "before" with "after" on the way in. +Output is sent to the file "redirector.out" and stored +in a property of the same name. Similarly, error output is sent to +a file and a property, both named "redirector.err". +

Note: Although it may work for you to specify arguments using a simple arg-element and separate them by spaces it may fail if you switch to @@ -301,6 +334,7 @@ This problem may occur with all JDK's < 1.2.

sub process is killed and a message printed to the log. The return value of the execution will be "-1", which will halt the build if failonerror=true, but be ignored otherwise. +

Copyright © 2000-2004 The Apache Software Foundation. All rights Reserved.

diff --git a/docs/manual/CoreTasks/java.html b/docs/manual/CoreTasks/java.html index 6cfc23c93..879a7d220 100644 --- a/docs/manual/CoreTasks/java.html +++ b/docs/manual/CoreTasks/java.html @@ -243,6 +243,19 @@ subelement.

since Ant 1.6.

+

redirector

+Since Ant 1.6.2 +

A nested I/O Redirector +can be specified. In general, the attributes of the redirector behave +as the corresponding attributes available at the task level. The most +notable peculiarity stems from the retention of the <java> +attributes for backwards compatibility. Any file mapping is done +using a null sourcefile; therefore not all +Mapper types will return +results. When no results are returned, redirection specifications +will fall back to the task level attributes. In practice this means that +defaults can be specified for input, output, and error output files. +

Errors and return codes

By default the return code of a <java> is ignored. Alternatively, you can set resultproperty to the name of a property and have it assigned to the result code (barring immutability, diff --git a/docs/manual/conceptstypeslist.html b/docs/manual/conceptstypeslist.html index fb2b88818..2aaf57215 100644 --- a/docs/manual/conceptstypeslist.html +++ b/docs/manual/conceptstypeslist.html @@ -27,6 +27,7 @@ Path-like Structures
Permissions
PropertySet
+I/O Redirectors
Regexp
Selectors
XMLCatalog
diff --git a/src/etc/testcases/taskdefs/exec/apply.xml b/src/etc/testcases/taskdefs/exec/apply.xml index 46a35521a..6af64438a 100755 --- a/src/etc/testcases/taskdefs/exec/apply.xml +++ b/src/etc/testcases/taskdefs/exec/apply.xml @@ -12,16 +12,6 @@ - - - - - - - - - - @@ -38,6 +28,7 @@ s/y/blah/g${line.separator} s/z/blah/g${line.separator} + @@ -83,7 +74,7 @@ - + @@ -93,8 +84,8 @@ - - + + @@ -102,19 +93,221 @@ - + - - redirect.out=${redirect.out} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x y z${line.separator} + + + + + + + + + + + + + x before y before z${line.separator} + + + + + + + + + + + + + + + + x before y before z${line.separator} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x before y before z${line.separator} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + z before y before x${line.separator} + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + diff --git a/src/etc/testcases/taskdefs/exec/exec.xml b/src/etc/testcases/taskdefs/exec/exec.xml index 89692878f..059c338a0 100644 --- a/src/etc/testcases/taskdefs/exec/exec.xml +++ b/src/etc/testcases/taskdefs/exec/exec.xml @@ -25,6 +25,16 @@ + + + + + + + + + + @@ -98,10 +108,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x y z + + + + + + + + + + + + blah before blah + + + + + + + + + + + + + + blah before blah + + + + + + + + + + + + + + + + + + + + + + + + + + blah before blah + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Files were not created. + + + + + + + + + + + + + Files were created. + + - + + diff --git a/src/etc/testcases/taskdefs/exec/expected/utf-8 b/src/etc/testcases/taskdefs/exec/expected/utf-8 new file mode 100644 index 000000000..c1949bc19 --- /dev/null +++ b/src/etc/testcases/taskdefs/exec/expected/utf-8 @@ -0,0 +1 @@ +äöüÄÖÜß diff --git a/src/etc/testcases/taskdefs/exec/input/iso8859-1 b/src/etc/testcases/taskdefs/exec/input/iso8859-1 new file mode 100644 index 000000000..09044014b --- /dev/null +++ b/src/etc/testcases/taskdefs/exec/input/iso8859-1 @@ -0,0 +1 @@ +äöüÄÖÜß diff --git a/src/etc/testcases/taskdefs/java.xml b/src/etc/testcases/taskdefs/java.xml index 363dbeb81..f5cae5134 100644 --- a/src/etc/testcases/taskdefs/java.xml +++ b/src/etc/testcases/taskdefs/java.xml @@ -7,6 +7,7 @@ + @@ -16,6 +17,11 @@ + + + + @@ -126,18 +132,17 @@ - - - - - - - + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/filters/StringInputStream.java b/src/main/org/apache/tools/ant/filters/StringInputStream.java index 3d7f1117e..16f8b703b 100644 --- a/src/main/org/apache/tools/ant/filters/StringInputStream.java +++ b/src/main/org/apache/tools/ant/filters/StringInputStream.java @@ -20,21 +20,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.StringReader; +import org.apache.tools.ant.util.ReaderInputStream; + /** * Wraps a String as an InputStream. * */ -public class StringInputStream - extends InputStream { - - /** Source string, stored as a StringReader */ - private StringReader in; - - private String encoding; - - private byte[] slack; - - private int begin; +public class StringInputStream extends ReaderInputStream { /** * Composes a stream from a String @@ -42,159 +34,17 @@ public class StringInputStream * @param source The string to read from. Must not be null. */ public StringInputStream(String source) { - in = new StringReader(source); + super(new StringReader(source)); } /** * Composes a stream from a String with the specified encoding * * @param source The string to read from. Must not be null. - * @param encoding The encoding scheme. + * @param encoding The encoding scheme. Also must not be null. */ public StringInputStream(String source, String encoding) { - in = new StringReader(source); - this.encoding = encoding; - } - - /** - * Reads from the Stringreader, returning the same value. - * - * @return the value of the next character in the StringReader - * - * @exception IOException if the original StringReader fails to be read - */ - public synchronized int read() throws IOException { - if (in == null) { - throw new IOException("Stream Closed"); - } - - byte result; - if (slack != null && begin < slack.length) { - result = slack[begin]; - if (++begin == slack.length) { - slack = null; - } - } else { - byte[] buf = new byte[1]; - if (read(buf, 0, 1) <= 0) { - return -1; - } - result = buf[0]; - } - if (result < 0) { - return 256 + result; - } else { - return result; - } + super(new StringReader(source), encoding); } - /** - * Reads from the Stringreader into a byte array - * - * @param b the byte array to read into - * @param off the offset in the byte array - * @param len the length in the byte array to fill - * @return the actual number read into the byte array, -1 at - * the end of the stream - * @exception IOException if an error occurs - */ - public synchronized int read(byte[] b, int off, int len) - throws IOException { - - if (in == null) { - throw new IOException("Stream Closed"); - } - - while (slack == null) { - char[] buf = new char[len]; // might read too much - int n = in.read(buf); - if (n == -1) { - return -1; - } - if (n > 0) { - String s = new String(buf, 0, n); - if (encoding == null) { - slack = s.getBytes(); - } else { - slack = s.getBytes(encoding); - } - begin = 0; - } - } - - if (len > slack.length - begin) { - len = slack.length - begin; - } - - System.arraycopy(slack, begin, b, off, len); - - if ((begin += len) >= slack.length) { - slack = null; - } - return len; - } - - /** - * Marks the read limit of the StringReader. - * - * @param limit the maximum limit of bytes that can be read before the - * mark position becomes invalid - */ - public synchronized void mark(final int limit) { - try { - in.mark(limit); - } catch (IOException ioe) { - throw new RuntimeException(ioe.getMessage()); - } - } - - - /** - * @return the current number of bytes ready for reading - * @exception IOException if an error occurs - */ - public synchronized int available() throws IOException { - if (in == null) { - throw new IOException("Stream Closed"); - } - if (slack != null) { - return slack.length - begin; - } - if (in.ready()) { - return 1; - } else { - return 0; - } - } - - /** - * @return false - mark is not supported - */ - public boolean markSupported () { - return false; // would be imprecise - } - - /** - * Resets the StringReader. - * - * @exception IOException if the StringReader fails to be reset - */ - public synchronized void reset() throws IOException { - if (in == null) { - throw new IOException("Stream Closed"); - } - slack = null; - in.reset(); - } - - /** - * Closes the Stringreader. - * - * @exception IOException if the original StringReader fails to be closed - */ - public synchronized void close() throws IOException { - in.close(); - slack = null; - in = null; - } } diff --git a/src/main/org/apache/tools/ant/taskdefs/ExecTask.java b/src/main/org/apache/tools/ant/taskdefs/ExecTask.java index dbbdd0896..a5eb515c6 100644 --- a/src/main/org/apache/tools/ant/taskdefs/ExecTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/ExecTask.java @@ -27,6 +27,7 @@ import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Commandline; import org.apache.tools.ant.types.Environment; import org.apache.tools.ant.types.Path; +import org.apache.tools.ant.types.RedirectorElement; import org.apache.tools.ant.util.FileUtils; /** @@ -53,7 +54,14 @@ public class ExecTask extends Task { private boolean spawn = false; private boolean incompatibleWithSpawn = false; - private Redirector redirector = new Redirector(this); + //include locally for screening purposes + private String inputString; + private File input; + private File output; + private File error; + + protected Redirector redirector = new Redirector(this); + protected RedirectorElement redirectorElement; /** * Controls whether the VM (1.3 and above) is used to execute the @@ -141,7 +149,7 @@ public class ExecTask extends Task { * @param out name of a file to which send output to */ public void setOutput(File out) { - redirector.setOutput(out); + this.output = out; incompatibleWithSpawn = true; } @@ -151,7 +159,11 @@ public class ExecTask extends Task { * @param input name of a file to get input from */ public void setInput(File input) { - redirector.setInput(input); + if (inputString != null) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + this.input = input; incompatibleWithSpawn = true; } @@ -161,7 +173,11 @@ public class ExecTask extends Task { * @param inputString the string which is used as the input source */ public void setInputString(String inputString) { - redirector.setInputString(inputString); + if (input != null) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + this.inputString = inputString; incompatibleWithSpawn = true; } @@ -185,7 +201,7 @@ public class ExecTask extends Task { * @since ant 1.6 */ public void setError(File error) { - redirector.setError(error); + this.error = error; incompatibleWithSpawn = true; } @@ -325,6 +341,20 @@ public class ExecTask extends Task { } + /** + * Add a RedirectorElement to this task. + * + * @param redirectorElement RedirectorElement. + */ + public void addConfiguredRedirector(RedirectorElement redirectorElement) { + if (this.redirectorElement != null) { + throw new BuildException("cannot have > 1 nested s"); + } else { + this.redirectorElement = redirectorElement; + incompatibleWithSpawn = true; + } + } + /** * Attempt to figure out where the executable is so that we can feed @@ -431,10 +461,23 @@ public class ExecTask extends Task { if (spawn && incompatibleWithSpawn) { getProject().log("spawn does not allow attributes related to input, " + "output, error, result", Project.MSG_ERR); - getProject().log("spawn does not also not allow timeout", Project.MSG_ERR); - throw new BuildException("You have used an attribute which is " - + "not compatible with spawn"); + getProject().log("spawn also does not allow timeout", Project.MSG_ERR); + getProject().log( "finally, spawn is not compatible " + + "with a nested I/O ", Project.MSG_ERR); + throw new BuildException("You have used an attribute " + + "or nested element which is not compatible with spawn"); } + setupRedirector(); + } + + /** + * Set up properties on the redirector that we needed to store locally. + */ + protected void setupRedirector() { + redirector.setInput(input); + redirector.setInputString(inputString); + redirector.setOutput(output); + redirector.setError(error); } /** @@ -485,6 +528,9 @@ public class ExecTask extends Task { if (dir == null) { dir = getProject().getBaseDir(); } + if (redirectorElement != null) { + redirectorElement.configure(redirector); + } Execute exe = new Execute(createHandler(), createWatchdog()); exe.setAntRun(getProject()); exe.setWorkingDirectory(dir); @@ -527,6 +573,7 @@ public class ExecTask extends Task { } } maybeSetResultPropertyValue(returnCode); + redirector.complete(); if (Execute.isFailure(returnCode)) { if (failOnError) { throw new BuildException(getTaskType() + " returned: " @@ -535,7 +582,6 @@ public class ExecTask extends Task { log("Result: " + returnCode, Project.MSG_ERR); } } - redirector.complete(); } else { exe.spawn(); } diff --git a/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java b/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java index 84f4d460c..ac177ae96 100644 --- a/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java +++ b/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2004 The Apache Software Foundation + * Copyright 2000-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -238,6 +238,17 @@ public class ExecuteOn extends ExecTask { } } + protected ExecuteStreamHandler createHandler() throws BuildException { + //if we have a RedirectorElement, return a decoy + return (redirectorElement == null) + ? super.createHandler() : new PumpStreamHandler(); + } + + protected void setupRedirector() { + super.setupRedirector(); + redirector.setAppendProperties(true); + } + protected void runExec(Execute exe) throws BuildException { int totalFiles = 0; int totalDirs = 0; @@ -293,10 +304,17 @@ public class ExecuteOn extends ExecTask { log(Commandline.describeCommand(command), Project.MSG_VERBOSE); exe.setCommandline(command); - if (haveExecuted) { + + if (redirectorElement != null) { + setupRedirector(); + redirectorElement.configure(redirector, s[j]); + } + + if (redirectorElement != null || haveExecuted) { // need to reset the stream handler to restart - // reading of pipes - exe.setStreamHandler(createHandler()); + // reading of pipes; + // go ahead and do it always w/ nested redirectors + exe.setStreamHandler(redirector.createHandler()); } runExecute(exe); haveExecuted = true; @@ -341,10 +359,17 @@ public class ExecuteOn extends ExecTask { log(Commandline.describeCommand(command), Project.MSG_VERBOSE); exe.setCommandline(command); - if (haveExecuted) { + + if (redirectorElement != null) { + setupRedirector(); + redirectorElement.configure(redirector, s[j]); + } + + if (redirectorElement != null || haveExecuted) { // need to reset the stream handler to restart - // reading of pipes - exe.setStreamHandler(createHandler()); + // reading of pipes; + // go ahead and do it always w/ nested redirectors + exe.setStreamHandler(redirector.createHandler()); } runExecute(exe); haveExecuted = true; @@ -373,6 +398,8 @@ public class ExecuteOn extends ExecTask { } finally { // close the output file if required logFlush(); + redirector.setAppendProperties(false); + redirector.setProperties(); } } @@ -582,10 +609,16 @@ public class ExecuteOn extends ExecTask { String[] command = getCommandline(cs, cb); log(Commandline.describeCommand(command), Project.MSG_VERBOSE); exe.setCommandline(command); - if (currentOffset > 0) { + if (redirectorElement != null) { + setupRedirector(); + redirectorElement.configure(redirector, null); + } + + if (redirectorElement != null || currentOffset > 0) { // need to reset the stream handler to restart - // reading of pipes - exe.setStreamHandler(createHandler()); + // reading of pipes; + // go ahead and do it always w/ nested redirectors + exe.setStreamHandler(redirector.createHandler()); } runExecute(exe); diff --git a/src/main/org/apache/tools/ant/taskdefs/Java.java b/src/main/org/apache/tools/ant/taskdefs/Java.java index 3390521c8..fe1a44214 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Java.java +++ b/src/main/org/apache/tools/ant/taskdefs/Java.java @@ -34,6 +34,7 @@ import org.apache.tools.ant.types.PropertySet; import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.types.Assertions; import org.apache.tools.ant.types.Permissions; +import org.apache.tools.ant.types.RedirectorElement; /** * Launcher for Java applications. Allows use of @@ -53,7 +54,16 @@ public class Java extends Task { private File dir = null; private boolean failOnError = false; private Long timeout = null; - private Redirector redirector = new Redirector(this); + + //include locally for screening purposes + private String inputString; + private File input; + private File output; + private File error; + + protected Redirector redirector = new Redirector(this); + protected RedirectorElement redirectorElement; + private String resultProperty; private Permissions perm = null; @@ -110,9 +120,11 @@ public class Java extends Task { if (spawn && incompatibleWithSpawn) { getProject().log("spawn does not allow attributes related to input, " + "output, error, result", Project.MSG_ERR); - getProject().log("spawn does not also not allow timeout", Project.MSG_ERR); - throw new BuildException("You have used an attribute which is " - + "not compatible with spawn"); + getProject().log("spawn also does not allow timeout", Project.MSG_ERR); + getProject().log( "finally, spawn is not compatible " + + "with a nested I/O ", Project.MSG_ERR); + throw new BuildException("You have used an attribute " + + "or nested element which is not compatible with spawn"); } if (cmdl.getAssertions() != null && !fork) { log("Assertion statements are currently ignored in non-forked mode"); @@ -151,6 +163,7 @@ public class Java extends Task { Project.MSG_VERBOSE); } + setupRedirector(); try { if (fork) { if (!spawn) { @@ -419,7 +432,7 @@ public class Java extends Task { * @param out name of the output file */ public void setOutput(File out) { - redirector.setOutput(out); + this.output = out; incompatibleWithSpawn = true; } @@ -429,7 +442,11 @@ public class Java extends Task { * @param input name of the input file */ public void setInput(File input) { - redirector.setInput(input); + if (inputString != null) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + this.input = input; incompatibleWithSpawn = true; } @@ -439,7 +456,11 @@ public class Java extends Task { * @param inputString the string which is used as the input source */ public void setInputString(String inputString) { - redirector.setInputString(inputString); + if (input != null) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + this.inputString = inputString; incompatibleWithSpawn = true; } @@ -464,7 +485,7 @@ public class Java extends Task { * @since ant 1.6 */ public void setError(File error) { - redirector.setError(error); + this.error = error; incompatibleWithSpawn = true; } @@ -572,6 +593,19 @@ public class Java extends Task { cmdl.setAssertions(asserts); } + /** + * Add a RedirectorElement to this task. + * @param redirectorElement RedirectorElement. + */ + public void addConfiguredRedirector(RedirectorElement redirectorElement) { + if (this.redirectorElement != null) { + throw new BuildException("cannot have > 1 nested s"); + } else { + this.redirectorElement = redirectorElement; + incompatibleWithSpawn = true; + } + } + /** * Pass output sent to System.out to specified output file. * @@ -653,6 +687,19 @@ public class Java extends Task { } } + /** + * Set up properties on the redirector that we needed to store locally. + */ + protected void setupRedirector() { + redirector.setInput(input); + redirector.setInputString(inputString); + redirector.setOutput(output); + redirector.setError(error); + if (redirectorElement != null) { + redirectorElement.configure(redirector); + } + } + /** * Executes the given classname with the given arguments as it * was a command line application. diff --git a/src/main/org/apache/tools/ant/taskdefs/Redirector.java b/src/main/org/apache/tools/ant/taskdefs/Redirector.java index 94e0f435b..84939c0d8 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Redirector.java +++ b/src/main/org/apache/tools/ant/taskdefs/Redirector.java @@ -16,23 +16,37 @@ */ package org.apache.tools.ant.taskdefs; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.StringReader; -import java.io.OutputStream; +import java.io.Reader; import java.io.InputStream; +import java.io.IOException; import java.io.PrintStream; -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; +import java.io.OutputStream; +import java.io.StringReader; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.PipedInputStream; +import java.io.InputStreamReader; +import java.io.PipedOutputStream; +import java.io.OutputStreamWriter; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.Vector; import org.apache.tools.ant.Task; -import org.apache.tools.ant.util.LazyFileOutputStream; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.filters.util.ChainReaderHelper; import org.apache.tools.ant.util.StringUtils; import org.apache.tools.ant.util.TeeOutputStream; +import org.apache.tools.ant.util.ReaderInputStream; +import org.apache.tools.ant.util.LeadPipeInputStream; +import org.apache.tools.ant.util.LazyFileOutputStream; +import org.apache.tools.ant.util.OutputStreamFunneler; +import org.apache.tools.ant.util.ConcatFileInputStream; +import org.apache.tools.ant.util.KeepAliveOutputStream; /** * The Redirector class manages the setup and connection of @@ -41,21 +55,43 @@ import org.apache.tools.ant.util.TeeOutputStream; * @since Ant 1.6 */ public class Redirector { + + private static final String defaultEncoding + = System.getProperty("file.encoding"); + + private class PropertyOutputStream extends ByteArrayOutputStream { + String property; + boolean closed = false; + + PropertyOutputStream(String property) { + super(); + this.property = property; + } + + public void close() throws IOException { + if (!closed && !(append && appendProperties)) { + setPropertyFromBAOS(this, property); + closed = true; + } + } + } + /** - * The file receiving standard output. Will also receive standard error - * unless standard error is redirected or logError is true. + * The file(s) from which standard input is being taken. + * If > 1, files' content will be concatenated in the order received. */ - private File out; + private File[] input; /** - * The file to which standard error is being redirected + * The file(s) receiving standard output. Will also receive standard error + * unless standard error is redirected or logError is true. */ - private File error; + private File[] out; /** - * The file from which standard input is being taken. + * The file(s) to which standard error is being redirected */ - private File input; + private File[] error; /** * Indicates if standard error should be logged to Ant's log system @@ -67,12 +103,12 @@ public class Redirector { /** * Buffer used to capture output for storage into a property */ - private ByteArrayOutputStream baos = null; + private PropertyOutputStream baos = null; /** * Buffer used to capture error output for storage into a property */ - private ByteArrayOutputStream errorBaos = null; + private PropertyOutputStream errorBaos = null; /** The name of the property into which output is to be stored */ private String outputProperty; @@ -86,6 +122,9 @@ public class Redirector { /** Flag which indicates if error and output files are to be appended. */ private boolean append = false; + /** Flag which indicates whether files should be created even when empty. */ + private boolean createEmptyFiles = true; + /** The task for which this redirector is working */ private Task managingTask; @@ -104,6 +143,30 @@ public class Redirector { /** Stream which is used for line oriented error output */ private PrintStream errorPrintStream = null; + /** The output filter chains */ + private Vector outputFilterChains; + + /** The error filter chains */ + private Vector errorFilterChains; + + /** The input filter chains */ + private Vector inputFilterChains; + + /** The output encoding */ + private String outputEncoding = defaultEncoding; + + /** The error encoding */ + private String errorEncoding = defaultEncoding; + + /** The input encoding */ + private String inputEncoding = defaultEncoding; + + /** Whether to complete properties settings **/ + private boolean appendProperties = true; + + /** The thread group used for starting StreamPumper threads */ + private ThreadGroup threadGroup = new ThreadGroup("redirector"); + /** * Create a redirector instance for the given task * @@ -119,6 +182,15 @@ public class Redirector { * @param input the file from which input is read. */ public void setInput(File input) { + setInput((input == null) ? null : new File[] {input}); + } + + /** + * Set the input to use for the task + * + * @param input the files from which input is read. + */ + public synchronized void setInput(File[] input) { this.input = input; } @@ -127,7 +199,7 @@ public class Redirector { * * @param inputString the string which is used as the input source */ - public void setInputString(String inputString) { + public synchronized void setInputString(String inputString) { this.inputString = inputString; } @@ -139,9 +211,61 @@ public class Redirector { * @param out the file to which output stream is written */ public void setOutput(File out) { + setOutput((out == null) ? null : new File[] {out}); + } + + /** + * Files the output of the process is redirected to. If error is not + * redirected, it too will appear in the output + * + * @param out the files to which output stream is written + */ + public synchronized void setOutput(File[] out) { this.out = out; } + /** + * Set the output encoding. + * + * @param outputEncoding String. + */ + public synchronized void setOutputEncoding(String outputEncoding) { + if (outputEncoding == null) { + throw new IllegalArgumentException( + "outputEncoding must not be null"); + } else { + this.outputEncoding = outputEncoding; + } + } + + /** + * Set the error encoding. + * + * @param errorEncoding String. + */ + public synchronized void setErrorEncoding(String errorEncoding) { + if (errorEncoding == null) { + throw new IllegalArgumentException( + "errorEncoding must not be null"); + } else { + this.errorEncoding = errorEncoding; + } + } + + /** + * Set the input encoding. + * + * @param inputEncoding String. + */ + public synchronized void setInputEncoding(String inputEncoding) { + if (inputEncoding == null) { + throw new IllegalArgumentException( + "inputEncoding must not be null"); + } else { + this.inputEncoding = inputEncoding; + } + } + /** * Controls whether error output of exec is logged. This is only useful * when output is being redirected and error output is desired in the @@ -150,16 +274,36 @@ public class Redirector { * @param logError if true the standard error is sent to the Ant log system * and not sent to output. */ - public void setLogError(boolean logError) { + public synchronized void setLogError(boolean logError) { this.logError = logError; } + /** + * This Redirector's subordinate + * PropertyOutputStreams will not set their respective + * properties while (appendProperties && append). + * + * @param appendProperties whether to append properties. + */ + public synchronized void setAppendProperties(boolean appendProperties) { + this.appendProperties = appendProperties; + } + /** * Set the file to which standard error is to be redirected. * * @param error the file to which error is to be written */ public void setError(File error) { + setError((error == null) ? null : new File[] {error}); + } + + /** + * Set the files to which standard error is to be redirected. + * + * @param error the file to which error is to be written + */ + public synchronized void setError(File[] error) { this.error = error; } @@ -170,8 +314,12 @@ public class Redirector { * @param outputProperty the name of the property to be set with the * task's output. */ - public void setOutputProperty(String outputProperty) { - this.outputProperty = outputProperty; + public synchronized void setOutputProperty(String outputProperty) { + if (outputProperty == null + || !(outputProperty.equals(this.outputProperty))) { + this.outputProperty = outputProperty; + baos = null; + } } /** @@ -181,10 +329,19 @@ public class Redirector { * @param append if true output and error streams are appended to their * respective files, if specified. */ - public void setAppend(boolean append) { + public synchronized void setAppend(boolean append) { this.append = append; } + /** + * Whether output and error files should be created even when empty. + * Defaults to true. + * @param createEmptyFiles boolean. + */ + public void setCreateEmptyFiles(boolean createEmptyFiles) { + this.createEmptyFiles = createEmptyFiles; + } + /** * Property name whose value should be set to the error of * the process. @@ -192,8 +349,39 @@ public class Redirector { * @param errorProperty the name of the property to be set * with the error output. */ - public void setErrorProperty(String errorProperty) { - this.errorProperty = errorProperty; + public synchronized void setErrorProperty(String errorProperty) { + if (errorProperty == null + || !(errorProperty.equals(this.errorProperty))) { + this.errorProperty = errorProperty; + errorBaos = null; + } + } + + /** + * Set the input FilterChains. + * + * @param Vector containing FilterChain. + */ + public synchronized void setInputFilterChains(Vector inputFilterChains) { + this.inputFilterChains = inputFilterChains; + } + + /** + * Set the output FilterChains. + * + * @param Vector containing FilterChain. + */ + public void setOutputFilterChains(Vector outputFilterChains) { + this.outputFilterChains = outputFilterChains; + } + + /** + * Set the error FilterChains. + * + * @param Vector containing FilterChain. + */ + public void setErrorFilterChains(Vector errorFilterChains) { + this.errorFilterChains = errorFilterChains; } /** @@ -207,8 +395,8 @@ public class Redirector { private void setPropertyFromBAOS(ByteArrayOutputStream baos, String propertyName) throws IOException { - BufferedReader in = - new BufferedReader(new StringReader(Execute.toString(baos))); + BufferedReader in + = new BufferedReader(new StringReader(Execute.toString(baos))); String line = null; StringBuffer val = new StringBuffer(); while ((line = in.readLine()) != null) { @@ -220,30 +408,34 @@ public class Redirector { managingTask.getProject().setNewProperty(propertyName, val.toString()); } - /** * Create the input, error and output streams based on the * configuration options. */ - public void createStreams() { - if (out == null && outputProperty == null) { + public synchronized void createStreams() { + if ((out == null || out.length == 0) && outputProperty == null) { outputStream = new LogOutputStream(managingTask, Project.MSG_INFO); - errorStream = new LogOutputStream(managingTask, Project.MSG_WARN); } else { - if (out != null) { - outputStream = new LazyFileOutputStream(out, append, true); - managingTask.log("Output redirected to " + out, - Project.MSG_VERBOSE); + if (out != null && out.length > 0) { + String logHead = new StringBuffer("Output ").append( + ((append) ? "appended" : "redirected")).append( + " to ").toString(); + outputStream = foldFiles(out, logHead, Project.MSG_VERBOSE); } if (outputProperty != null) { - baos = new ByteArrayOutputStream(); - managingTask.log("Output redirected to property: " - + outputProperty, Project.MSG_VERBOSE); - if (out == null) { - outputStream = baos; + if (baos == null) { + baos = new PropertyOutputStream(outputProperty); + managingTask.log("Output redirected to property: " + + outputProperty, Project.MSG_VERBOSE); + } + //shield it from being closed by a filtering StreamPumper + OutputStream keepAliveOutput = new KeepAliveOutputStream(baos); + if (outputStream == null) { + outputStream = keepAliveOutput; } else { - outputStream = new TeeOutputStream(outputStream, baos); + outputStream + = new TeeOutputStream(outputStream, keepAliveOutput); } } else { baos = null; @@ -252,44 +444,132 @@ public class Redirector { errorStream = outputStream; } - if (logError) { - errorStream = new LogOutputStream(managingTask, Project.MSG_WARN); - } + if (error != null && error.length > 0) { + String logHead = new StringBuffer("Error ").append( + ((append) ? "appended" : "redirected")).append( + " to ").toString(); + errorStream = foldFiles(error, logHead, Project.MSG_VERBOSE); - if (error != null) { - errorStream = new LazyFileOutputStream(error, append, true); - managingTask.log("Error redirected to " + error, - Project.MSG_VERBOSE); + } else if (logError || errorStream == null) { + errorStream = new LogOutputStream(managingTask, Project.MSG_WARN); + } else { //must be errorStream == outputStream + long funnelTimeout = 0L; + OutputStreamFunneler funneler + = new OutputStreamFunneler(outputStream, funnelTimeout); + try { + outputStream = funneler.getFunnelInstance(); + errorStream = funneler.getFunnelInstance(); + } catch (IOException eyeOhEx) { + throw new BuildException( + "error splitting output/error streams", eyeOhEx); + } } if (errorProperty != null) { - errorBaos = new ByteArrayOutputStream(); - managingTask.log("Error redirected to property: " + errorProperty, - Project.MSG_VERBOSE); - if (error == null) { - errorStream = errorBaos; - } else { - errorStream = new TeeOutputStream(errorStream, errorBaos); + if (errorBaos == null) { + errorBaos = new PropertyOutputStream(errorProperty); + managingTask.log("Error redirected to property: " + errorProperty, + Project.MSG_VERBOSE); } + //shield it from being closed by a filtering StreamPumper + OutputStream keepAliveError = new KeepAliveOutputStream(errorBaos); + errorStream = (error == null || error.length == 0) ? keepAliveError + : new TeeOutputStream(errorStream, keepAliveError); } else { errorBaos = null; } - if (input != null && inputString != null) { - throw new BuildException("The \"input\" and \"inputstring\" " - + "attributes cannot both be specified"); + if ((outputFilterChains != null && outputFilterChains.size() > 0) + || !(outputEncoding.equalsIgnoreCase(inputEncoding))) { + try { + LeadPipeInputStream snk = new LeadPipeInputStream(); + snk.setManagingTask(managingTask); + + InputStream outPumpIn = snk; + + Reader reader = new InputStreamReader(outPumpIn, inputEncoding); + + if (outputFilterChains != null && outputFilterChains.size() > 0) { + ChainReaderHelper helper = new ChainReaderHelper(); + helper.setPrimaryReader(reader); + helper.setFilterChains(outputFilterChains); + reader = helper.getAssembledReader(); + } + outPumpIn = new ReaderInputStream(reader, outputEncoding); + + Thread t = new Thread(threadGroup, new StreamPumper( + outPumpIn, outputStream, true), "output pumper"); + t.setPriority(Thread.MAX_PRIORITY); + outputStream = new PipedOutputStream(snk); + t.start(); + } catch (IOException eyeOhEx) { + throw new BuildException( + "error setting up output stream", eyeOhEx); + } } - if (input != null) { + + if ((errorFilterChains != null && errorFilterChains.size() > 0) + || !(errorEncoding.equalsIgnoreCase(inputEncoding))) { try { - inputStream = new FileInputStream(input); - } catch (FileNotFoundException fne) { - throw new BuildException("Cannot read from " + input, fne); + LeadPipeInputStream snk = new LeadPipeInputStream(); + snk.setManagingTask(managingTask); + + InputStream errPumpIn = snk; + + Reader reader = new InputStreamReader(errPumpIn, inputEncoding); + + if (errorFilterChains != null && errorFilterChains.size() > 0) { + ChainReaderHelper helper = new ChainReaderHelper(); + helper.setPrimaryReader(reader); + helper.setFilterChains(errorFilterChains); + reader = helper.getAssembledReader(); + } + errPumpIn = new ReaderInputStream(reader, errorEncoding); + + Thread t = new Thread(threadGroup, new StreamPumper( + errPumpIn, errorStream, true), "error pumper"); + t.setPriority(Thread.MAX_PRIORITY); + errorStream = new PipedOutputStream(snk); + t.start(); + } catch (IOException eyeOhEx) { + throw new BuildException( + "error setting up error stream", eyeOhEx); } + } + + // if input files are specified, inputString is ignored; + // classes that work with redirector attributes can enforce + // whatever warnings are needed + if (input != null && input.length > 0) { + managingTask.log("Redirecting input from file" + + ((input.length == 1) ? "" : "s"), Project.MSG_VERBOSE); + try { + inputStream = new ConcatFileInputStream(input); + } catch (IOException eyeOhEx) { + throw new BuildException(eyeOhEx); + } + ((ConcatFileInputStream)inputStream).setManagingTask(managingTask); } else if (inputString != null) { + managingTask.log("Using input \"" + inputString + "\"", + Project.MSG_VERBOSE); inputStream = new ByteArrayInputStream(inputString.getBytes()); } - } + if (inputStream != null + && inputFilterChains != null && inputFilterChains.size() > 0) { + ChainReaderHelper helper = new ChainReaderHelper(); + try { + helper.setPrimaryReader( + new InputStreamReader(inputStream, inputEncoding)); + } catch (IOException eyeOhEx) { + throw new BuildException( + "error setting up input stream", eyeOhEx); + } + helper.setFilterChains(inputFilterChains); + inputStream = new ReaderInputStream( + helper.getAssembledReader(), inputEncoding); + } + } /** * Create the StreamHandler to use with our Execute instance. @@ -299,7 +579,8 @@ public class Redirector { * * @throws BuildException if the execute stream handler cannot be created. */ - public ExecuteStreamHandler createHandler() throws BuildException { + public synchronized ExecuteStreamHandler createHandler() + throws BuildException { createStreams(); return new PumpStreamHandler(outputStream, errorStream, inputStream); } @@ -309,7 +590,7 @@ public class Redirector { * * @param output the data to be output */ - protected void handleOutput(String output) { + protected synchronized void handleOutput(String output) { if (outPrintStream == null) { outPrintStream = new PrintStream(outputStream); } @@ -327,8 +608,8 @@ public class Redirector { * * @exception IOException if the data cannot be read */ - protected int handleInput(byte[] buffer, int offset, int length) - throws IOException { + protected synchronized int handleInput(byte[] buffer, int offset, + int length) throws IOException { if (inputStream == null) { return managingTask.getProject().defaultInput(buffer, offset, length); @@ -342,7 +623,7 @@ public class Redirector { * * @param output the data being flushed. */ - protected void handleFlush(String output) { + protected synchronized void handleFlush(String output) { if (outPrintStream == null) { outPrintStream = new PrintStream(outputStream); } @@ -355,7 +636,7 @@ public class Redirector { * * @param output the error output data. */ - protected void handleErrorOutput(String output) { + protected synchronized void handleErrorOutput(String output) { if (errorPrintStream == null) { errorPrintStream = new PrintStream(errorStream); } @@ -367,7 +648,7 @@ public class Redirector { * * @param output the error information being flushed. */ - protected void handleErrorFlush(String output) { + protected synchronized void handleErrorFlush(String output) { if (errorPrintStream == null) { errorPrintStream = new PrintStream(errorStream); } @@ -380,7 +661,7 @@ public class Redirector { * @return the redirector's output stream or null if no output * has been configured */ - public OutputStream getOutputStream() { + public synchronized OutputStream getOutputStream() { return outputStream; } @@ -390,7 +671,7 @@ public class Redirector { * @return the redirector's error stream or null if no output * has been configured */ - public OutputStream getErrorStream() { + public synchronized OutputStream getErrorStream() { return errorStream; } @@ -400,7 +681,7 @@ public class Redirector { * @return the redirector's input stream or null if no output * has been configured */ - public InputStream getInputStream() { + public synchronized InputStream getInputStream() { return inputStream; } @@ -413,7 +694,7 @@ public class Redirector { * @throws IOException if the output properties cannot be read from their * output streams. */ - public void complete() throws IOException { + public synchronized void complete() throws IOException { System.out.flush(); System.err.flush(); @@ -421,17 +702,69 @@ public class Redirector { inputStream.close(); } + outputStream.flush(); outputStream.close(); - if (errorStream != outputStream) { - errorStream.close(); + errorStream.flush(); + errorStream.close(); + + //wait for the StreamPumpers to finish + while (threadGroup.activeCount() > 0) { + try { + managingTask.log("waiting for " + threadGroup.activeCount() + + " Threads:", Project.MSG_DEBUG); + Thread[] thread = new Thread[threadGroup.activeCount()]; + threadGroup.enumerate(thread); + for (int i = 0; i < thread.length && thread[i] != null; i++) { + try { + managingTask.log(thread[i].toString(), Project.MSG_DEBUG); + } catch (NullPointerException enPeaEx) { + } + } + Thread.sleep(1000); + } catch (InterruptedException eyeEx) { + } } + setProperties(); + + inputStream = null; + outputStream = errorStream = outPrintStream = errorPrintStream = null; + } + + /** + * Notify the Redirector that it is now okay + * to set any output and/or error properties. + */ + public synchronized void setProperties() { if (baos != null) { - setPropertyFromBAOS(baos, outputProperty); + try { + baos.close(); + } catch (IOException eyeOhEx) { + } } if (errorBaos != null) { - setPropertyFromBAOS(errorBaos, errorProperty); + try { + errorBaos.close(); + } catch (IOException eyeOhEx) { + } + } + } + + private OutputStream foldFiles(File[] file, String logHead, int loglevel) { + OutputStream result + = new LazyFileOutputStream(file[0], append, createEmptyFiles); + + managingTask.log(logHead + file[0], loglevel); + char[] c = new char[logHead.length()]; + Arrays.fill(c, ' '); + String indent = new String(c); + + for (int i = 1; i < file.length ; i++) { + outputStream = new TeeOutputStream(outputStream, + new LazyFileOutputStream(file[i], append, createEmptyFiles)); + managingTask.log(indent + file[i], loglevel); } + return result; } } diff --git a/src/main/org/apache/tools/ant/types/RedirectorElement.java b/src/main/org/apache/tools/ant/types/RedirectorElement.java new file mode 100755 index 000000000..267c71aa3 --- /dev/null +++ b/src/main/org/apache/tools/ant/types/RedirectorElement.java @@ -0,0 +1,511 @@ +/* + * Copyright 2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.types; + +import java.io.File; +import java.util.Vector; +import java.util.ArrayList; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.Redirector; +import org.apache.tools.ant.types.DataType; + +/** + * Element representation of a Redirector. + */ +public class RedirectorElement extends DataType { + + /** + * Whether the input mapper was set via setOutput. + */ + private boolean usingInput = false; + + /** + * Whether the output mapper was set via setOutput. + */ + private boolean usingOutput = false; + + /** + * Whether the error mapper was set via setError. + */ + private boolean usingError = false; + + /** + * Indicates if standard error should be logged to Ant's log system + * rather than the output. This has no effect if standard error is + * redirected to a file or property. + */ + private Boolean logError; + + /** The name of the property into which output is to be stored */ + private String outputProperty; + + /** The name of the property into which error output is to be stored */ + private String errorProperty; + + /** String from which input is taken */ + private String inputString; + + /** Flag which indicates if error and output files are to be appended. */ + private Boolean append; + + /** Flag which indicates whether files should be created even if empty. */ + private Boolean createEmptyFiles; + + /** Input file mapper. */ + private Mapper inputMapper; + + /** Output file mapper. */ + private Mapper outputMapper; + + /** Error file mapper. */ + private Mapper errorMapper; + + /** input filter chains. */ + private Vector inputFilterChains = new Vector(); + + /** output filter chains. */ + private Vector outputFilterChains = new Vector(); + + /** error filter chains. */ + private Vector errorFilterChains = new Vector(); + + /** The output encoding */ + private String outputEncoding; + + /** The error encoding */ + private String errorEncoding; + + /** The input encoding */ + private String inputEncoding; + + /** + * Add the input file mapper. + * @param inputMapper Mapper. + */ + public void addConfiguredInputMapper(Mapper inputMapper) { + if (isReference()) { + throw noChildrenAllowed(); + } + if (this.inputMapper != null) { + if (usingInput) { + throw new BuildException("attribute \"input\"" + + " cannot coexist with a nested "); + } else { + throw new BuildException("Cannot have > 1 "); + } + } + this.inputMapper = inputMapper; + } + + /** + * Add the output file mapper. + * @param outputMapper Mapper. + */ + public void addConfiguredOutputMapper(Mapper outputMapper) { + if (isReference()) { + throw noChildrenAllowed(); + } + if (this.outputMapper != null) { + if (usingOutput) { + throw new BuildException("attribute \"output\"" + + " cannot coexist with a nested "); + } else { + throw new BuildException("Cannot have > 1 "); + } + } + this.outputMapper = outputMapper; + } + + /** + * Add the error file mapper. + * @param errorMapper Mapper. + */ + public void addConfiguredErrorMapper(Mapper errorMapper) { + if (isReference()) { + throw noChildrenAllowed(); + } + if (this.errorMapper != null) { + if (usingError) { + throw new BuildException("attribute \"error\"" + + " cannot coexist with a nested "); + } else { + throw new BuildException("Cannot have > 1 "); + } + } + this.errorMapper = errorMapper; + } + + /** + * Makes this instance in effect a reference to another instance. + * + *

You must not set another attribute or nest elements inside + * this element if you make it a reference.

+ */ + public void setRefid(Reference r) throws BuildException { + if (usingInput + || usingOutput + || usingError + || inputString != null + || logError != null + || append != null + || outputProperty != null + || errorProperty != null) { + throw tooManyAttributes(); + } + super.setRefid(r); + } + + /** + * Set the input to use for the task + * @param input the file from which input is read. + */ + public void setInput(File input) { + if (isReference()) { + throw tooManyAttributes(); + } + if (inputString != null) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + usingInput = true; + inputMapper = createMergeMapper(input); + } + + /** + * Set the string to use as input + * @param inputString the string which is used as the input source + */ + public void setInputString(String inputString) { + if (isReference()) { + throw tooManyAttributes(); + } + if (usingInput) { + throw new BuildException("The \"input\" and \"inputstring\" " + + "attributes cannot both be specified"); + } + this.inputString = inputString; + } + + + /** + * File the output of the process is redirected to. If error is not + * redirected, it too will appear in the output + * + * @param out the file to which output stream is written + */ + public void setOutput(File out) { + if (isReference()) { + throw tooManyAttributes(); + } + if (out == null) { + throw new IllegalArgumentException("output file specified as null"); + } + usingOutput = true; + outputMapper = createMergeMapper(out); + } + + /** + * Set the output encoding. + * @param outputEncoding String. + */ + public void setOutputEncoding(String outputEncoding) { + if (isReference()) { + throw tooManyAttributes(); + } + this.outputEncoding = outputEncoding; + } + + /** + * Set the error encoding. + * + * @param errorEncoding String. + */ + public void setErrorEncoding(String errorEncoding) { + if (isReference()) { + throw tooManyAttributes(); + } + this.errorEncoding = errorEncoding; + } + + /** + * Set the input encoding. + * @param inputEncoding String. + */ + public void setInputEncoding(String inputEncoding) { + if (isReference()) { + throw tooManyAttributes(); + } + this.inputEncoding = inputEncoding; + } + + /** + * Controls whether error output of exec is logged. This is only useful + * when output is being redirected and error output is desired in the + * Ant log + * @param logError if true the standard error is sent to the Ant log system + * and not sent to output. + */ + public void setLogError(boolean logError) { + if (isReference()) { + throw tooManyAttributes(); + } + //pre JDK 1.4 compatible + this.logError = ((logError) ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Set the file to which standard error is to be redirected. + * @param error the file to which error is to be written + */ + public void setError(File error) { + if (isReference()) { + throw tooManyAttributes(); + } + if (error == null) { + throw new IllegalArgumentException("error file specified as null"); + } + usingError = true; + errorMapper = createMergeMapper(error); + } + + /** + * Property name whose value should be set to the output of + * the process. + * @param outputProperty the name of the property to be set with the + * task's output. + */ + public void setOutputProperty(String outputProperty) { + if (isReference()) { + throw tooManyAttributes(); + } + this.outputProperty = outputProperty; + } + + /** + * Whether output should be appended to or overwrite an existing file. + * Defaults to false. + * @param append if true output and error streams are appended to their + * respective files, if specified. + */ + public void setAppend(boolean append) { + if (isReference()) { + throw tooManyAttributes(); + } + //pre JDK 1.4 compatible + this.append = ((append) ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Whether output and error files should be created even when empty. + * Defaults to true. + * @param createEmptyFiles boolean. + */ + public void setCreateEmptyFiles(boolean createEmptyFiles) { + if (isReference()) { + throw tooManyAttributes(); + } + //pre JDK 1.4 compatible + this.createEmptyFiles = ((createEmptyFiles) + ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Property name whose value should be set to the error of + * the process. + * @param errorProperty the name of the property to be set + * with the error output. + */ + public void setErrorProperty(String errorProperty) { + if (isReference()) { + throw tooManyAttributes(); + } + this.errorProperty = errorProperty; + } + + /** + * Create a nested input FilterChain. + * @return FilterChain. + */ + public FilterChain createInputFilterChain() { + if (isReference()) { + throw noChildrenAllowed(); + } + FilterChain result = new FilterChain(); + result.setProject(getProject()); + inputFilterChains.add(result); + return result; + } + + /** + * Create a nested output FilterChain. + * @return FilterChain. + */ + public FilterChain createOutputFilterChain() { + if (isReference()) { + throw noChildrenAllowed(); + } + FilterChain result = new FilterChain(); + result.setProject(getProject()); + outputFilterChains.add(result); + return result; + } + + /** + * Create a nested error FilterChain. + * @return FilterChain. + */ + public FilterChain createErrorFilterChain() { + if (isReference()) { + throw noChildrenAllowed(); + } + FilterChain result = new FilterChain(); + result.setProject(getProject()); + errorFilterChains.add(result); + return result; + } + + /** + * Configure the specified Redirector. + * @param redirector Redirector. + */ + public void configure(Redirector redirector) { + configure(redirector, null); + } + + /** + * Configure the specified Redirector + * for the specified sourcefile. + * @param redirector Redirector. + * @param sourcefile String. + */ + public void configure(Redirector redirector, String sourcefile) { + if (logError != null) { + redirector.setLogError(logError.booleanValue()); + } + if (append != null) { + redirector.setAppend(append.booleanValue()); + } + if (createEmptyFiles != null) { + redirector.setCreateEmptyFiles(createEmptyFiles.booleanValue()); + } + if (outputProperty != null) { + redirector.setOutputProperty(outputProperty); + } + if (errorProperty != null) { + redirector.setErrorProperty(errorProperty); + } + if (inputString != null) { + redirector.setInputString(inputString); + } + if (inputMapper != null) { + String[] inputTargets = null; + try { + inputTargets = + inputMapper.getImplementation().mapFileName(sourcefile); + } catch (NullPointerException enPeaEx) { + if (sourcefile != null) { + throw enPeaEx; + } + } + if (inputTargets != null && inputTargets.length > 0) { + redirector.setInput(toFileArray(inputTargets)); + } + } + if (outputMapper != null) { + String[] outputTargets = null; + try { + outputTargets = + outputMapper.getImplementation().mapFileName(sourcefile); + } catch (NullPointerException enPeaEx) { + if (sourcefile != null) { + throw enPeaEx; + } + } + if (outputTargets != null && outputTargets.length > 0) { + redirector.setOutput(toFileArray(outputTargets)); + } + } + if (errorMapper != null) { + String[] errorTargets = null; + try { + errorTargets = + errorMapper.getImplementation().mapFileName(sourcefile); + } catch (NullPointerException enPeaEx) { + if (sourcefile != null) { + throw enPeaEx; + } + } + if (errorTargets != null && errorTargets.length > 0) { + redirector.setError(toFileArray(errorTargets)); + } + } + if (inputFilterChains.size() > 0) { + redirector.setInputFilterChains(inputFilterChains); + } + if (outputFilterChains.size() > 0) { + redirector.setOutputFilterChains(outputFilterChains); + } + if (errorFilterChains.size() > 0) { + redirector.setErrorFilterChains(errorFilterChains); + } + if (inputEncoding != null) { + redirector.setInputEncoding(inputEncoding); + } + if (outputEncoding != null) { + redirector.setOutputEncoding(outputEncoding); + } + if (errorEncoding != null) { + redirector.setErrorEncoding(errorEncoding); + } + } + + /** + * Create a merge mapper pointing to the specified destination file. + * @param destfile File + * @return Mapper. + */ + protected Mapper createMergeMapper(File destfile) { + Mapper result = new Mapper(getProject()); + result.setClassname( + org.apache.tools.ant.util.MergingMapper.class.getName()); + result.setTo(destfile.getAbsolutePath()); + return result; + } + + /** + * Return a File[] from the specified set of filenames. + * @param name String[] + * @return File[]. + */ + protected File[] toFileArray(String[] name) { + if (name == null) { + return null; + } + //remove any null elements + ArrayList list = new ArrayList(name.length); + for (int i = 0; i < name.length; i++) { + if (name[i] != null) { + list.add(getProject().resolveFile(name[i])); + } + } + return (File[])(list.toArray(new File[list.size()])); + } + +} diff --git a/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java b/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java new file mode 100755 index 000000000..b65a40ff1 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java @@ -0,0 +1,120 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.util; + +import java.io.File; +import java.io.InputStream; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.FileInputStream; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Project; + +/** + * Special InputStream that will + * concatenate the contents of an array of files. + */ +public class ConcatFileInputStream extends InputStream { + + private static final int EOF = -1; + private int currentIndex = 0; + private boolean eof = false; + private File[] file; + private InputStream currentStream; + private Task managingTask; + + /** + * Construct a new ConcatFileInputStream + * with the specified File[]. + * @param file File[]. + * @throws IOException if I/O errors occur. + */ + public ConcatFileInputStream(File[] file) throws IOException { + this.file = file; + openFile(currentIndex); + } + + // inherit doc + public void close() throws IOException { + closeCurrent(); + eof = true; + } + + // inherit doc + public int read() throws IOException { + int result = readCurrent(); + if (result == EOF && !eof) { + openFile(++currentIndex); + result = readCurrent(); + } + return result; + } + + /** + * Set a managing Task for + * this ConcatFileInputStream. + * @param task the managing Task. + */ + public void setManagingTask(Task task) { + this.managingTask = task; + } + + /** + * Log a message with the specified logging level. + * @param message the String message. + * @param loglevel the int logging level. + */ + public void log(String message, int loglevel) { + if (managingTask != null) { + managingTask.log(message, loglevel); + } else { + if (loglevel > Project.MSG_WARN) { + System.out.println(message); + } else { + System.err.println(message); + } + } + } + + private int readCurrent() throws IOException { + return (eof || currentStream == null) ? EOF : currentStream.read(); + } + + private void openFile(int index) throws IOException { + closeCurrent(); + if (file != null && index < file.length) { + log("Opening " + file[index], Project.MSG_VERBOSE); + currentStream = new BufferedInputStream( + new FileInputStream(file[index])); + } else { + eof = true; + } + } + + private void closeCurrent() { + if (currentStream != null) { + try { + currentStream.close(); + } catch (IOException eyeOhEx) { + } + currentStream = null; + } + } +} + diff --git a/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java b/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java new file mode 100755 index 000000000..6e8bad3d5 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java @@ -0,0 +1,95 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.util; + +import java.io.IOException; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import org.apache.tools.ant.Task; +import org.apache.tools.ant.Project; + +/** + * Special PipedInputStream that will not die + * when the writing Thread is no longer alive. + */ +public class LeadPipeInputStream extends PipedInputStream { + private Task managingTask; + + /** + * Construct a new LeadPipeInputStream. + */ + public LeadPipeInputStream() { + super(); + } + + /** + * Construct a new LeadPipeInputStream to pull + * from the specified PipedOutputStream. + * @param src the PipedOutputStream source. + */ + public LeadPipeInputStream(PipedOutputStream src) throws IOException { + super(src); + } + + //inherit doc + public synchronized int read() throws IOException { + int result = -1; + try { + result = super.read(); + } catch (IOException eyeOhEx) { + if ("write end dead".equalsIgnoreCase(eyeOhEx.getMessage())) { + if (super.in > 0 && super.out < super.buffer.length + && super.out > super.in) { + result = super.buffer[super.out++] & 0xFF; + } + } else { + log("error at LeadPipeInputStream.read(): " + + eyeOhEx.getMessage(), Project.MSG_INFO); + } + } + return result; + } + + /** + * Set a managing Task for + * this LeadPipeInputStream. + * @param task the managing Task. + */ + public void setManagingTask(Task task) { + this.managingTask = task; + } + + /** + * Log a message with the specified logging level. + * @param message the String message. + * @param loglevel the int logging level. + */ + public void log(String message, int loglevel) { + if (managingTask != null) { + managingTask.log(message, loglevel); + } else { + if (loglevel > Project.MSG_WARN) { + System.out.println(message); + } else { + System.err.println(message); + } + } + } +} + diff --git a/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java b/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java new file mode 100755 index 000000000..9e3f6c739 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java @@ -0,0 +1,175 @@ +/* + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.FilterOutputStream; + +/** + * Manages a set of OutputStreams to + * write to a single underlying stream, which is + * closed only when the last "funnel" + * has been closed. + */ +public class OutputStreamFunneler { + + /** + * Default timeout. + * @see #setTimeout() + */ + public static final long DEFAULT_TIMEOUT_MILLIS = 1000; + + private class Funnel extends OutputStream { + private boolean closed = false; + + private Funnel() { + synchronized (OutputStreamFunneler.this) { + ++count; + } + } + + public void flush() throws IOException { + synchronized (OutputStreamFunneler.this) { + dieIfClosed(); + out.flush(); + } + } + + public void write(int b) throws IOException { + synchronized (OutputStreamFunneler.this) { + dieIfClosed(); + out.write(b); + } + } + + public void write(byte[] b) throws IOException { + synchronized (OutputStreamFunneler.this) { + dieIfClosed(); + out.write(b); + } + } + + public void write(byte[] b, int off, int len) throws IOException { + synchronized (OutputStreamFunneler.this) { + dieIfClosed(); + out.write(b, off, len); + } + } + + public void close() throws IOException { + release(this); + } + } + + private OutputStream out; + private int count = 0; + private boolean closed; + private long timeoutMillis; + + /** + * Create a new OutputStreamFunneler for + * the specified OutputStream. + * @param out OutputStream. + */ + public OutputStreamFunneler(OutputStream out) { + this(out, DEFAULT_TIMEOUT_MILLIS); + } + + /** + * Create a new OutputStreamFunneler for + * the specified OutputStream, with the + * specified timeout value. + * @param out OutputStream. + * @param timeoutMillis long. + * @see #setTimeout() + */ + public OutputStreamFunneler(OutputStream out, long timeoutMillis) { + if (out == null) { + throw new IllegalArgumentException( + "OutputStreamFunneler.: out == null"); + } + this.out = out; + this.closed = false; //as far as we know + setTimeout(timeoutMillis); + } + + /** + * Set the timeout for this OutputStreamFunneler. + * This is the maximum time that may elapse between the closure + * of the last "funnel" and the next call to + * getOutputStream() without closing the + * underlying stream. + * @param timeoutMillis long timeout value. + */ + public synchronized void setTimeout(long timeoutMillis) { + this.timeoutMillis = timeoutMillis; + } + + /** + * Get a "funnel" OutputStream instance to + * write to this OutputStreamFunneler's underlying + * OutputStream. + * @return OutputStream. + */ + public synchronized OutputStream getFunnelInstance() + throws IOException { + dieIfClosed(); + try { + return new Funnel(); + } finally { + notifyAll(); + } + } + + private synchronized void release(Funnel funnel) throws IOException { + //ignore release of an already-closed funnel + if (!funnel.closed) { + try { + if (timeoutMillis > 0) { + try { + wait(timeoutMillis); + } catch (InterruptedException eyeEx) { + //ignore + } + } + if (--count == 0) { + close(); + } + } finally { + funnel.closed = true; + } + } + } + + private synchronized void close() throws IOException { + try { + dieIfClosed(); + out.close(); + } finally { + closed = true; + } + } + + private synchronized void dieIfClosed() throws IOException { + if (closed) { + throw new IOException("The funneled OutputStream has been closed."); + } + } + +} diff --git a/src/main/org/apache/tools/ant/util/ReaderInputStream.java b/src/main/org/apache/tools/ant/util/ReaderInputStream.java new file mode 100755 index 000000000..913a1a2c7 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/ReaderInputStream.java @@ -0,0 +1,203 @@ +/* + * Copyright 2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.util; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +/** + * Adapts a Reader as an InputStream. + * Adapted from StringInputStream. + * + */ +public class ReaderInputStream extends InputStream { + + /** Source Reader */ + private Reader in; + + private String encoding = System.getProperty("file.encoding"); + + private byte[] slack; + + private int begin; + + /** + * Construct a ReaderInputStream + * for the specified Reader. + * + * @param reader Reader. Must not be null. + */ + public ReaderInputStream(Reader reader) { + in = reader; + } + + /** + * Construct a ReaderInputStream + * for the specified Reader, + * with the specified encoding. + * + * @param reader non-null Reader. + * @param encoding non-null String encoding. + */ + public ReaderInputStream(Reader reader, String encoding) { + this(reader); + if (encoding == null) { + throw new IllegalArgumentException("encoding must not be null"); + } else { + this.encoding = encoding; + } + } + + /** + * Reads from the Reader, returning the same value. + * + * @return the value of the next character in the Reader. + * + * @exception IOException if the original Reader fails to be read + */ + public synchronized int read() throws IOException { + if (in == null) { + throw new IOException("Stream Closed"); + } + + byte result; + if (slack != null && begin < slack.length) { + result = slack[begin]; + if (++begin == slack.length) { + slack = null; + } + } else { + byte[] buf = new byte[1]; + if (read(buf, 0, 1) <= 0) { + result = -1; + } + result = buf[0]; + } + + if (result < -1) { + result+= 256; + } + + return result; + } + + /** + * Reads from the Reader into a byte array + * + * @param b the byte array to read into + * @param off the offset in the byte array + * @param len the length in the byte array to fill + * @return the actual number read into the byte array, -1 at + * the end of the stream + * @exception IOException if an error occurs + */ + public synchronized int read(byte[] b, int off, int len) + throws IOException { + if (in == null) { + throw new IOException("Stream Closed"); + } + + while (slack == null) { + char[] buf = new char[len]; // might read too much + int n = in.read(buf); + if (n == -1) { + return -1; + } + if (n > 0) { + slack = new String(buf, 0, n).getBytes(encoding); + begin = 0; + } + } + + if (len > slack.length - begin) { + len = slack.length - begin; + } + + System.arraycopy(slack, begin, b, off, len); + + if ((begin+= len) >= slack.length) { + slack = null; + } + + return len; + } + + /** + * Marks the read limit of the StringReader. + * + * @param limit the maximum limit of bytes that can be read before the + * mark position becomes invalid + */ + public synchronized void mark(final int limit) { + try { + in.mark(limit); + } catch (IOException ioe) { + throw new RuntimeException(ioe.getMessage()); + } + } + + + /** + * @return the current number of bytes ready for reading + * @exception IOException if an error occurs + */ + public synchronized int available() throws IOException { + if (in == null) { + throw new IOException("Stream Closed"); + } + if (slack != null) { + return slack.length - begin; + } + if (in.ready()) { + return 1; + } else { + return 0; + } + } + + /** + * @return false - mark is not supported + */ + public boolean markSupported () { + return false; // would be imprecise + } + + /** + * Resets the StringReader. + * + * @exception IOException if the StringReader fails to be reset + */ + public synchronized void reset() throws IOException { + if (in == null) { + throw new IOException("Stream Closed"); + } + slack = null; + in.reset(); + } + + /** + * Closes the Stringreader. + * + * @exception IOException if the original StringReader fails to be closed + */ + public synchronized void close() throws IOException { + in.close(); + slack = null; + in = null; + } +} diff --git a/src/testcases/org/apache/tools/ant/taskdefs/ExecTaskTest.java b/src/testcases/org/apache/tools/ant/taskdefs/ExecTaskTest.java index 317044aee..19d865bda 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/ExecTaskTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/ExecTaskTest.java @@ -29,6 +29,7 @@ import java.util.GregorianCalendar; import junit.framework.ComparisonFailure; /** + * Unit test for the <exec> task. */ public class ExecTaskTest extends BuildFileTest { private static final String BUILD_PATH = "src/etc/testcases/taskdefs/exec/"; @@ -61,11 +62,12 @@ public class ExecTaskTest extends BuildFileTest { if (getProject().getProperty("test.can.run") == null) { return; } - assertEquals(getProject().getProperty("ant.file") + " out" + assertEquals("unexpected log content", + getProject().getProperty("ant.file") + " out" + getProject().getProperty("ant.file") + " err", getLog()); } - public void testRedirect1() { + public void testRedirect1() throws IOException { executeTarget("redirect1"); if (getProject().getProperty("test.can.run") == null) { return; @@ -73,55 +75,39 @@ public class ExecTaskTest extends BuildFileTest { String expectedOut = getProject().getProperty("ant.file") + " out\n" + getProject().getProperty("ant.file") + " err\n"; - String actualOut = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - } catch (IOException eyeOhEx) { - } - assertEquals("unexpected output", expectedOut, actualOut); + assertEquals("unexpected output", + expectedOut, getFileString("redirect.out")); } - public void testRedirect2() { + public void testRedirect2() throws IOException { executeTarget("redirect2"); if (getProject().getProperty("test.can.run") == null) { return; } - String expectedOut = getProject().getProperty("ant.file") + " out\n"; - String expectedErr = getProject().getProperty("ant.file") + " err\n"; - String actualOut = null; - String actualErr = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { - } - assertEquals("unexpected output", expectedOut, actualOut); - assertEquals("unexpected error output", expectedErr, actualErr); + assertEquals("unexpected output", + getProject().getProperty("ant.file") + " out\n", + getFileString("redirect.out")); + assertEquals("unexpected error output", + getProject().getProperty("ant.file") + " err\n", + getFileString("redirect.err")); } - public void testRedirect3() { + public void testRedirect3() throws IOException { executeTarget("redirect3"); if (getProject().getProperty("test.can.run") == null) { return; } - assertEquals(getProject().getProperty("ant.file") + " err", getLog()); + assertEquals("unexpected log content", + getProject().getProperty("ant.file") + " err", getLog()); String expectedOut = getProject().getProperty("ant.file") + " out\n"; - String actualOut = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - } catch (IOException eyeOhEx) { - } - assertEquals("unexpected output", expectedOut, actualOut); + assertEquals("unexpected output", + expectedOut, getFileString("redirect.out")); assertPropertyEquals("redirect.out", expectedOut.trim()); } - public void testRedirect4() { + public void testRedirect4() throws IOException { executeTarget("redirect4"); if (getProject().getProperty("test.can.run") == null) { return; @@ -129,72 +115,218 @@ public class ExecTaskTest extends BuildFileTest { String expectedOut = getProject().getProperty("ant.file") + " out\n"; String expectedErr = getProject().getProperty("ant.file") + " err\n"; - String actualOut = null; - String actualErr = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { - } - assertEquals("unexpected output", expectedOut, actualOut); + assertEquals("unexpected output", + expectedOut, getFileString("redirect.out")); assertPropertyEquals("redirect.out", expectedOut.trim()); - assertEquals("unexpected error output", expectedErr, actualErr); + assertEquals("unexpected error output", + expectedErr, getFileString("redirect.err")); assertPropertyEquals("redirect.err", expectedErr.trim()); } - public void testRedirect5() { + public void testRedirect5() throws IOException { testRedirect5or6("redirect5"); } - public void testRedirect6() { + public void testRedirect6() throws IOException { testRedirect5or6("redirect6"); } - public void testRedirect5or6(String target) { + public void testRedirect5or6(String target) throws IOException { executeTarget(target); if (getProject().getProperty("wc.can.run") == null) { return; } - String expectedOut = getProject().getProperty("ant.file") + " out\n"; - - String actualOut = null; - String actualErr = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { - } - assertEquals("unexpected output", "3", actualOut.trim()); - assertEquals(getProject().getProperty("redirect.out").trim(), "3"); - assertEquals("unexpected error output", null, actualErr); + assertEquals("unexpected output", "3", getFileString("redirect.out").trim()); + assertEquals("property redirect.out", "3", + getProject().getProperty("redirect.out").trim()); + assertNull("unexpected error output", getFileString("redirect.err")); assertPropertyEquals("redirect.err", ""); } - public void testRedirect7() { + public void testRedirect7() throws IOException { executeTarget("redirect7"); if (getProject().getProperty("wc.can.run") == null) { return; } + assertEquals("unexpected output", "3", getFileString("redirect.out").trim()); + assertEquals("property redirect.out", "3", + getProject().getProperty("redirect.out").trim()); + assertNull("unexpected error output", getFileString("redirect.err")); + } + + public void testRedirector1() { + executeTarget("init"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + expectBuildException("redirector1", "cannot have > 1 nested s"); + } + + public void testRedirector2() throws IOException { + executeTarget("redirector2"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + + assertEquals("unexpected output", + getProject().getProperty("ant.file") + " out\n" + + getProject().getProperty("ant.file") + " err\n", + getFileString("redirector.out")); + } + + public void testRedirector3() throws IOException { + executeTarget("redirector3"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + + assertEquals("unexpected output", + getProject().getProperty("ant.file") + " out\n", + getFileString("redirector.out")); + assertEquals("unexpected error output", + getProject().getProperty("ant.file") + " err\n", + getFileString("redirector.err")); + } + + public void testRedirector4() throws IOException { + executeTarget("redirector4"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + String expectedOut = getProject().getProperty("ant.file") + " out\n"; + + assertEquals("unexpected log content", + getProject().getProperty("ant.file") + " err", getLog()); + assertEquals("unexpected output", expectedOut, + getFileString("redirector.out")); + assertPropertyEquals("redirector.out", expectedOut.trim()); + } + + public void testRedirector5() throws IOException { + testRedirector5or6("redirector5"); + } + + public void testRedirector6() throws IOException { + testRedirector5or6("redirector6"); + } + + private void testRedirector5or6(String target) throws IOException { + executeTarget(target); + if (getProject().getProperty("test.can.run") == null) { + return; + } + String expectedOut = getProject().getProperty("ant.file") + " out\n"; + String expectedErr = getProject().getProperty("ant.file") + " err\n"; + + assertEquals("unexpected output", expectedOut, + getFileString("redirector.out")); + assertPropertyEquals("redirector.out", expectedOut.trim()); + assertEquals("unexpected error output", expectedErr, + getFileString("redirector.err")); + assertPropertyEquals("redirector.err", expectedErr.trim()); + } + + public void testRedirector7() throws IOException { + executeTarget("redirector7"); + if (getProject().getProperty("test.can.run") == null) { + return; + } String expectedOut = getProject().getProperty("ant.file") + " out\n"; + String expectedErr = getProject().getProperty("ant.file") + " ERROR!!!\n"; + + assertEquals("unexpected output", expectedOut, + getFileString("redirector.out")); + assertPropertyEquals("redirector.out", expectedOut.trim()); + assertEquals("unexpected error output", expectedErr, + getFileString("redirector.err")); + assertPropertyEquals("redirector.err", expectedErr.trim()); + } + + public void testRedirector8() throws IOException { + executeTarget("redirector8"); + if (getProject().getProperty("wc.can.run") == null) { + return; + } + + assertEquals("unexpected output", "3", getFileString("redirector.out").trim()); + assertEquals("property redirector.out", "3", + getProject().getProperty("redirector.out").trim()); + assertNull("unexpected error output", getFileString("redirector.err")); + assertPropertyEquals("redirector.err", ""); + } + + public void testRedirector9() throws IOException { + testRedirector9Thru12("redirector9"); + } + + public void testRedirector10() throws IOException { + testRedirector9Thru12("redirector10"); + } + + public void testRedirector11() throws IOException { + testRedirector9Thru12("redirector11"); + } + + public void testRedirector12() throws IOException { + testRedirector9Thru12("redirector12"); + } + + private void testRedirector9Thru12(String target) throws IOException { + executeTarget(target); + if (getProject().getProperty("cat.can.run") == null) { + return; + } + String expectedOut = "blah after blah"; - String actualOut = null; - String actualErr = null; + assertEquals("unexpected output", + expectedOut, getFileString("redirector.out").trim()); + assertPropertyEquals("redirector.out", expectedOut.trim()); + assertNull("unexpected error output", getFileString("redirector.err")); + assertPropertyEquals("redirector.err", ""); + } + + public void testRedirector13() { + executeTarget("redirector13"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + String antfile = getProject().getProperty("ant.file"); try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { + //no point in setting a message + assertEquals(antfile + " OUTPUT???" + antfile + " ERROR!!!", getLog()); + } catch (ComparisonFailure cf) { + assertEquals("unexpected log content", + antfile + " ERROR!!!" + antfile + " OUTPUT???", getLog()); + } + } + + public void testRedirector14() { + executeTarget("redirector14"); + if (getProject().getProperty("cat.can.run") == null) { + return; + } + assertEquals("unexpected log output", "blah after blah", getLog()); + } + + public void testRedirector15() throws IOException { + executeTarget("redirector15"); + if (getProject().getProperty("cat.can.run") == null) { + return; } - assertEquals("unexpected output", "3", actualOut.trim()); - assertEquals(getProject().getProperty("redirect.out").trim(), "3"); - assertEquals("unexpected error output", null, actualErr); + assertTrue("error with transcoding", + FileUtils.newFileUtils().contentEquals( + getProject().resolveFile("expected/utf-8"), + getProject().resolveFile("redirector.out"))); + } + + public void testRedirector16() { + executeTarget("redirector16"); + } + + public void testRedirector17() { + executeTarget("redirector17"); } public void testspawn() { @@ -256,7 +388,6 @@ public class ExecTaskTest extends BuildFileTest { project.setProperty("logFile", logFile); } - public void setTimeToWait(int timeToWait) { this.timeToWait = timeToWait; project.setProperty("timeToWait", Long.toString(timeToWait)); @@ -319,4 +450,24 @@ public class ExecTaskTest extends BuildFileTest { public void messageLogged(BuildEvent event) { } } + + //borrowed from TokenFilterTest + private String getFileString(String filename) throws IOException { + String result = null; + FileReader reader = null; + try { + reader = new FileReader(getProject().resolveFile(filename)); + result = FileUtils.newFileUtils().readFully(reader); + } catch (IOException eyeOhEx) { + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Throwable ignore) { + } + } + } + return result; + } + } diff --git a/src/testcases/org/apache/tools/ant/taskdefs/ExecuteOnTest.java b/src/testcases/org/apache/tools/ant/taskdefs/ExecuteOnTest.java index 856f15f07..d42cfa098 100755 --- a/src/testcases/org/apache/tools/ant/taskdefs/ExecuteOnTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/ExecuteOnTest.java @@ -17,23 +17,21 @@ package org.apache.tools.ant.taskdefs; -import org.apache.tools.ant.*; +import org.apache.tools.ant.BuildFileTest; import org.apache.tools.ant.util.FileUtils; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; -import java.util.GregorianCalendar; - -import junit.framework.ComparisonFailure; /** + * Unit test for the <apply> task. */ public class ExecuteOnTest extends BuildFileTest { private static final String BUILD_PATH = "src/etc/testcases/taskdefs/exec/"; private static final String BUILD_FILE = BUILD_PATH + "apply.xml"; - + public ExecuteOnTest(String name) { super(name); } @@ -62,29 +60,25 @@ public class ExecuteOnTest extends BuildFileTest { int xerr = log.indexOf(x + " err"); int yerr = log.indexOf(y + " err"); int zerr = log.indexOf(z + " err"); - assertFalse("xout < 0", xout < 0); - assertFalse("yout < 0", yout < 0); - assertFalse("zout < 0", zout < 0); - assertFalse("xerr < 0", xerr < 0); - assertFalse("yerr < 0", yerr < 0); - assertFalse("zerr < 0", zerr < 0); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); assertFalse("yout < xout", yout < xout); assertFalse("zout < yout", zout < yout); assertFalse("yerr < xerr", yerr < xerr); assertFalse("zerr < yerr", zerr < yerr); } - public void testRedirect1() { + public void testRedirect1() throws IOException { executeTarget("redirect1"); if (getProject().getProperty("test.can.run") == null) { return; } - String actualOut = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - } catch (IOException eyeOhEx) { - } + String actualOut = getFileString("redirect.out"); + File x = getProject().resolveFile("x"); File y = getProject().resolveFile("y"); File z = getProject().resolveFile("z"); @@ -94,32 +88,26 @@ public class ExecuteOnTest extends BuildFileTest { int xerr = actualOut.indexOf(x + " err"); int yerr = actualOut.indexOf(y + " err"); int zerr = actualOut.indexOf(z + " err"); - assertFalse("xout < 0", xout < 0); - assertFalse("yout < 0", yout < 0); - assertFalse("zout < 0", zout < 0); - assertFalse("xerr < 0", xerr < 0); - assertFalse("yerr < 0", yerr < 0); - assertFalse("zerr < 0", zerr < 0); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); assertFalse("yout < xout", yout < xout); assertFalse("zout < yout", zout < yout); assertFalse("yerr < xerr", yerr < xerr); assertFalse("zerr < yerr", zerr < yerr); } - public void testRedirect2() { + public void testRedirect2() throws IOException { executeTarget("redirect2"); if (getProject().getProperty("test.can.run") == null) { return; } - String actualOut = null; - String actualErr = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { - } + String actualOut = getFileString("redirect.out"); + String actualErr = getFileString("redirect.err"); + File x = getProject().resolveFile("x"); File y = getProject().resolveFile("y"); File z = getProject().resolveFile("z"); @@ -129,29 +117,25 @@ public class ExecuteOnTest extends BuildFileTest { int xerr = actualErr.indexOf(x + " err"); int yerr = actualErr.indexOf(y + " err"); int zerr = actualErr.indexOf(z + " err"); - assertFalse("xout < 0", xout < 0); - assertFalse("yout < 0", yout < 0); - assertFalse("zout < 0", zout < 0); - assertFalse("xerr < 0", xerr < 0); - assertFalse("yerr < 0", yerr < 0); - assertFalse("zerr < 0", zerr < 0); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); assertFalse("yout < xout", yout < xout); assertFalse("zout < yout", zout < yout); assertFalse("yerr < xerr", yerr < xerr); assertFalse("zerr < yerr", zerr < yerr); } - public void testRedirect3() { + public void testRedirect3() throws IOException { executeTarget("redirect3"); if (getProject().getProperty("test.can.run") == null) { return; } - String actualOut = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - } catch (IOException eyeOhEx) { - } + String actualOut = getFileString("redirect.out"); + File x = getProject().resolveFile("x"); File y = getProject().resolveFile("y"); File z = getProject().resolveFile("z"); @@ -161,33 +145,36 @@ public class ExecuteOnTest extends BuildFileTest { int xerr = getLog().indexOf(x + " err"); int yerr = getLog().indexOf(y + " err"); int zerr = getLog().indexOf(z + " err"); - assertFalse("xout < 0", xout < 0); - assertFalse("yout < 0", yout < 0); - assertFalse("zout < 0", zout < 0); - assertFalse("xerr < 0", xerr < 0); - assertFalse("yerr < 0", yerr < 0); - assertFalse("zerr < 0", zerr < 0); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); assertFalse("yout < xout", yout < xout); assertFalse("zout < yout", zout < yout); assertFalse("yerr < xerr", yerr < xerr); assertFalse("zerr < yerr", zerr < yerr); - assertPropertyEquals("redirect.out", x + " out"); + + String outProperty = getProject().getProperty("redirect.out"); + int pxout = outProperty.indexOf(x + " out"); + int pyout = outProperty.indexOf(y + " out"); + int pzout = outProperty.indexOf(z + " out"); + assertFalse("pxout=" + pxout, pxout < 0); + assertFalse("pyout=" + pyout, pyout < 0); + assertFalse("pzout=" + pzout, pzout < 0); + assertFalse("pyout < pxout", pyout < pxout); + assertFalse("pzout < pyout", pzout < pyout); } - public void testRedirect4() { + public void testRedirect4() throws IOException { executeTarget("redirect4"); if (getProject().getProperty("test.can.run") == null) { return; } - String actualOut = null; - String actualErr = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { - } + String actualOut = getFileString("redirect.out"); + String actualErr = getFileString("redirect.err"); + File x = getProject().resolveFile("x"); File y = getProject().resolveFile("y"); File z = getProject().resolveFile("z"); @@ -197,68 +184,388 @@ public class ExecuteOnTest extends BuildFileTest { int xerr = actualErr.indexOf(x + " err"); int yerr = actualErr.indexOf(y + " err"); int zerr = actualErr.indexOf(z + " err"); - assertFalse("xout < 0", xout < 0); - assertFalse("yout < 0", yout < 0); - assertFalse("zout < 0", zout < 0); - assertFalse("xerr < 0", xerr < 0); - assertFalse("yerr < 0", yerr < 0); - assertFalse("zerr < 0", zerr < 0); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); assertFalse("yout < xout", yout < xout); assertFalse("zout < yout", zout < yout); assertFalse("yerr < xerr", yerr < xerr); assertFalse("zerr < yerr", zerr < yerr); - assertPropertyEquals("redirect.out", x + " out"); - assertPropertyEquals("redirect.err", x + " err"); + + String outProperty = getProject().getProperty("redirect.out"); + int pxout = outProperty.indexOf(x + " out"); + int pyout = outProperty.indexOf(y + " out"); + int pzout = outProperty.indexOf(z + " out"); + assertFalse("pxout=" + pxout, pxout < 0); + assertFalse("pyout=" + pyout, pyout < 0); + assertFalse("pzout=" + pzout, pzout < 0); + assertFalse("pyout < pxout", pyout < pxout); + assertFalse("pzout < pyout", pzout < pyout); + + String errorProperty = getProject().getProperty("redirect.err"); + int pxerr = errorProperty.indexOf(x + " err"); + int pyerr = errorProperty.indexOf(y + " err"); + int pzerr = errorProperty.indexOf(z + " err"); + assertFalse("pxerr=" + pxerr, pxerr < 0); + assertFalse("pyerr=" + pyerr, pyerr < 0); + assertFalse("pzerr=" + pzerr, pzerr < 0); + assertFalse("pyerr < pxerr", pyerr < pxerr); + assertFalse("pzerr < pyerr", pzerr < pyerr); } - public void testRedirect5() { + public void testRedirect5() throws IOException { testRedirect5or6("redirect5"); } - public void testRedirect6() { + public void testRedirect6() throws IOException { testRedirect5or6("redirect6"); } - private void testRedirect5or6(String target) { + private void testRedirect5or6(String target) throws IOException { executeTarget(target); if (getProject().getProperty("sed.can.run") == null) { return; } - String actualOut = null; - String actualErr = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { - } - assertPropertyEquals("redirect.out", "blah y z"); + + assertPropertyEquals("redirect.out", getProject().replaceProperties( + "blah y z${line.separator}x blah z${line.separator}x y blah")); assertPropertyEquals("redirect.err", ""); - assertEquals("unexpected content in redirect.out", - "blah y z\nx blah z\nx y blah\n", actualOut); - assertEquals("unexpected content in redirect.err", null, actualErr); + assertEquals("unexpected output", + "blah y z\nx blah z\nx y blah\n", getFileString("redirect.out")); + assertNull("unexpected error output", getFileString("redirect.err")); } - public void testRedirect7() { + public void testRedirect7() throws IOException { executeTarget("redirect7"); if (getProject().getProperty("sed.can.run") == null) { return; } - String actualOut = null; - String actualErr = null; - try { - actualOut = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.out"))); - actualErr = FileUtils.newFileUtils().readFully(new FileReader( - getProject().resolveFile("redirect.err"))); - } catch (IOException eyeOhEx) { - } + assertPropertyEquals("redirect.out", "blah y z"); assertPropertyUnset("redirect.err"); - assertEquals("unexpected content in redirect.out", - "x y blah\n", actualOut); - assertEquals("unexpected content in redirect.err", null, actualErr); + assertEquals("unexpected output", + "x y blah\n", getFileString("redirect.out")); + assertNull("unexpected error output", getFileString("redirect.err")); + } + + public void testRedirector1() { + executeTarget("init"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + expectBuildException("redirector1", "cannot have > 1 nested s"); + } + + public void testRedirector2() throws IOException { + executeTarget("redirector2"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + + String actualOut = getFileString("redirector.out"); + + File x = getProject().resolveFile("x"); + File y = getProject().resolveFile("y"); + File z = getProject().resolveFile("z"); + int xout = actualOut.indexOf(x + " out"); + int yout = actualOut.indexOf(y + " out"); + int zout = actualOut.indexOf(z + " out"); + int xerr = actualOut.indexOf(x + " err"); + int yerr = actualOut.indexOf(y + " err"); + int zerr = actualOut.indexOf(z + " err"); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); + assertFalse("yout < xout", yout < xout); + assertFalse("zout < yout", zout < yout); + assertFalse("yerr < xerr", yerr < xerr); + assertFalse("zerr < yerr", zerr < yerr); + } + + public void testRedirector3() throws IOException { + executeTarget("redirector3"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + + String actualOut = getFileString("redirector.out"); + String actualErr = getFileString("redirector.err"); + + File x = getProject().resolveFile("x"); + File y = getProject().resolveFile("y"); + File z = getProject().resolveFile("z"); + int xout = actualOut.indexOf(x + " out"); + int yout = actualOut.indexOf(y + " out"); + int zout = actualOut.indexOf(z + " out"); + int xerr = actualErr.indexOf(x + " err"); + int yerr = actualErr.indexOf(y + " err"); + int zerr = actualErr.indexOf(z + " err"); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); + assertFalse("yout < xout", yout < xout); + assertFalse("zout < yout", zout < yout); + assertFalse("yerr < xerr", yerr < xerr); + assertFalse("zerr < yerr", zerr < yerr); + } + + public void testRedirector4() throws IOException { + executeTarget("redirector4"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + String actualOut = getFileString("redirector.out"); + + File x = getProject().resolveFile("x"); + File y = getProject().resolveFile("y"); + File z = getProject().resolveFile("z"); + int xout = actualOut.indexOf(x + " out"); + int yout = actualOut.indexOf(y + " out"); + int zout = actualOut.indexOf(z + " out"); + int xerr = getLog().indexOf(x + " err"); + int yerr = getLog().indexOf(y + " err"); + int zerr = getLog().indexOf(z + " err"); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); + assertFalse("yout < xout", yout < xout); + assertFalse("zout < yout", zout < yout); + assertFalse("yerr < xerr", yerr < xerr); + assertFalse("zerr < yerr", zerr < yerr); + + String outProperty = getProject().getProperty("redirector.out"); + int pxout = outProperty.indexOf(x + " out"); + int pyout = outProperty.indexOf(y + " out"); + int pzout = outProperty.indexOf(z + " out"); + assertFalse("pxout=" + pxout, pxout < 0); + assertFalse("pyout=" + pyout, pyout < 0); + assertFalse("pzout=" + pzout, pzout < 0); + assertFalse("pyout < pxout", pyout < pxout); + assertFalse("pzout < pyout", pzout < pyout); + } + + public void testRedirector5() throws IOException { + testRedirector5or6("redirector5"); + } + + public void testRedirector6() throws IOException { + testRedirector5or6("redirector6"); + } + + private void testRedirector5or6(String target) throws IOException { + executeTarget(target); + if (getProject().getProperty("test.can.run") == null) { + return; + } + String actualOut = getFileString("redirector.out"); + String actualErr = getFileString("redirector.err"); + + File x = getProject().resolveFile("x"); + File y = getProject().resolveFile("y"); + File z = getProject().resolveFile("z"); + int xout = actualOut.indexOf(x + " out"); + int yout = actualOut.indexOf(y + " out"); + int zout = actualOut.indexOf(z + " out"); + int xerr = actualErr.indexOf(x + " err"); + int yerr = actualErr.indexOf(y + " err"); + int zerr = actualErr.indexOf(z + " err"); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); + assertFalse("yout < xout", yout < xout); + assertFalse("zout < yout", zout < yout); + assertFalse("yerr < xerr", yerr < xerr); + assertFalse("zerr < yerr", zerr < yerr); + + String outProperty = getProject().getProperty("redirector.out"); + int pxout = outProperty.indexOf(x + " out"); + int pyout = outProperty.indexOf(y + " out"); + int pzout = outProperty.indexOf(z + " out"); + assertFalse("pxout=" + pxout, pxout < 0); + assertFalse("pyout=" + pyout, pyout < 0); + assertFalse("pzout=" + pzout, pzout < 0); + assertFalse("pyout < pxout", pyout < pxout); + assertFalse("pzout < pyout", pzout < pyout); + + String errorProperty = getProject().getProperty("redirector.err"); + int pxerr = errorProperty.indexOf(x + " err"); + int pyerr = errorProperty.indexOf(y + " err"); + int pzerr = errorProperty.indexOf(z + " err"); + assertFalse("pxerr=" + pxerr, pxerr < 0); + assertFalse("pyerr=" + pyerr, pyerr < 0); + assertFalse("pzerr=" + pzerr, pzerr < 0); + assertFalse("pyerr < pxerr", pyerr < pxerr); + assertFalse("pzerr < pyerr", pzerr < pyerr); + } + + public void testRedirector7() throws IOException { + executeTarget("redirector7"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + String actualOut = getFileString("redirector.out"); + String actualErr = getFileString("redirector.err"); + + File x = getProject().resolveFile("x"); + File y = getProject().resolveFile("y"); + File z = getProject().resolveFile("z"); + int xout = actualOut.indexOf(x + " out"); + int yout = actualOut.indexOf(y + " out"); + int zout = actualOut.indexOf(z + " out"); + int xerr = actualErr.indexOf(x + " ERROR!!!"); + int yerr = actualErr.indexOf(y + " ERROR!!!"); + int zerr = actualErr.indexOf(z + " ERROR!!!"); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); + assertFalse("yout < xout", yout < xout); + assertFalse("zout < yout", zout < yout); + assertFalse("yerr < xerr", yerr < xerr); + assertFalse("zerr < yerr", zerr < yerr); + + String outProperty = getProject().getProperty("redirector.out"); + int pxout = outProperty.indexOf(x + " out"); + int pyout = outProperty.indexOf(y + " out"); + int pzout = outProperty.indexOf(z + " out"); + assertFalse("pxout=" + pxout, pxout < 0); + assertFalse("pyout=" + pyout, pyout < 0); + assertFalse("pzout=" + pzout, pzout < 0); + assertFalse("pyout < pxout", pyout < pxout); + assertFalse("pzout < pyout", pzout < pyout); + + String errorProperty = getProject().getProperty("redirector.err"); + int pxerr = errorProperty.indexOf(x + " ERROR!!!"); + int pyerr = errorProperty.indexOf(y + " ERROR!!!"); + int pzerr = errorProperty.indexOf(z + " ERROR!!!"); + assertFalse("pxerr=" + pxerr, pxerr < 0); + assertFalse("pyerr=" + pyerr, pyerr < 0); + assertFalse("pzerr=" + pzerr, pzerr < 0); + assertFalse("pyerr < pxerr", pyerr < pxerr); + assertFalse("pzerr < pyerr", pzerr < pyerr); + } + + public void testRedirector8() throws IOException { + executeTarget("redirector8"); + if (getProject().getProperty("sed.can.run") == null) { + return; + } + + assertPropertyEquals("redirector.out", getProject().replaceProperties( + "blah y z${line.separator}x blah z${line.separator}x y blah")); + assertPropertyEquals("redirector.err", ""); + assertEquals("unexpected output", + "blah y z\nx blah z\nx y blah\n", getFileString("redirector.out")); + assertNull("unexpected error output", getFileString("redirector.err")); + } + + public void testRedirector9() throws IOException { + testRedirector9Thru12("redirector9"); + } + + public void testRedirector10() throws IOException { + testRedirector9Thru12("redirector10"); + } + + public void testRedirector11() throws IOException { + testRedirector9Thru12("redirector11"); + } + + public void testRedirector12() throws IOException { + testRedirector9Thru12("redirector12"); + } + + private void testRedirector9Thru12(String target) throws IOException { + executeTarget(target); + if (getProject().getProperty("sed.can.run") == null) { + return; + } + + assertNull("unexpected error output", getFileString("redirector.err")); + assertPropertyEquals("redirector.out", getProject().replaceProperties( + "blah after y after z${line.separator}x after blah after z" + + "${line.separator}x after y after blah")); + assertPropertyEquals("redirector.err", ""); + assertEquals("unexpected output", + "blah after y after z\nx after blah after z" + + "\nx after y after blah\n", getFileString("redirector.out")); + } + + public void testRedirector13() { + executeTarget("redirector13"); + if (getProject().getProperty("test.can.run") == null) { + return; + } + String log = getLog(); + File x = getProject().resolveFile("x"); + File y = getProject().resolveFile("y"); + File z = getProject().resolveFile("z"); + int xout = log.indexOf(x + " OUTPUT???"); + int yout = log.indexOf(y + " OUTPUT???"); + int zout = log.indexOf(z + " OUTPUT???"); + int xerr = log.indexOf(x + " ERROR!!!"); + int yerr = log.indexOf(y + " ERROR!!!"); + int zerr = log.indexOf(z + " ERROR!!!"); + assertFalse("xout=" + xout, xout < 0); + assertFalse("yout=" + yout, yout < 0); + assertFalse("zout=" + zout, zout < 0); + assertFalse("xerr=" + xerr, xerr < 0); + assertFalse("yerr=" + yerr, yerr < 0); + assertFalse("zerr=" + zerr, zerr < 0); + assertFalse("yout < xout", yout < xout); + assertFalse("zout < yout", zout < yout); + assertFalse("yerr < xerr", yerr < xerr); + assertFalse("zerr < yerr", zerr < yerr); + } + + public void testRedirector14() throws IOException { + executeTarget("redirector14"); + if (getProject().getProperty("sed.can.run") == null) { + return; + } + + assertEquals("unexpected log content", + "z after y after blahx after y after blah", getLog()); + + assertEquals("unexpected redirector.out content", + "x after blah after z\n", getFileString("redirector.out")); + + assertNull("unexpected redirector.err content", getFileString("redirector.err")); + } + + //borrowed from TokenFilterTest + private String getFileString(String filename) throws IOException { + String result = null; + FileReader reader = null; + try { + reader = new FileReader(getProject().resolveFile(filename)); + result = FileUtils.newFileUtils().readFully(reader); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Throwable ignore) { + } + } + } + return result; } } diff --git a/src/testcases/org/apache/tools/ant/taskdefs/JavaTest.java b/src/testcases/org/apache/tools/ant/taskdefs/JavaTest.java index 589ebfd15..f9238c6f5 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/JavaTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/JavaTest.java @@ -20,7 +20,9 @@ package org.apache.tools.ant.taskdefs; import junit.framework.*; import java.io.*; import org.apache.tools.ant.*; +//import org.apache.tools.ant.taskdefs.StreamPumper; import org.apache.tools.ant.util.FileUtils; +import org.apache.tools.ant.util.TeeOutputStream; /** * stress out java task @@ -186,6 +188,26 @@ public class JavaTest extends BuildFileTest { assertTrue("log file exists", logFile.exists()); } + public void testRedirect1() { + executeTarget("redirect1"); + } + + public void testRedirect2() { + executeTarget("redirect2"); + } + + public void testRedirect3() { + executeTarget("redirect3"); + } + + public void testRedirector1() { + executeTarget("redirector1"); + } + + public void testRedirector2() { + executeTarget("redirector2"); + } + /** * entry point class with no dependencies other * than normal JRE runtime @@ -266,4 +288,35 @@ public class JavaTest extends BuildFileTest { } } + + /** + * entry point class to pipe System.in to the specified stream: + * "out", "err", or "both". If none specified, swallow the input. + */ + public static class PipeEntryPoint { + + /** + * pipe input to specified output + */ + public static void main(String[] args) { + OutputStream os = null; + if (args.length > 0) { + if ("out".equalsIgnoreCase(args[0])) { + os = System.out; + } else if ("err".equalsIgnoreCase(args[0])) { + os = System.err; + } else if ("both".equalsIgnoreCase(args[0])) { + os = new TeeOutputStream(System.out, System.err); + } + } + if (os != null) { + Thread t = new Thread(new StreamPumper(System.in, os, true)); + t.start(); + try { + t.join(); + } catch (InterruptedException eyeEx) { + } + } + } + } }