diff --git a/src/etc/testcases/taskdefs/parallel.xml b/src/etc/testcases/taskdefs/parallel.xml
new file mode 100644
index 000000000..b66a53e13
--- /dev/null
+++ b/src/etc/testcases/taskdefs/parallel.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Test build file for the <parallel> task.
+ Use the various targets to run the tests.
+
+
diff --git a/src/main/org/apache/tools/ant/DemuxOutputStream.java b/src/main/org/apache/tools/ant/DemuxOutputStream.java
index 63878b9a3..cf9f471e0 100644
--- a/src/main/org/apache/tools/ant/DemuxOutputStream.java
+++ b/src/main/org/apache/tools/ant/DemuxOutputStream.java
@@ -64,25 +64,39 @@ import java.util.Hashtable;
* project object which will forward the content to the appropriate
* task.
*
+ * @since 1.4
* @author Conor MacNeill
*/
public class DemuxOutputStream extends OutputStream {
+ /**
+ * A data class to store information about a buffer. Such informatio
+ * is stored on a per-thread basis.
+ */
+ private static class BufferInfo {
+ /**
+ * The per-thread output stream
+ */
+ private ByteArrayOutputStream buffer;
+
+ /**
+ * Whether the next line-terminator should be skipped in terms
+ * of processing the buffer or not. Used to avoid \r\n invoking
+ * processBuffer twice.
+ */
+ private boolean skip = false;
+ }
+
/** Maximum buffer size */
private final static int MAX_SIZE = 1024;
- /** Mapping from thread to buffer (Thread to ByteOutputStream) */
+ /** Mapping from thread to buffer (Thread to BufferInfo) */
private Hashtable buffers = new Hashtable();
-// private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- /**
- * Whether the next line-terminator should be skipped in terms
- * of processing the buffer or not. Used to avoid \r\n invoking
- * processBuffer twice.
- */
- private boolean skip = false;
+
/**
* The project to send output to
*/
private Project project;
+
/**
* Whether or not this stream represents an error stream
*/
@@ -91,8 +105,11 @@ public class DemuxOutputStream extends OutputStream {
/**
* Creates a new instance of this class.
*
- * @param task the task for whom to log
- * @param level loglevel used to log data written to this stream.
+ * @param project the project instance for which output is being
+ * demultiplexed.
+ * @param isErrorStream true if this is the error string, otherwise
+ * a normal output stream. This is passed to the project so it knows
+ * which stream it is receiving.
*/
public DemuxOutputStream(Project project, boolean isErrorStream) {
this.project = project;
@@ -104,20 +121,22 @@ public class DemuxOutputStream extends OutputStream {
*
* @return a ByteArrayOutputStream for the current thread to write data to
*/
- private ByteArrayOutputStream getBuffer() {
+ private BufferInfo getBufferInfo() {
Thread current = Thread.currentThread();
- ByteArrayOutputStream buffer = (ByteArrayOutputStream)buffers.get(current);
- if (buffer == null) {
- buffer = new ByteArrayOutputStream();
- buffers.put(current, buffer);
+ BufferInfo bufferInfo = (BufferInfo)buffers.get(current);
+ if (bufferInfo == null) {
+ bufferInfo = new BufferInfo();
+ bufferInfo.buffer = new ByteArrayOutputStream();
+ bufferInfo.skip = false;
+ buffers.put(current, bufferInfo);
}
- return buffer;
+ return bufferInfo;
}
/**
* Resets the buffer for the current thread.
*/
- private void resetBuffer() {
+ private void resetBufferInfo() {
Thread current = Thread.currentThread();
buffers.remove(current);
}
@@ -127,44 +146,43 @@ public class DemuxOutputStream extends OutputStream {
* separator is detected or if the buffer has reached its maximum size.
*
* @param cc data to log (byte).
+ * @exception IOException if the data cannot be written to the stream
*/
public void write(int cc) throws IOException {
final byte c = (byte)cc;
+
+ BufferInfo bufferInfo = getBufferInfo();
if ((c == '\n') || (c == '\r')) {
- if (!skip) {
- processBuffer();
+ if (!bufferInfo.skip) {
+ processBuffer(bufferInfo.buffer);
}
} else {
- ByteArrayOutputStream buffer = getBuffer();
- buffer.write(cc);
- if (buffer.size() > MAX_SIZE) {
- processBuffer();
+ bufferInfo.buffer.write(cc);
+ if (bufferInfo.buffer.size() > MAX_SIZE) {
+ processBuffer(bufferInfo.buffer);
}
}
- // XXX: This isn't threadsafe. Consider two threads logging
- // Hello\r\n
- // and
- // There\r\n
- // at the same time, with the two '\r's both being sent before
- // either '\n', and the '\n's coming in the opposite order (thread-wise)
- // to the '\r's - one buffer will be processed twice, and the other won't
- // be processed at all.
- skip = (c == '\r');
+ bufferInfo.skip = (c == '\r');
}
/**
* Converts the buffer to a string and sends it to
* {@link Project#demuxOutput(String,boolean) Project.demuxOutput}.
+ *
+ * @param buffer the ByteArrayOutputStream used to collect the output
+ * until a line separator is seen.
*/
- protected void processBuffer() {
- String output = getBuffer().toString();
+ protected void processBuffer(ByteArrayOutputStream buffer) {
+ String output = buffer.toString();
project.demuxOutput(output, isErrorStream);
- resetBuffer();
+ resetBufferInfo();
}
/**
* Equivalent to calling {@link #flush flush} on the stream.
+ *
+ * @exception IOException if there is a problem closing the stream.
*/
public void close() throws IOException {
flush();
@@ -173,10 +191,13 @@ public class DemuxOutputStream extends OutputStream {
/**
* Writes all remaining data in the buffer associated
* with the current thread to the project.
+ *
+ * @exception IOException if there is a problem flushing the stream.
*/
public void flush() throws IOException {
- if (getBuffer().size() > 0) {
- processBuffer();
+ BufferInfo bufferInfo = getBufferInfo();
+ if (bufferInfo.buffer.size() > 0) {
+ processBuffer(bufferInfo.buffer);
}
}
}
diff --git a/src/main/org/apache/tools/ant/UnknownElement.java b/src/main/org/apache/tools/ant/UnknownElement.java
index d56811650..1812082f2 100644
--- a/src/main/org/apache/tools/ant/UnknownElement.java
+++ b/src/main/org/apache/tools/ant/UnknownElement.java
@@ -80,12 +80,17 @@ public class UnknownElement extends Task {
*/
private Vector children = new Vector();
+ /**
+ * Create an UnknownElement for the given element name.
+ *
+ * @param elementName the name of the unknown element.
+ */
public UnknownElement (String elementName) {
this.elementName = elementName;
}
/**
- * return the corresponding XML element name.
+ * @return the corresponding XML element name.
*/
public String getTag() {
return elementName;
@@ -94,6 +99,8 @@ public class UnknownElement extends Task {
/**
* creates the real object instance, creates child elements, configures
* the attributes of the real object.
+ *
+ * @exception BuildException if the configuration fails
*/
public void maybeConfigure() throws BuildException {
realThing = makeObject(this, wrapper);
@@ -113,6 +120,32 @@ public class UnknownElement extends Task {
}
}
+ /**
+ * Handle output sent to System.out by this task or its real task.
+ *
+ * @param line the output string
+ */
+ protected void handleOutput(String line) {
+ if (realThing instanceof Task) {
+ ((Task)realThing).handleOutput(line);
+ } else {
+ super.handleOutput(line);
+ }
+ }
+
+ /**
+ * Handle error output sent to System.err by this task or its real task.
+ *
+ * @param line the error string
+ */
+ protected void handleErrorOutput(String line) {
+ if (realThing instanceof Task) {
+ ((Task)realThing).handleErrorOutput(line);
+ } else {
+ super.handleErrorOutput(line);
+ }
+ }
+
/**
* Called when the real task has been configured for the first time.
*/
@@ -131,6 +164,8 @@ public class UnknownElement extends Task {
/**
* Adds a child element to this element.
+ *
+ * @param child the child element
*/
public void addChild(UnknownElement child) {
children.addElement(child);
@@ -139,6 +174,9 @@ public class UnknownElement extends Task {
/**
* Creates child elements, creates children of the children, sets
* attributes of the child elements.
+ *
+ * @param parent the configured object for the parent
+ * @exception BuildException if the children cannot be configured.
*/
protected void handleChildren(Object parent,
RuntimeConfigurable parentWrapper)
@@ -151,7 +189,7 @@ public class UnknownElement extends Task {
Class parentClass = parent.getClass();
IntrospectionHelper ih = IntrospectionHelper.getHelper(parentClass);
- for (int i=0; i." +lSep
+ + " Fix: look in the JAR to verify, then rebuild with the needed" + lSep
+ + " libraries, or download a release version from apache.org" + lSep
+ + " - The build file was written for a later version of Ant" + lSep
+ + " Fix: upgrade to at least the latest release version of Ant" + lSep
+ + " - The task is not an Ant core or optional task " + lSep
+ + " and needs to be declared using ." + lSep
+ + lSep
+ + "Remember that for JAR files to be visible to An t tasks implemented" + lSep
+ + "in ANT_HOME/lib, the files must be in the same directory or on the" + lSep
+ + "classpath" + lSep
+ lSep
- + "Remember that for JAR files to be visible to Ant tasks implemented" +lSep
- + "in ANT_HOME/lib, the files must be in the same directory or on the" +lSep
- + "classpath"+ lSep
- + lSep
- + "Please neither file bug reports on this problem, nor email the" +lSep
- + "Ant mailing lists, until all of these causes have been explored," +lSep
+ + "Please neither file bug reports on this problem, nor email the" + lSep
+ + "Ant mailing lists, until all of these causes have been explored," + lSep
+ "as this is not an Ant bug.";
@@ -259,7 +298,9 @@ public class UnknownElement extends Task {
}
/**
- * Return the task instance after it has been created (and if it is a task.
+ * Return the task instance after it has been created and if it is a task.
+ *
+ * @return a task instance or null if the real object is not a task
*/
public Task getTask() {
if (realThing != null && realThing instanceof Task) {
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java b/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java
new file mode 100644
index 000000000..6ab9dfa44
--- /dev/null
+++ b/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java
@@ -0,0 +1,110 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.BuildFileTest;
+import java.util.Random;
+/**
+ * A simple task that prints to System.out and System.err and then catches
+ * the output which it then check. If the output does not match, an
+ * exception is thrown
+ *
+ * @since 1.5
+ * @author Conor MacNeill
+ * @created 21 February 2002
+ */
+public class DemuxOutputTask extends Task {
+ private String randomOutValue;
+ private String randomErrValue;
+ private boolean outputReceived = false;
+ private boolean errorReceived = false;
+
+ public void execute() {
+ Random generator = new Random();
+ randomOutValue = "Output Value is " + generator.nextInt();
+ randomErrValue = "Error Value is " + generator.nextInt();
+
+ System.out.println(randomOutValue);
+ System.err.println(randomErrValue);
+ if (!outputReceived) {
+ throw new BuildException("Did not receive output");
+ }
+
+ if (!errorReceived) {
+ throw new BuildException("Did not receive error");
+ }
+ }
+
+ protected void handleOutput(String line) {
+ if (!line.equals(randomOutValue)) {
+ String message = "Received = [" + line + "], expected = ["
+ + randomOutValue + "]";
+ throw new BuildException(message);
+ }
+ outputReceived = true;
+ }
+
+ protected void handleErrorOutput(String line) {
+ if (!line.equals(randomErrValue)) {
+ String message = "Received = [" + line + "], expected = ["
+ + randomErrValue + "]";
+ throw new BuildException(message);
+ }
+ errorReceived = true;
+ }
+
+
+}
+
diff --git a/src/testcases/org/apache/tools/ant/taskdefs/ParallelTest.java b/src/testcases/org/apache/tools/ant/taskdefs/ParallelTest.java
new file mode 100644
index 000000000..8edc6376b
--- /dev/null
+++ b/src/testcases/org/apache/tools/ant/taskdefs/ParallelTest.java
@@ -0,0 +1,133 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+package org.apache.tools.ant.taskdefs;
+import java.io.PrintStream;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.DemuxOutputStream;
+import org.apache.tools.ant.Project;
+
+/**
+ * Test of the parallel TaskContainer
+ *
+ * @author Conor MacNeill
+ * @created 21 February 2002
+ */
+public class ParallelTest extends BuildFileTest {
+ /** Standard property value for the basic test */
+ public final static String DIRECT_MESSAGE = "direct";
+ /** Standard property value for the basic and fail test */
+ public final static String DELAYED_MESSAGE = "delayed";
+ /** Standard property value for the fail test */
+ public final static String FAILURE_MESSAGE = "failure";
+
+ /** the build fiel associated with this test */
+ public final static String TEST_BUILD_FILE
+ = "src/etc/testcases/taskdefs/parallel.xml";
+
+ /**
+ * Constructor for the ParallelTest object
+ *
+ * @param name name of the test
+ */
+ public ParallelTest(String name) {
+ super(name);
+ }
+
+ /** The JUnit setup method */
+ public void setUp() {
+ configureProject(TEST_BUILD_FILE);
+ }
+
+ /** tests basic operation of the parallel task */
+ public void testBasic() {
+ // should get no output at all
+ Project project = getProject();
+ project.setUserProperty("test.direct", DIRECT_MESSAGE);
+ project.setUserProperty("test.delayed", DELAYED_MESSAGE);
+ expectOutputAndError("testBasic", "", "");
+ String log = getLog();
+ assertEquals("parallel tasks didn't output correct data", log,
+ DIRECT_MESSAGE + DELAYED_MESSAGE);
+
+ }
+
+ /** tests the failure of a task within a parallel construction */
+ public void testFail() {
+ // should get no output at all
+ Project project = getProject();
+ project.setUserProperty("test.failure", FAILURE_MESSAGE);
+ project.setUserProperty("test.delayed", DELAYED_MESSAGE);
+ expectBuildExceptionContaining("testFail",
+ "fail task in one parallel branch", FAILURE_MESSAGE);
+ }
+
+ /** tests the demuxing of output streams in a multithreaded situation */
+ public void testDemux() {
+ Project project = getProject();
+ project.addTaskDefinition("demuxtest", DemuxOutputTask.class);
+ PrintStream out = System.out;
+ PrintStream err = System.err;
+ System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
+ System.setErr(new PrintStream(new DemuxOutputStream(project, true)));
+
+ try {
+ project.executeTarget("testDemux");
+ } finally {
+ System.setOut(out);
+ System.setErr(err);
+ }
+ }
+}
+