It introduces the concept of a TaskContainer to allow a task to contain other tasks. This allows Task composition It introduces a <parallel> task for multithreading support. There is also a <sequential> task. It reworks System.out management to handle all task generated output and route it through the Ant event system. This handles multithreaded output. This is a major rework to the patch originally submitted by Thomas. I have taken a different route for the output management, in particular. Based on patch by Thomas Christen <chr@active.ch> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@269371 13f79535-47bb-0310-9956-ffa450edef68master
@@ -0,0 +1,151 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2000 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 | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant; | |||
import java.io.*; | |||
import java.util.*; | |||
/** | |||
* Logs content written by a thread and forwards the buffers onto the | |||
* project object which will forward the content to the appropriate | |||
* task | |||
* | |||
* @author Conor MacNeill | |||
*/ | |||
public class DemuxOutputStream extends OutputStream { | |||
static private final int MAX_SIZE = 1024; | |||
private Hashtable buffers = new Hashtable(); | |||
// private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | |||
private boolean skip = false; | |||
private Project project; | |||
private boolean isErrorStream; | |||
/** | |||
* 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. | |||
*/ | |||
public DemuxOutputStream(Project project, boolean isErrorStream) { | |||
this.project = project; | |||
this.isErrorStream = isErrorStream; | |||
} | |||
private ByteArrayOutputStream getBuffer() { | |||
Thread current = Thread.currentThread(); | |||
ByteArrayOutputStream buffer = (ByteArrayOutputStream)buffers.get(current); | |||
if (buffer == null) { | |||
buffer = new ByteArrayOutputStream(); | |||
buffers.put(current, buffer); | |||
} | |||
return buffer; | |||
} | |||
private void resetBuffer() { | |||
Thread current = Thread.currentThread(); | |||
buffers.remove(current); | |||
} | |||
/** | |||
* Write the data to the buffer and flush the buffer, if a line | |||
* separator is detected. | |||
* | |||
* @param cc data to log (byte). | |||
*/ | |||
public void write(int cc) throws IOException { | |||
final byte c = (byte)cc; | |||
if ((c == '\n') || (c == '\r')) { | |||
if (!skip) { | |||
processBuffer(); | |||
} | |||
} else { | |||
ByteArrayOutputStream buffer = getBuffer(); | |||
buffer.write(cc); | |||
if (buffer.size() > MAX_SIZE) { | |||
processBuffer(); | |||
} | |||
} | |||
skip = (c == '\r'); | |||
} | |||
/** | |||
* Converts the buffer to a string and sends it to <code>processLine</code> | |||
*/ | |||
protected void processBuffer() { | |||
String output = getBuffer().toString(); | |||
project.demuxOutput(output, isErrorStream); | |||
resetBuffer(); | |||
} | |||
/** | |||
* Writes all remaining | |||
*/ | |||
public void close() throws IOException { | |||
flush(); | |||
} | |||
/** | |||
* Writes all remaining | |||
*/ | |||
public void flush() throws IOException { | |||
if (getBuffer().size() > 0) { | |||
processBuffer(); | |||
} | |||
} | |||
} |
@@ -267,7 +267,14 @@ public class Main { | |||
System.out.println("Only one logger class may be specified."); | |||
return; | |||
} | |||
loggerClassname = args[++i]; | |||
try { | |||
loggerClassname = args[++i]; | |||
} | |||
catch (ArrayIndexOutOfBoundsException aioobe) { | |||
System.out.println("You must specify a classname when " + | |||
"using the -logger argument"); | |||
return; | |||
} | |||
} else if (arg.equals("-emacs")) { | |||
emacsMode = true; | |||
} else if (arg.equals("-projecthelp")) { | |||
@@ -403,43 +410,54 @@ public class Main { | |||
try { | |||
addBuildListeners(project); | |||
project.fireBuildStarted(); | |||
project.init(); | |||
project.setUserProperty("ant.version", getAntVersion()); | |||
// set user-define properties | |||
Enumeration e = definedProps.keys(); | |||
while (e.hasMoreElements()) { | |||
String arg = (String)e.nextElement(); | |||
String value = (String)definedProps.get(arg); | |||
project.setUserProperty(arg, value); | |||
} | |||
project.setUserProperty("ant.file" , buildFile.getAbsolutePath() ); | |||
// first use the ProjectHelper to create the project object | |||
// from the given build file. | |||
PrintStream err = System.err; | |||
PrintStream out = System.out; | |||
try { | |||
Class.forName("javax.xml.parsers.SAXParserFactory"); | |||
ProjectHelper.configureProject(project, buildFile); | |||
} catch (NoClassDefFoundError ncdfe) { | |||
throw new BuildException("No JAXP compliant XML parser found. See http://java.sun.com/xml for the\nreference implementation.", ncdfe); | |||
} catch (ClassNotFoundException cnfe) { | |||
throw new BuildException("No JAXP compliant XML parser found. See http://java.sun.com/xml for the\nreference implementation.", cnfe); | |||
} catch (NullPointerException npe) { | |||
throw new BuildException("No JAXP compliant XML parser found. See http://java.sun.com/xml for the\nreference implementation.", npe); | |||
System.setOut(new PrintStream(new DemuxOutputStream(project, false))); | |||
System.setErr(new PrintStream(new DemuxOutputStream(project, true))); | |||
project.fireBuildStarted(); | |||
project.init(); | |||
project.setUserProperty("ant.version", getAntVersion()); | |||
// set user-define properties | |||
Enumeration e = definedProps.keys(); | |||
while (e.hasMoreElements()) { | |||
String arg = (String)e.nextElement(); | |||
String value = (String)definedProps.get(arg); | |||
project.setUserProperty(arg, value); | |||
} | |||
project.setUserProperty("ant.file" , buildFile.getAbsolutePath() ); | |||
// first use the ProjectHelper to create the project object | |||
// from the given build file. | |||
try { | |||
Class.forName("javax.xml.parsers.SAXParserFactory"); | |||
ProjectHelper.configureProject(project, buildFile); | |||
} catch (NoClassDefFoundError ncdfe) { | |||
throw new BuildException("No JAXP compliant XML parser found. See http://java.sun.com/xml for the\nreference implementation.", ncdfe); | |||
} catch (ClassNotFoundException cnfe) { | |||
throw new BuildException("No JAXP compliant XML parser found. See http://java.sun.com/xml for the\nreference implementation.", cnfe); | |||
} catch (NullPointerException npe) { | |||
throw new BuildException("No JAXP compliant XML parser found. See http://java.sun.com/xml for the\nreference implementation.", npe); | |||
} | |||
// make sure that we have a target to execute | |||
if (targets.size() == 0) { | |||
targets.addElement(project.getDefaultTarget()); | |||
} | |||
if (!projectHelp) { | |||
project.executeTargets(targets); | |||
} | |||
} | |||
// make sure that we have a target to execute | |||
if (targets.size() == 0) { | |||
targets.addElement(project.getDefaultTarget()); | |||
finally { | |||
System.setOut(out); | |||
System.setErr(err); | |||
} | |||
if (projectHelp) { | |||
printTargets(project); | |||
} else { | |||
// actually do some work | |||
project.executeTargets(targets); | |||
printTargets(project); | |||
} | |||
} | |||
catch(RuntimeException exc) { | |||
@@ -113,6 +113,9 @@ public class Project { | |||
/** The system classloader - may be null */ | |||
private ClassLoader systemLoader = null; | |||
/** Records the latest task on a thread */ | |||
private Hashtable threadTasks = new Hashtable(); | |||
static { | |||
@@ -525,6 +528,21 @@ public class Project { | |||
} | |||
} | |||
public void demuxOutput(String line, boolean isError) { | |||
Task task = (Task)threadTasks.get(Thread.currentThread()); | |||
if (task == null) { | |||
fireMessageLogged(this, line, isError ? MSG_ERR : MSG_INFO); | |||
} | |||
else { | |||
if (isError) { | |||
task.handleOutput(line); | |||
} | |||
else { | |||
task.handleErrorOutput(line); | |||
} | |||
} | |||
} | |||
public void executeTarget(String targetName) throws BuildException { | |||
// sanity check ourselves, if we've been asked to build nothing | |||
@@ -547,7 +565,7 @@ public class Project { | |||
do { | |||
curtarget = (Target) sortedTargets.elementAt(curidx++); | |||
runTarget(curtarget); | |||
curtarget.performTasks(); | |||
} while (!curtarget.getName().equals(targetName)); | |||
} | |||
@@ -903,23 +921,6 @@ public class Project { | |||
s.equalsIgnoreCase("yes")); | |||
} | |||
// Given a string defining a target name, and a Hashtable | |||
// containing the "name to Target" mapping, pick out the | |||
// Target and execute it. | |||
public void runTarget(Target target) | |||
throws BuildException { | |||
try { | |||
fireTargetStarted(target); | |||
target.execute(); | |||
fireTargetFinished(target, null); | |||
} | |||
catch(RuntimeException exc) { | |||
fireTargetFinished(target, exc); | |||
throw exc; | |||
} | |||
} | |||
/** | |||
* Topologically sort a set of Targets. | |||
* @param root is the (String) name of the root Target. The sort is | |||
@@ -1084,6 +1085,8 @@ public class Project { | |||
} | |||
protected void fireTaskStarted(Task task) { | |||
// register this as the current task on the current thread. | |||
threadTasks.put(Thread.currentThread(), task); | |||
BuildEvent event = new BuildEvent(task); | |||
for (int i = 0; i < listeners.size(); i++) { | |||
BuildListener listener = (BuildListener) listeners.elementAt(i); | |||
@@ -1092,6 +1095,9 @@ public class Project { | |||
} | |||
protected void fireTaskFinished(Task task, Throwable exception) { | |||
threadTasks.remove(Thread.currentThread()); | |||
System.out.flush(); | |||
System.err.flush(); | |||
BuildEvent event = new BuildEvent(task); | |||
for (int i = 0; i < listeners.size(); i++) { | |||
BuildListener listener = (BuildListener) listeners.elementAt(i); | |||
@@ -348,11 +348,11 @@ public class ProjectHelper { | |||
} | |||
private void handleTaskdef(String name, AttributeList attrs) throws SAXParseException { | |||
(new TaskHandler(this, null)).init(name, attrs); | |||
(new TaskHandler(this, null, null)).init(name, attrs); | |||
} | |||
private void handleProperty(String name, AttributeList attrs) throws SAXParseException { | |||
(new TaskHandler(this, null)).init(name, attrs); | |||
(new TaskHandler(this, null, null)).init(name, attrs); | |||
} | |||
private void handleTarget(String tag, AttributeList attrs) throws SAXParseException { | |||
@@ -433,7 +433,7 @@ public class ProjectHelper { | |||
if (project.getDataTypeDefinitions().get(name) != null) { | |||
new DataTypeHandler(this, target).init(name, attrs); | |||
} else { | |||
new TaskHandler(this, target).init(name, attrs); | |||
new TaskHandler(this, target, target).init(name, attrs); | |||
} | |||
} | |||
} | |||
@@ -443,12 +443,13 @@ public class ProjectHelper { | |||
*/ | |||
private class TaskHandler extends AbstractHandler { | |||
private Target target; | |||
private TaskContainer container; | |||
private Task task; | |||
private RuntimeConfigurable wrapper = null; | |||
public TaskHandler(DocumentHandler parentHandler, Target target) { | |||
public TaskHandler(DocumentHandler parentHandler, TaskContainer container, Target target) { | |||
super(parentHandler); | |||
this.container = container; | |||
this.target = target; | |||
} | |||
@@ -471,7 +472,7 @@ public class ProjectHelper { | |||
// Top level tasks don't have associated targets | |||
if (target != null) { | |||
task.setOwningTarget(target); | |||
target.addTask(task); | |||
container.addTask(task); | |||
task.init(); | |||
wrapper = task.getRuntimeConfigurableWrapper(); | |||
wrapper.setAttributes(attrs); | |||
@@ -500,7 +501,13 @@ public class ProjectHelper { | |||
} | |||
public void startElement(String name, AttributeList attrs) throws SAXParseException { | |||
new NestedElementHandler(this, task, wrapper).init(name, attrs); | |||
if (task instanceof TaskContainer) { | |||
// task can contain other tasks - no other nested elements possible | |||
new TaskHandler(this, (TaskContainer)task, target).init(name, attrs); | |||
} | |||
else { | |||
new NestedElementHandler(this, task, wrapper, target).init(name, attrs); | |||
} | |||
} | |||
} | |||
@@ -508,35 +515,38 @@ public class ProjectHelper { | |||
* Handler for all nested properties. | |||
*/ | |||
private class NestedElementHandler extends AbstractHandler { | |||
private Object target; | |||
private Object parent; | |||
private Object child; | |||
private RuntimeConfigurable parentWrapper; | |||
private RuntimeConfigurable childWrapper = null; | |||
private Target target; | |||
public NestedElementHandler(DocumentHandler parentHandler, | |||
Object target, | |||
RuntimeConfigurable parentWrapper) { | |||
Object parent, | |||
RuntimeConfigurable parentWrapper, | |||
Target target) { | |||
super(parentHandler); | |||
if (target instanceof TaskAdapter) { | |||
this.target = ((TaskAdapter) target).getProxy(); | |||
if (parent instanceof TaskAdapter) { | |||
this.parent = ((TaskAdapter) parent).getProxy(); | |||
} else { | |||
this.target = target; | |||
this.parent = parent; | |||
} | |||
this.parentWrapper = parentWrapper; | |||
this.target = target; | |||
} | |||
public void init(String propType, AttributeList attrs) throws SAXParseException { | |||
Class targetClass = target.getClass(); | |||
Class parentClass = parent.getClass(); | |||
IntrospectionHelper ih = | |||
IntrospectionHelper.getHelper(targetClass); | |||
IntrospectionHelper.getHelper(parentClass); | |||
try { | |||
if (target instanceof UnknownElement) { | |||
if (parent instanceof UnknownElement) { | |||
child = new UnknownElement(propType.toLowerCase()); | |||
((UnknownElement) target).addChild((UnknownElement) child); | |||
((UnknownElement) parent).addChild((UnknownElement) child); | |||
} else { | |||
child = ih.createElement(project, target, propType.toLowerCase()); | |||
child = ih.createElement(project, parent, propType.toLowerCase()); | |||
} | |||
configureId(child, attrs); | |||
@@ -566,7 +576,14 @@ public class ProjectHelper { | |||
} | |||
public void startElement(String name, AttributeList attrs) throws SAXParseException { | |||
new NestedElementHandler(this, child, childWrapper).init(name, attrs); | |||
if (child instanceof TaskContainer) { | |||
// taskcontainer nested element can contain other tasks - no other | |||
// nested elements possible | |||
new TaskHandler(this, (TaskContainer)child, target).init(name, attrs); | |||
} | |||
else { | |||
new NestedElementHandler(this, child, childWrapper, target).init(name, attrs); | |||
} | |||
} | |||
} | |||
@@ -616,7 +633,7 @@ public class ProjectHelper { | |||
} | |||
public void startElement(String name, AttributeList attrs) throws SAXParseException { | |||
new NestedElementHandler(this, element, wrapper).init(name, attrs); | |||
new NestedElementHandler(this, element, wrapper, target).init(name, attrs); | |||
} | |||
} | |||
@@ -62,7 +62,7 @@ import java.util.*; | |||
* @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a> | |||
*/ | |||
public class Target { | |||
public class Target implements TaskContainer { | |||
private String name; | |||
private String ifCondition = ""; | |||
@@ -161,23 +161,7 @@ public class Target { | |||
Object o = enum.nextElement(); | |||
if (o instanceof Task) { | |||
Task task = (Task) o; | |||
try { | |||
project.fireTaskStarted(task); | |||
task.maybeConfigure(); | |||
task.execute(); | |||
project.fireTaskFinished(task, null); | |||
} | |||
catch(RuntimeException exc) { | |||
if (exc instanceof BuildException) { | |||
BuildException be = (BuildException) exc; | |||
if (be.getLocation() == Location.UNKNOWN_LOCATION) { | |||
be.setLocation(task.getLocation()); | |||
} | |||
} | |||
project.fireTaskFinished(task, exc); | |||
throw exc; | |||
} | |||
task.perform(); | |||
} else { | |||
RuntimeConfigurable r = (RuntimeConfigurable) o; | |||
r.maybeConfigure(project); | |||
@@ -192,6 +176,18 @@ public class Target { | |||
} | |||
} | |||
public final void performTasks() { | |||
try { | |||
project.fireTargetStarted(this); | |||
execute(); | |||
project.fireTargetFinished(this, null); | |||
} | |||
catch(RuntimeException exc) { | |||
project.fireTargetFinished(this, exc); | |||
throw exc; | |||
} | |||
} | |||
void replaceTask(UnknownElement el, Task t) { | |||
int index = -1; | |||
while ((index = children.indexOf(el)) >= 0) { | |||
@@ -220,5 +220,36 @@ public abstract class Task { | |||
wrapper.maybeConfigure(project); | |||
} | |||
} | |||
protected void handleOutput(String line) { | |||
log(line, Project.MSG_INFO); | |||
} | |||
protected void handleErrorOutput(String line) { | |||
log(line, Project.MSG_ERR); | |||
} | |||
/** | |||
* Perform this task | |||
*/ | |||
public final void perform() { | |||
try { | |||
project.fireTaskStarted(this); | |||
maybeConfigure(); | |||
execute(); | |||
project.fireTaskFinished(this, null); | |||
} | |||
catch(RuntimeException exc) { | |||
if (exc instanceof BuildException) { | |||
BuildException be = (BuildException) exc; | |||
if (be.getLocation() == Location.UNKNOWN_LOCATION) { | |||
be.setLocation(getLocation()); | |||
} | |||
} | |||
project.fireTaskFinished(this, exc); | |||
throw exc; | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,70 @@ | |||
/* | |||
* 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 | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant; | |||
/** | |||
* Interface for objects which can contain tasks | |||
* | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
*/ | |||
public interface TaskContainer { | |||
/** | |||
* Add a task to this task container | |||
* | |||
* @param task the task to be added to this container | |||
*/ | |||
void addTask(Task task); | |||
} | |||
@@ -76,7 +76,6 @@ public class ExecuteJava { | |||
private Commandline javaCommand = null; | |||
private Path classpath = null; | |||
private CommandlineJava.SysProperties sysProperties = null; | |||
private PrintStream out; | |||
public void setJavaCommand(Commandline javaCommand) { | |||
this.javaCommand = javaCommand; | |||
@@ -93,15 +92,13 @@ public class ExecuteJava { | |||
/** | |||
* All output (System.out as well as System.err) will be written | |||
* to this Stream. | |||
* | |||
* @deprecated manage output at the task level | |||
*/ | |||
public void setOutput(PrintStream out) { | |||
this.out = out; | |||
} | |||
public void execute(Project project) throws BuildException{ | |||
PrintStream sOut = System.out; | |||
PrintStream sErr = System.err; | |||
final String classname = javaCommand.getExecutable(); | |||
final Object[] argument = { javaCommand.getArguments() }; | |||
@@ -111,11 +108,6 @@ public class ExecuteJava { | |||
sysProperties.setSystem(); | |||
} | |||
if (out != null) { | |||
System.setErr(out); | |||
System.setOut(out); | |||
} | |||
final Class[] param = { Class.forName("[Ljava.lang.String;") }; | |||
Class target = null; | |||
if (classpath == null) { | |||
@@ -150,11 +142,6 @@ public class ExecuteJava { | |||
if (sysProperties != null) { | |||
sysProperties.restoreSystem(); | |||
} | |||
if (out != null) { | |||
System.setOut(sOut); | |||
System.setErr(sErr); | |||
out.close(); | |||
} | |||
} | |||
} | |||
} |
@@ -76,6 +76,7 @@ public class Java extends Task { | |||
private boolean fork = false; | |||
private File dir = null; | |||
private File out; | |||
private PrintStream outStream = null; | |||
private boolean failOnError = false; | |||
/** | |||
@@ -239,6 +240,24 @@ public class Java extends Task { | |||
} | |||
} | |||
protected void handleOutput(String line) { | |||
if (outStream != null) { | |||
outStream.println(line); | |||
} | |||
else { | |||
super.handleOutput(line); | |||
} | |||
} | |||
protected void handleErrorOutput(String line) { | |||
if (outStream != null) { | |||
outStream.println(line); | |||
} | |||
else { | |||
super.handleErrorOutput(line); | |||
} | |||
} | |||
/** | |||
* Executes the given classname with the given arguments as it | |||
* was a command line application. | |||
@@ -250,13 +269,20 @@ public class Java extends Task { | |||
exe.setSystemProperties(command.getSystemProperties()); | |||
if (out != null) { | |||
try { | |||
exe.setOutput(new PrintStream(new FileOutputStream(out))); | |||
outStream = new PrintStream(new FileOutputStream(out)); | |||
exe.execute(project); | |||
} catch (IOException io) { | |||
throw new BuildException(io, location); | |||
} | |||
finally { | |||
if (outStream != null) { | |||
outStream.close(); | |||
} | |||
} | |||
} | |||
else { | |||
exe.execute(project); | |||
} | |||
exe.execute(project); | |||
} | |||
/** | |||
@@ -0,0 +1,179 @@ | |||
/* | |||
* 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 | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs; | |||
import org.apache.tools.ant.*; | |||
import org.apache.tools.ant.types.*; | |||
import java.util.*; | |||
import java.text.*; | |||
import java.lang.RuntimeException; | |||
/** | |||
* Implements a multi threaded task execution. | |||
* <p> | |||
* @author Thomas Christen <a href="mailto:chr@active.ch">chr@active.ch</a> | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill </a> | |||
*/ | |||
public class Parallel extends Task | |||
implements TaskContainer { | |||
/** Collection holding the nested tasks */ | |||
private Vector nestedTasks = new Vector(); | |||
/** | |||
* Add a nested task to execute parallel (asynchron). | |||
* <p> | |||
* @param nestedTask Nested task to be executed in parallel | |||
*/ | |||
public void addTask(Task nestedTask) throws BuildException { | |||
nestedTasks.addElement(nestedTask); | |||
} | |||
/** | |||
* Block execution until the specified time or for a | |||
* specified amount of milliseconds and if defined, | |||
* execute the wait status. | |||
*/ | |||
public void execute() throws BuildException { | |||
TaskThread[] threads = new TaskThread[nestedTasks.size()]; | |||
int threadNumber = 0; | |||
for (Enumeration e = nestedTasks.elements(); e.hasMoreElements(); threadNumber++) { | |||
Task nestedTask = (Task)e.nextElement(); | |||
threads[threadNumber] = new TaskThread(threadNumber, nestedTask); | |||
} | |||
// now start all threads | |||
for (int i = 0; i < threads.length; ++i) { | |||
threads[i].start(); | |||
} | |||
// now join to all the threads | |||
for (int i = 0; i < threads.length; ++i) { | |||
try { | |||
threads[i].join(); | |||
} | |||
catch (InterruptedException ie) { | |||
// who would interrupt me at a time like this? | |||
} | |||
} | |||
// now did any of the threads throw an exception | |||
StringBuffer exceptionMessage = new StringBuffer(); | |||
String lSep = System.getProperty("line.separator"); | |||
int numExceptions = 0; | |||
Throwable firstException = null; | |||
Location firstLocation = Location.UNKNOWN_LOCATION;; | |||
for (int i = 0; i < threads.length; ++i) { | |||
Throwable t = threads[i].getException(); | |||
if (t != null) { | |||
numExceptions++; | |||
if (firstException == null) { | |||
firstException = t; | |||
} | |||
if (t instanceof BuildException && | |||
firstLocation == Location.UNKNOWN_LOCATION) { | |||
firstLocation = ((BuildException)t).getLocation(); | |||
} | |||
exceptionMessage.append(lSep); | |||
exceptionMessage.append(t.getMessage()); | |||
} | |||
} | |||
if (numExceptions == 1) { | |||
if (firstException instanceof BuildException) { | |||
throw (BuildException)firstException; | |||
} | |||
else { | |||
throw new BuildException(firstException); | |||
} | |||
} | |||
else if (numExceptions > 1) { | |||
throw new BuildException(exceptionMessage.toString(), firstLocation); | |||
} | |||
} | |||
class TaskThread extends Thread { | |||
private Throwable exception; | |||
private Task task; | |||
private int taskNumber; | |||
/** | |||
* Construct a new TaskThread<p> | |||
* | |||
* @param task the Task to be executed in a seperate thread | |||
*/ | |||
TaskThread(int taskNumber, Task task) { | |||
this.task = task; | |||
this.taskNumber = taskNumber; | |||
} | |||
/** | |||
* Executes the task within a thread and takes care about | |||
* Exceptions raised within the task. | |||
*/ | |||
public void run() { | |||
try { | |||
task.perform(); | |||
} | |||
catch (Throwable t) { | |||
exception = t; | |||
} | |||
} | |||
public Throwable getException() { | |||
return exception; | |||
} | |||
} | |||
} |
@@ -0,0 +1,92 @@ | |||
/* | |||
* 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 | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant.taskdefs; | |||
import org.apache.tools.ant.*; | |||
import org.apache.tools.ant.types.*; | |||
import java.util.*; | |||
import java.text.*; | |||
import java.lang.RuntimeException; | |||
/** | |||
* Implements a single threaded task execution. | |||
* <p> | |||
* @author Thomas Christen <a href="mailto:chr@active.ch">chr@active.ch</a> | |||
*/ | |||
public class Sequential extends Task | |||
implements TaskContainer { | |||
/** Optional Vector holding the nested tasks */ | |||
private Vector nestedTasks = new Vector(); | |||
/** | |||
* Add a nested task to Sequential. | |||
* <p> | |||
* @param nestedTask Nested task to execute Sequential | |||
* <p> | |||
*/ | |||
public void addTask(Task nestedTask) { | |||
nestedTasks.addElement(nestedTask); | |||
} | |||
/** | |||
* Execute all nestedTasks. | |||
*/ | |||
public void execute() throws BuildException { | |||
for (Enumeration e = nestedTasks.elements(); e.hasMoreElements();) { | |||
Task nestedTask = (Task)e.nextElement(); | |||
nestedTask.perform(); | |||
} | |||
} | |||
} |
@@ -83,15 +83,8 @@ public class Javac13 extends DefaultCompilerAdapter { | |||
attributes.log("Using modern compiler", Project.MSG_VERBOSE); | |||
Commandline cmd = setupJavacCommand(); | |||
PrintStream err = System.err; | |||
PrintStream out = System.out; | |||
PrintStream logstr = | |||
new PrintStream(new LogOutputStream(attributes, Project.MSG_WARN)); | |||
// Use reflection to be able to build on all JDKs >= 1.1: | |||
try { | |||
System.setOut(logstr); | |||
System.setErr(logstr); | |||
Class c = Class.forName ("com.sun.tools.javac.Main"); | |||
Object compiler = c.newInstance (); | |||
Method compile = c.getMethod ("compile", | |||
@@ -105,10 +98,6 @@ public class Javac13 extends DefaultCompilerAdapter { | |||
} else { | |||
throw new BuildException("Error starting modern compiler", ex, location); | |||
} | |||
} finally { | |||
System.setErr(err); | |||
System.setOut(out); | |||
logstr.close(); | |||
} | |||
} | |||
} |
@@ -49,6 +49,8 @@ typedef=org.apache.tools.ant.taskdefs.Typedef | |||
sleep=org.apache.tools.ant.taskdefs.Sleep | |||
pathconvert=org.apache.tools.ant.taskdefs.PathConvert | |||
ear=org.apache.tools.ant.taskdefs.Ear | |||
parallel=org.apache.tools.ant.taskdefs.Parallel | |||
sequential=org.apache.tools.ant.taskdefs.Sequential | |||
# optional tasks | |||
script=org.apache.tools.ant.taskdefs.optional.Script | |||
@@ -308,19 +308,12 @@ public class Javah extends Task { | |||
throw new BuildException("Compile failed"); | |||
} | |||
*/ | |||
PrintStream err = System.err; | |||
PrintStream out = System.out; | |||
PrintStream logstr = | |||
new PrintStream(new LogOutputStream(this, Project.MSG_WARN)); | |||
try { | |||
// Javac uses logstr to change the output stream and calls | |||
// the constructor's invoke method to create a compiler instance | |||
// dynamically. However, javah has a different interface and this | |||
// makes it harder, so here's a simple alternative. | |||
//------------------------------------------------------------------ | |||
System.setOut(logstr); | |||
System.setErr(logstr); | |||
com.sun.tools.javah.Main main = new com.sun.tools.javah.Main( cmd.getArguments() ); | |||
main.run(); | |||
} | |||
@@ -335,10 +328,6 @@ public class Javah extends Task { | |||
} else { | |||
throw new BuildException("Error starting javah: ", ex, location); | |||
} | |||
} finally { | |||
System.setErr(err); | |||
System.setOut(out); | |||
logstr.close(); | |||
} | |||
} | |||
@@ -155,7 +155,8 @@ public class JUnitTask extends Task { | |||
private Integer timeout = null; | |||
private boolean summary = false; | |||
private String summaryValue = ""; | |||
private JUnitTestRunner runner = null; | |||
/** | |||
* Tells this task to halt when there is an error in a test. | |||
* this property is applied on all BatchTest (batchtest) and JUnitTest (test) | |||
@@ -509,6 +510,25 @@ public class JUnitTask extends Task { | |||
// whole build. IMHO this method should be avoided and it would be best | |||
// to remove it in future versions. TBD. (SBa) | |||
protected void handleOutput(String line) { | |||
if (runner != null) { | |||
runner.handleOutput(line); | |||
} | |||
else { | |||
super.handleOutput(line); | |||
} | |||
} | |||
protected void handleErrorOutput(String line) { | |||
if (runner != null) { | |||
runner.handleErrorOutput(line); | |||
} | |||
else { | |||
super.handleErrorOutput(line); | |||
} | |||
} | |||
/** | |||
* Execute inside VM. | |||
*/ | |||
@@ -535,7 +555,7 @@ public class JUnitTask extends Task { | |||
// will cause trouble in JDK 1.1 if omitted | |||
cl.addSystemPackageRoot("org.apache.tools.ant"); | |||
} | |||
JUnitTestRunner runner = new JUnitTestRunner(test, test.getHaltonerror(), test.getHaltonfailure(), cl); | |||
runner = new JUnitTestRunner(test, test.getHaltonerror(), test.getHaltonfailure(), cl); | |||
if (summary) { | |||
log("Running " + test.getName(), Project.MSG_INFO); | |||
@@ -138,6 +138,12 @@ public class JUnitTestRunner implements TestListener { | |||
*/ | |||
private JUnitTest junitTest; | |||
/** output written during the test */ | |||
private PrintStream systemError; | |||
/** Error output during the test */ | |||
private PrintStream systemOut; | |||
/** | |||
* Constructor for fork=true or when the user hasn't specified a | |||
* classpath. | |||
@@ -212,22 +218,19 @@ public class JUnitTestRunner implements TestListener { | |||
} else { | |||
PrintStream oldErr = System.err; | |||
PrintStream oldOut = System.out; | |||
ByteArrayOutputStream errStrm = new ByteArrayOutputStream(); | |||
System.setErr(new PrintStream(errStrm)); | |||
systemError = new PrintStream(errStrm); | |||
ByteArrayOutputStream outStrm = new ByteArrayOutputStream(); | |||
System.setOut(new PrintStream(outStrm)); | |||
systemOut = new PrintStream(outStrm); | |||
try { | |||
suite.run(res); | |||
} finally { | |||
System.err.close(); | |||
System.out.close(); | |||
System.setErr(oldErr); | |||
System.setOut(oldOut); | |||
systemError.close(); | |||
systemError = null; | |||
systemOut.close(); | |||
systemOut = null; | |||
sendOutAndErr(new String(outStrm.toByteArray()), | |||
new String(errStrm.toByteArray())); | |||
@@ -299,6 +302,18 @@ public class JUnitTestRunner implements TestListener { | |||
} | |||
} | |||
protected void handleOutput(String line) { | |||
if (systemOut != null) { | |||
systemOut.println(line); | |||
} | |||
} | |||
protected void handleErrorOutput(String line) { | |||
if (systemError != null) { | |||
systemError.println(line); | |||
} | |||
} | |||
private void sendOutAndErr(String out, String err) { | |||
for (int i=0; i<formatters.size(); i++) { | |||
JUnitResultFormatter formatter = | |||
@@ -74,15 +74,7 @@ public class KaffeRmic extends DefaultRmicAdapter { | |||
getRmic().log("Using Kaffe rmic", Project.MSG_VERBOSE); | |||
Commandline cmd = setupRmicCommand(); | |||
PrintStream err = System.err; | |||
PrintStream out = System.out; | |||
// the project log | |||
PrintStream logstr = | |||
new PrintStream(new LogOutputStream(getRmic(), Project.MSG_WARN)); | |||
try { | |||
System.setOut(logstr); | |||
System.setErr(logstr); | |||
Class c = Class.forName("kaffe.rmi.rmic.RMIC"); | |||
Constructor cons = c.getConstructor(new Class[] { String[].class }); | |||
@@ -103,10 +95,6 @@ public class KaffeRmic extends DefaultRmicAdapter { | |||
} else { | |||
throw new BuildException("Error starting Kaffe rmic: ", ex, getRmic().getLocation()); | |||
} | |||
} finally { | |||
System.setErr(err); | |||
System.setOut(out); | |||
logstr.close(); | |||
} | |||
} | |||
} |
@@ -75,15 +75,7 @@ public class WLRmic extends DefaultRmicAdapter { | |||
getRmic().log("Using WebLogic rmic", Project.MSG_VERBOSE); | |||
Commandline cmd = setupRmicCommand(new String[] {"-noexit"}); | |||
PrintStream err = System.err; | |||
PrintStream out = System.out; | |||
PrintStream logstr = | |||
new PrintStream(new LogOutputStream(getRmic(), Project.MSG_WARN)); | |||
try { | |||
System.setOut(logstr); | |||
System.setErr(logstr); | |||
// Create an instance of the rmic | |||
Class c = Class.forName("weblogic.rmic"); | |||
Method doRmic = c.getMethod("main", | |||
@@ -101,10 +93,6 @@ public class WLRmic extends DefaultRmicAdapter { | |||
} else { | |||
throw new BuildException("Error starting WebLogic rmic: ", ex, getRmic().getLocation()); | |||
} | |||
} finally { | |||
System.setErr(err); | |||
System.setOut(out); | |||
logstr.close(); | |||
} | |||
} | |||