From 059ad359164e376960bc91ee4bce1e47abb3ef45 Mon Sep 17 00:00:00 2001 From: Conor MacNeill Date: Sun, 22 Jul 2001 13:12:29 +0000 Subject: [PATCH] This is a major change. :-) It introduces the concept of a TaskContainer to allow a task to contain other tasks. This allows Task composition It introduces a task for multithreading support. There is also a 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 git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@269371 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/tools/ant/DemuxOutputStream.java | 151 +++++++++++++++ src/main/org/apache/tools/ant/Main.java | 86 +++++---- src/main/org/apache/tools/ant/Project.java | 42 ++-- .../org/apache/tools/ant/ProjectHelper.java | 57 ++++-- src/main/org/apache/tools/ant/Target.java | 32 ++-- src/main/org/apache/tools/ant/Task.java | 31 +++ .../org/apache/tools/ant/TaskContainer.java | 70 +++++++ .../tools/ant/taskdefs/ExecuteJava.java | 17 +- .../org/apache/tools/ant/taskdefs/Java.java | 32 +++- .../apache/tools/ant/taskdefs/Parallel.java | 179 ++++++++++++++++++ .../apache/tools/ant/taskdefs/Sequential.java | 92 +++++++++ .../tools/ant/taskdefs/compilers/Javac13.java | 11 -- .../tools/ant/taskdefs/defaults.properties | 2 + .../tools/ant/taskdefs/optional/Javah.java | 11 -- .../taskdefs/optional/junit/JUnitTask.java | 24 ++- .../optional/junit/JUnitTestRunner.java | 33 +++- .../tools/ant/taskdefs/rmic/KaffeRmic.java | 12 -- .../tools/ant/taskdefs/rmic/WLRmic.java | 12 -- 18 files changed, 729 insertions(+), 165 deletions(-) create mode 100644 src/main/org/apache/tools/ant/DemuxOutputStream.java create mode 100644 src/main/org/apache/tools/ant/TaskContainer.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/Parallel.java create mode 100644 src/main/org/apache/tools/ant/taskdefs/Sequential.java diff --git a/src/main/org/apache/tools/ant/DemuxOutputStream.java b/src/main/org/apache/tools/ant/DemuxOutputStream.java new file mode 100644 index 000000000..8568e7ff1 --- /dev/null +++ b/src/main/org/apache/tools/ant/DemuxOutputStream.java @@ -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 + * . + */ + +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 processLine + */ + 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(); + } + } +} diff --git a/src/main/org/apache/tools/ant/Main.java b/src/main/org/apache/tools/ant/Main.java index d8e186fae..37a239c9f 100644 --- a/src/main/org/apache/tools/ant/Main.java +++ b/src/main/org/apache/tools/ant/Main.java @@ -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) { diff --git a/src/main/org/apache/tools/ant/Project.java b/src/main/org/apache/tools/ant/Project.java index fc2aff151..30d37583d 100644 --- a/src/main/org/apache/tools/ant/Project.java +++ b/src/main/org/apache/tools/ant/Project.java @@ -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); diff --git a/src/main/org/apache/tools/ant/ProjectHelper.java b/src/main/org/apache/tools/ant/ProjectHelper.java index 62bfb2b50..6e87e0b57 100644 --- a/src/main/org/apache/tools/ant/ProjectHelper.java +++ b/src/main/org/apache/tools/ant/ProjectHelper.java @@ -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); } } diff --git a/src/main/org/apache/tools/ant/Target.java b/src/main/org/apache/tools/ant/Target.java index 35d884c2b..36a28ffd4 100644 --- a/src/main/org/apache/tools/ant/Target.java +++ b/src/main/org/apache/tools/ant/Target.java @@ -62,7 +62,7 @@ import java.util.*; * @author James Davidson duncan@x180.com */ -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) { diff --git a/src/main/org/apache/tools/ant/Task.java b/src/main/org/apache/tools/ant/Task.java index 9e4b1f122..11d348865 100644 --- a/src/main/org/apache/tools/ant/Task.java +++ b/src/main/org/apache/tools/ant/Task.java @@ -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; + } + } } diff --git a/src/main/org/apache/tools/ant/TaskContainer.java b/src/main/org/apache/tools/ant/TaskContainer.java new file mode 100644 index 000000000..87ca7dadd --- /dev/null +++ b/src/main/org/apache/tools/ant/TaskContainer.java @@ -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 + * . + */ + +package org.apache.tools.ant; + +/** + * Interface for objects which can contain tasks + * + * @author Conor MacNeill + */ +public interface TaskContainer { + /** + * Add a task to this task container + * + * @param task the task to be added to this container + */ + void addTask(Task task); +} + diff --git a/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java b/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java index 3133bb9a7..144d324e2 100644 --- a/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java +++ b/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java @@ -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(); - } } } } diff --git a/src/main/org/apache/tools/ant/taskdefs/Java.java b/src/main/org/apache/tools/ant/taskdefs/Java.java index 5bab1d8a2..d407fed4f 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Java.java +++ b/src/main/org/apache/tools/ant/taskdefs/Java.java @@ -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); } /** diff --git a/src/main/org/apache/tools/ant/taskdefs/Parallel.java b/src/main/org/apache/tools/ant/taskdefs/Parallel.java new file mode 100644 index 000000000..6c83b4ebf --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/Parallel.java @@ -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 + * . + */ +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. + *

+ * @author Thomas Christen chr@active.ch + * @author Conor MacNeill + */ +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). + *

+ * @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

+ * + * @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; + } + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/Sequential.java b/src/main/org/apache/tools/ant/taskdefs/Sequential.java new file mode 100644 index 000000000..cb663f4a7 --- /dev/null +++ b/src/main/org/apache/tools/ant/taskdefs/Sequential.java @@ -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 + * . + */ +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. + *

+ * @author Thomas Christen chr@active.ch + */ +public class Sequential extends Task + implements TaskContainer { + + /** Optional Vector holding the nested tasks */ + private Vector nestedTasks = new Vector(); + + /** + * Add a nested task to Sequential. + *

+ * @param nestedTask Nested task to execute Sequential + *

+ */ + 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(); + } + } +} diff --git a/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java b/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java index d0724511e..34641bf72 100644 --- a/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java +++ b/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java @@ -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(); } } } diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties index 1a2453597..979219625 100644 --- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties +++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties @@ -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 diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java b/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java index f8834ec41..ee32e580a 100755 --- a/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java @@ -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(); } } diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java index 483ab368d..b8872eec1 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java @@ -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); diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java index fd0f52c31..3021a7172 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java @@ -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