From 4a9153f9f2f1c19286f5e2cb6d203ecd4a890103 Mon Sep 17 00:00:00 2001 From: Conor MacNeill Date: Sun, 9 Feb 2003 07:59:53 +0000 Subject: [PATCH] Input handling framework Non-forked Java tasks can now have their input redirected. Note that it would be possible to add a noninteractive flag to Ant preventing any input from System.in in any java classes druing a build. Would prevent locking up waiting for input git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274021 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/tools/ant/DemuxInputStream.java | 100 +++++++++ src/main/org/apache/tools/ant/Main.java | 15 +- src/main/org/apache/tools/ant/Project.java | 194 +++++++++++++----- src/main/org/apache/tools/ant/Task.java | 20 ++ .../org/apache/tools/ant/UnknownElement.java | 41 +++- .../org/apache/tools/ant/taskdefs/Ant.java | 15 +- .../apache/tools/ant/taskdefs/CallTarget.java | 10 + .../org/apache/tools/ant/taskdefs/Java.java | 9 + .../apache/tools/ant/taskdefs/Redirector.java | 152 +++++++++++++- .../taskdefs/optional/junit/JUnitTask.java | 15 ++ .../optional/junit/JUnitTestRunner.java | 5 + .../tools/ant/taskdefs/DemuxOutputTask.java | 3 +- 12 files changed, 498 insertions(+), 81 deletions(-) create mode 100644 src/main/org/apache/tools/ant/DemuxInputStream.java diff --git a/src/main/org/apache/tools/ant/DemuxInputStream.java b/src/main/org/apache/tools/ant/DemuxInputStream.java new file mode 100644 index 000000000..a0ffe24ae --- /dev/null +++ b/src/main/org/apache/tools/ant/DemuxInputStream.java @@ -0,0 +1,100 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2003 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.IOException; +import java.io.InputStream; + +/** + * + * Passes input requests tot he project objetc for demuxing into + * individual tasks and threads. + * + * @since Ant 1.6 + * @author Conor MacNeill + */ +public class DemuxInputStream extends InputStream { + + /** + * The project to from which to get input. + */ + private Project project; + + /** + * Create a DemuxInputStream for the given project + * + * @param project the project instance + */ + public DemuxInputStream(Project project) { + this.project = project; + } + + /** + * @see InputStream.read() + */ + public int read() throws IOException { + byte[] buffer = new byte[1]; + project.demuxInput(buffer, 0, 1); + return buffer[0]; + } + + + /** + * @see InputStream.read(byte[], int, int) + */ + public int read(byte[] buffer, int offset, int length) throws IOException { + return project.demuxInput(buffer, offset, length); + } +} diff --git a/src/main/org/apache/tools/ant/Main.java b/src/main/org/apache/tools/ant/Main.java index 62db4c238..57ab31eb9 100644 --- a/src/main/org/apache/tools/ant/Main.java +++ b/src/main/org/apache/tools/ant/Main.java @@ -572,6 +572,8 @@ public class Main { //System.setSecurityManager(new NoExitSecurityManager()); } try { + project.setDefaultInputStream(System.in); + System.setIn(new DemuxInputStream(project)); System.setOut(new PrintStream(new DemuxOutputStream(project, false))); System.setErr(new PrintStream(new DemuxOutputStream(project, true))); @@ -662,7 +664,7 @@ public class Main { * @exception BuildException if a specified InputHandler * implementation could not be loaded. */ - private void addInputHandler(Project project) { + private void addInputHandler(Project project) throws BuildException { InputHandler handler = null; if (inputHandlerClassname == null) { handler = new DefaultInputHandler(); @@ -675,8 +677,7 @@ public class Main { + inputHandlerClassname + " does not implement the InputHandler interface"; throw new BuildException(msg); - } - catch (Exception e) { + } catch (Exception e) { String msg = "Unable to instantiate specified input handler " + "class " + inputHandlerClassname + " : " + e.getClass().getName(); @@ -866,8 +867,8 @@ public class Main { maxLength); //if there were no main targets, we list all subtargets //as it means nothing has a description - if(topNames.size()==0) { - printSubTargets=true; + if (topNames.size() == 0) { + printSubTargets = true; } if (printSubTargets) { printTargets(project, subNames, null, "Subtargets:", 0); @@ -918,8 +919,8 @@ public class Main { * position so they line up (so long as the names really * are shorter than this). */ - private static void printTargets(Project project,Vector names, - Vector descriptions,String heading, + private static void printTargets(Project project, Vector names, + Vector descriptions, String heading, int maxlen) { // now, start printing the targets and their descriptions String lSep = System.getProperty("line.separator"); diff --git a/src/main/org/apache/tools/ant/Project.java b/src/main/org/apache/tools/ant/Project.java index 0a9d42298..270666bf4 100644 --- a/src/main/org/apache/tools/ant/Project.java +++ b/src/main/org/apache/tools/ant/Project.java @@ -90,7 +90,6 @@ import org.apache.tools.ant.util.LazyHashtable; */ public class Project { - /** Message priority of "error". */ public static final int MSG_ERR = 0; /** Message priority of "warning". */ @@ -113,6 +112,13 @@ public class Project { */ private static final String VISITED = "VISITED"; + /** + * The class name of the Ant class loader to use for + * JDK 1.2 and above + */ + private static final String ANTCLASSLOADER_JDK12 + = "org.apache.tools.ant.loader.AntClassLoader2"; + /** * Version constant for Java 1.0 * @@ -204,15 +210,37 @@ public class Project { */ private InputHandler inputHandler = null; + /** + * The default input stream used to read any input + */ + private InputStream defaultInputStream = null; + /** * Sets the input handler + * + * @param handler the InputHandler instance to use for gathering input. */ public void setInputHandler(InputHandler handler) { inputHandler = handler; } + /** + * Set the default System input stream. Normally this stream is set to + * System.in. This inputStream is used when no task inptu redirection is + * being performed. + * + * @param defaultInputStream the default input stream to use when input + * is reuested. + */ + public void setDefaultInputStream(InputStream defaultInputStream) { + this.defaultInputStream = defaultInputStream; + } + /** * Retrieves the current input handler. + * + * @return the InputHandler instance currently in place for the project + * instance. */ public InputHandler getInputHandler() { return inputHandler; @@ -250,7 +278,7 @@ public class Project { } props.load(in); in.close(); - ((AntTaskTable)taskClassDefinitions).addDefinitions( props ); + ((AntTaskTable) taskClassDefinitions).addDefinitions(props); } catch (IOException ioe) { @@ -268,7 +296,7 @@ public class Project { props.load(in); in.close(); - ((AntTaskTable)dataClassDefinitions).addDefinitions(props); + ((AntTaskTable) dataClassDefinitions).addDefinitions(props); } catch (IOException ioe) { @@ -278,13 +306,18 @@ public class Project { setSystemProperties(); } + /** + * Factory method to create a class loader for loading classes + * + * @return an appropriate classloader + */ private AntClassLoader createClassLoader() { AntClassLoader loader = null; if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) { try { // 1.2+ - create advanced helper dynamically Class loaderClass - = Class.forName("org.apache.tools.ant.loader.AntClassLoader2"); + = Class.forName(ANTCLASSLOADER_JDK12); loader = (AntClassLoader) loaderClass.newInstance(); } catch (Exception e) { log("Unable to create Class Loader: " @@ -300,6 +333,14 @@ public class Project { return loader; } + /** + * Factory method to create a class loader for loading classes from + * a given path + * + * @param path the path from whcih clases are to be loaded. + * + * @return an appropriate classloader + */ public AntClassLoader createClassLoader(Path path) { AntClassLoader loader = createClassLoader(); loader.setClassPath(path); @@ -434,7 +475,8 @@ public class Project { * @since 1.5 */ public synchronized void setNewProperty(String name, String value) { - PropertyHelper.getPropertyHelper(this).setNewProperty( null, name, value); + PropertyHelper.getPropertyHelper(this).setNewProperty(null, name, + value); } /** @@ -447,7 +489,8 @@ public class Project { * @see #setProperty(String,String) */ public synchronized void setUserProperty(String name, String value) { - PropertyHelper.getPropertyHelper(this).setUserProperty( null, name, value); + PropertyHelper.getPropertyHelper(this).setUserProperty(null, name, + value); } /** @@ -463,7 +506,7 @@ public class Project { * @see #setProperty(String,String) */ public synchronized void setInheritedProperty(String name, String value) { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); ph.setInheritedProperty(null, name, value); } @@ -476,8 +519,8 @@ public class Project { * @param value The property value. Must not be null. */ private void setPropertyInternal(String name, String value) { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); - ph.setProperty(null, name, value, false ); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); + ph.setProperty(null, name, value, false); } /** @@ -490,8 +533,8 @@ public class Project { * or if a null name is provided. */ public String getProperty(String name) { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); - return (String)ph.getProperty(null, name); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); + return (String) ph.getProperty(null, name); } /** @@ -509,9 +552,8 @@ public class Project { * property name, e.g. ${xxx */ public String replaceProperties(String value) - throws BuildException - { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); + throws BuildException { + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); return ph.replaceProperties(null, value, null); } @@ -525,8 +567,8 @@ public class Project { * or if a null name is provided. */ public String getUserProperty(String name) { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); - return (String)ph.getUserProperty( null, name ); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); + return (String) ph.getUserProperty(null, name); } /** @@ -535,7 +577,7 @@ public class Project { * (including user properties). */ public Hashtable getProperties() { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); return ph.getProperties(); } @@ -544,7 +586,7 @@ public class Project { * @return a hashtable containing just the user properties */ public Hashtable getUserProperties() { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); return ph.getUserProperties(); } @@ -561,7 +603,7 @@ public class Project { * @since Ant 1.5 */ public void copyUserProperties(Project other) { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); ph.copyUserProperties(other); } @@ -578,7 +620,7 @@ public class Project { * @since Ant 1.5 */ public void copyInheritedProperties(Project other) { - PropertyHelper ph=PropertyHelper.getPropertyHelper(this); + PropertyHelper ph = PropertyHelper.getPropertyHelper(this); ph.copyInheritedProperties(other); } @@ -654,8 +696,8 @@ public class Project { * been set. */ public String getDescription() { - if( description== null ) { - description=Description.getDescription(this); + if (description == null) { + description = Description.getDescription(this); } return description; @@ -924,7 +966,7 @@ public class Project { * Must not be null. */ public void addDataTypeDefinition(String typeName, Class typeClass) { - synchronized(dataClassDefinitions) { + synchronized (dataClassDefinitions) { Class old = (Class) dataClassDefinitions.get(typeName); if (null != old) { if (old.equals(typeClass)) { @@ -1043,8 +1085,8 @@ public class Project { * creation fails. */ public Task createTask(String taskType) throws BuildException { - Task task=createNewTask(taskType); - if(task!=null) { + Task task = createNewTask(taskType); + if (task != null) { addCreatedTask(taskType, task); } return task; @@ -1132,11 +1174,11 @@ public class Project { if (v != null) { Enumeration enum = v.elements(); while (enum.hasMoreElements()) { - WeakishReference ref= + WeakishReference ref = (WeakishReference) enum.nextElement(); Task t = (Task) ref.get(); //being a weak ref, it may be null by this point - if(t!=null) { + if (t != null) { t.markInvalid(); } } @@ -1240,6 +1282,48 @@ public class Project { } } + /** + * Read data from the default input stream. If no default has been + * specified, System.in is used. + * + * @param buffer the buffer into which data is to be read. + * @param offset the offset into the buffer at which data is stored. + * @param length the amount of data to read + * + * @return the number of bytes read + * + * @exception IOException if the data cannot be read + */ + public int defaultInput(byte[] buffer, int offset, int length) + throws IOException { + if (defaultInputStream != null) { + return defaultInputStream.read(buffer, offset, length); + } else { + return System.in.read(buffer, offset, length); + } + } + + /** + * Demux an input request to the correct task. + * + * @param buffer the buffer into which data is to be read. + * @param offset the offset into the buffer at which data is stored. + * @param length the amount of data to read + * + * @return the number of bytes read + * + * @exception IOException if the data cannot be read + */ + public int demuxInput(byte[] buffer, int offset, int length) + throws IOException { + Task task = (Task) threadTasks.get(Thread.currentThread()); + if (task == null) { + return defaultInput(buffer, offset, length); + } else { + return task.handleInput(buffer, offset, length); + } + } + /** * Demultiplexes flush operation so that each task receives the appropriate * messages. If the current thread is not currently executing a task, @@ -1250,8 +1334,6 @@ public class Project { * @param line Message to handle. Should not be null. * @param isError Whether the text represents an error (true) * or information (false). - * @param terminated true if this line should be terminated with an - * end-of-line marker */ public void demuxFlush(String line, boolean isError) { Task task = (Task) threadTasks.get(Thread.currentThread()); @@ -1763,7 +1845,7 @@ public class Project { */ public void addReference(String name, Object value) { synchronized (references) { - Object old = ((AntRefTable)references).getReal(name); + Object old = ((AntRefTable) references).getReal(name); if (old == value) { // no warning, this is not changing anything return; @@ -1777,7 +1859,7 @@ public class Project { try { valueAsString = value.toString(); } catch (Throwable t) { - log("Caught exception (" + t.getClass().getName() +")" + log("Caught exception (" + t.getClass().getName() + ")" + " while expanding " + name + ": " + t.getMessage(), MSG_WARN); } @@ -2051,7 +2133,7 @@ public class Project { Project project; public AntRefTable(Project project) { super(); - this.project=project; + this.project = project; } /** Returns the unmodified original object. @@ -2061,8 +2143,8 @@ public class Project { * of UnknownElement ( this is similar with the JDNI * refs behavior ) */ - public Object getReal(Object key ) { - return super.get( key ); + public Object getReal(Object key) { + return super.get(key); } /** Get method for the reference table. @@ -2078,11 +2160,11 @@ public class Project { */ public Object get(Object key) { //System.out.println("AntRefTable.get " + key); - Object o=super.get(key); - if( o instanceof UnknownElement ) { + Object o = super.get(key); + if (o instanceof UnknownElement) { // Make sure that - ((UnknownElement)o).maybeConfigure(); - o=((UnknownElement)o).getTask(); + ((UnknownElement) o).maybeConfigure(); + o = ((UnknownElement) o).getTask(); } return o; } @@ -2091,28 +2173,28 @@ public class Project { private static class AntTaskTable extends LazyHashtable { Project project; Properties props; - boolean tasks=false; + boolean tasks = false; - public AntTaskTable( Project p, boolean tasks ) { - this.project=p; - this.tasks=tasks; + public AntTaskTable(Project p, boolean tasks) { + this.project = p; + this.tasks = tasks; } - public void addDefinitions( Properties props ) { - this.props=props; + public void addDefinitions(Properties props) { + this.props = props; } - protected void initAll( ) { - if( initAllDone ) return; + protected void initAll() { + if (initAllDone ) return; project.log("InitAll", Project.MSG_DEBUG); - if( props==null ) return; + if (props==null ) return; Enumeration enum = props.propertyNames(); while (enum.hasMoreElements()) { String key = (String) enum.nextElement(); Class taskClass=getTask( key ); - if( taskClass!=null ) { + if (taskClass!=null ) { // This will call a get() and a put() - if( tasks ) + if (tasks ) project.addTaskDefinition(key, taskClass); else project.addDataTypeDefinition(key, taskClass ); @@ -2122,19 +2204,19 @@ public class Project { } protected Class getTask(String key) { - if( props==null ) return null; // for tasks loaded before init() + if (props==null ) return null; // for tasks loaded before init() String value=props.getProperty(key); - if( value==null) { + if (value==null) { //project.log( "No class name for " + key, Project.MSG_VERBOSE ); return null; } try { Class taskClass=null; - if( project.getCoreLoader() != null && + if (project.getCoreLoader() != null && !("only".equals(project.getProperty("build.sysclasspath")))) { try { taskClass=project.getCoreLoader().loadClass(value); - if( taskClass != null ) return taskClass; + if (taskClass != null ) return taskClass; } catch( Exception ex ) { } } @@ -2153,11 +2235,11 @@ public class Project { // Hashtable implementation public Object get( Object key ) { Object orig=super.get( key ); - if( orig!= null ) return orig; - if( ! (key instanceof String) ) return null; + if (orig!= null ) return orig; + if (! (key instanceof String) ) return null; project.log("Get task " + key, Project.MSG_DEBUG ); Object taskClass=getTask( (String) key); - if( taskClass != null) + if (taskClass != null) super.put( key, taskClass ); return taskClass; } diff --git a/src/main/org/apache/tools/ant/Task.java b/src/main/org/apache/tools/ant/Task.java index ac2e9f4f9..f0c5e3dce 100644 --- a/src/main/org/apache/tools/ant/Task.java +++ b/src/main/org/apache/tools/ant/Task.java @@ -55,6 +55,7 @@ package org.apache.tools.ant; import java.util.Enumeration; +import java.io.IOException; /** * Base class for all tasks. @@ -312,6 +313,22 @@ public abstract class Task extends ProjectComponent { handleOutput(line); } + /** + * Handle an input request by this task + * + * @param buffer the buffer into which data is to be read. + * @param offset the offset into the buffer at which data is stored. + * @param length the amount of data to read + * + * @return the number of bytes read + * + * @exception IOException if the data cannot be read + */ + protected int handleInput(byte[] buffer, int offset, int length) + throws IOException { + return getProject().defaultInput(buffer, offset, length); + } + /** * Handles an error line by logging it with the INFO priority. * @@ -397,6 +414,9 @@ public abstract class Task extends ProjectComponent { /** * Has this task been marked invalid? * + * @return true if this task is no longer valid. A new task should be + * configured in this case. + * * @since Ant 1.5 */ protected final boolean isInvalid() { diff --git a/src/main/org/apache/tools/ant/UnknownElement.java b/src/main/org/apache/tools/ant/UnknownElement.java index f2bab4800..030e59c53 100644 --- a/src/main/org/apache/tools/ant/UnknownElement.java +++ b/src/main/org/apache/tools/ant/UnknownElement.java @@ -55,6 +55,7 @@ package org.apache.tools.ant; import java.util.Vector; +import java.io.IOException; /** * Wrapper class that holds all the information necessary to create a task @@ -124,7 +125,7 @@ public class UnknownElement extends Task { getWrapper().setProxy(realThing); if (realThing instanceof Task) { - Task task=(Task)realThing; + Task task = (Task) realThing; task.setProject(project); task.setRuntimeConfigurableWrapper(getWrapper()); @@ -135,7 +136,7 @@ public class UnknownElement extends Task { // For Script to work. Ugly // The reference is replaced by RuntimeConfigurable - this.getOwningTarget().replaceChild(this, (Task)realThing); + this.getOwningTarget().replaceChild(this, (Task) realThing); } handleChildren(realThing, getWrapper()); @@ -156,6 +157,26 @@ public class UnknownElement extends Task { } } + /** + * Handle an input request by this element + * + * @param buffer the buffer into which data is to be read. + * @param offset the offset into the buffer at which data is stored. + * @param length the amount of data to read + * + * @return the number of bytes read + * + * @exception IOException if the data cannot be read + */ + protected int handleInput(byte[] buffer, int offset, int length) + throws IOException { + if (realThing instanceof Task) { + return ((Task) realThing).handleInput(buffer, offset, length); + } else { + return super.handleInput(buffer, offset, length); + } + + } /** * Handles output sent to System.out by this task or its real task. * @@ -214,7 +235,7 @@ public class UnknownElement extends Task { // the task will not be reused ( a new init() will be called ) // Let GC do its job - realThing=null; + realThing = null; } /** @@ -241,8 +262,7 @@ public class UnknownElement extends Task { */ protected void handleChildren(Object parent, RuntimeConfigurable parentWrapper) - throws BuildException - { + throws BuildException { if (parent instanceof TaskAdapter) { parent = ((TaskAdapter) parent).getProxy(); } @@ -259,15 +279,15 @@ public class UnknownElement extends Task { //ProjectComponentHelper helper=ProjectComponentHelper.getProjectComponentHelper(); //realChild = helper.createProjectComponent( child, getProject(), null, // child.getTag()); - realChild=makeTask(child, childWrapper, false); + realChild = makeTask(child, childWrapper, false); - if (realChild == null ) { + if (realChild == null) { throw getNotFoundException("task", child.getTag()); } // XXX DataTypes will be wrapped or treated like normal components - if( realChild instanceof Task ) { - Task task=(Task)realChild; + if (realChild instanceof Task) { + Task task = (Task) realChild; ((TaskContainer) parent).addTask(task); task.setLocation(child.getLocation()); // UnknownElement always has an associated target @@ -277,7 +297,8 @@ public class UnknownElement extends Task { // What ? Add data type ? createElement ? } } else { - realChild = ih.createElement(getProject(), parent, child.getTag()); + realChild + = ih.createElement(getProject(), parent, child.getTag()); } childWrapper.setProxy(realChild); diff --git a/src/main/org/apache/tools/ant/taskdefs/Ant.java b/src/main/org/apache/tools/ant/taskdefs/Ant.java index 3851f8e52..ac69d4443 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Ant.java +++ b/src/main/org/apache/tools/ant/taskdefs/Ant.java @@ -296,6 +296,18 @@ public class Ant extends Task { } } + /** + * @see Task#handleInput(byte[], int, int) + */ + public int handleInput(byte[] buffer, int offset, int length) + throws IOException { + if (newProject != null) { + return newProject.demuxInput(buffer, offset, length); + } else { + return super.handleInput(buffer, offset, length); + } + } + /** * Pass output sent to System.out to the new project. * @@ -443,7 +455,8 @@ public class Ant extends Task { * requested. */ private void addReferences() throws BuildException { - Hashtable thisReferences = (Hashtable) getProject().getReferences().clone(); + Hashtable thisReferences + = (Hashtable) getProject().getReferences().clone(); Hashtable newReferences = newProject.getReferences(); Enumeration e; if (references.size() > 0) { diff --git a/src/main/org/apache/tools/ant/taskdefs/CallTarget.java b/src/main/org/apache/tools/ant/taskdefs/CallTarget.java index 4c5d2701e..5e1548d51 100644 --- a/src/main/org/apache/tools/ant/taskdefs/CallTarget.java +++ b/src/main/org/apache/tools/ant/taskdefs/CallTarget.java @@ -56,6 +56,7 @@ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; +import java.io.IOException; /** * Call another target in the same project. @@ -185,6 +186,15 @@ public class CallTarget extends Task { } } + public int handleInput(byte[] buffer, int offset, int length) + throws IOException { + if (callee != null) { + return callee.handleInput(buffer, offset, length); + } else { + return super.handleInput(buffer, offset, length); + } + } + /** * Pass output sent to System.out to the new project. * diff --git a/src/main/org/apache/tools/ant/taskdefs/Java.java b/src/main/org/apache/tools/ant/taskdefs/Java.java index 4a3df7af4..dafc714dc 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Java.java +++ b/src/main/org/apache/tools/ant/taskdefs/Java.java @@ -416,6 +416,15 @@ public class Java extends Task { } } + public int handleInput(byte[] buffer, int offset, int length) + throws IOException { + if (redirector.getInputStream() != null) { + return redirector.handleInput(buffer, offset, length); + } else { + return super.handleInput(buffer, offset, length); + } + } + /** * Pass output sent to System.out to specified output file. * diff --git a/src/main/org/apache/tools/ant/taskdefs/Redirector.java b/src/main/org/apache/tools/ant/taskdefs/Redirector.java index e90aab107..4a394d07c 100644 --- a/src/main/org/apache/tools/ant/taskdefs/Redirector.java +++ b/src/main/org/apache/tools/ant/taskdefs/Redirector.java @@ -79,37 +79,92 @@ import org.apache.tools.ant.util.TeeOutputStream; * @since Ant 1.6 */ public class Redirector { + /** + * The file receiveing standard output. Will also receive standard error + * unless standard error is redirected or logError is true. + */ private File out; + + /** + * The file to which standard error is being redirected + */ private File error; + + /** + * The file from which standard input is being taken. + */ private File input; + /** + * Indicates if standard error should be logged to Ant's log system + * rather than the output. This has no effect if standard error is + * redirected to a file or property. + */ private boolean logError = false; + + /** + * Buffer used to capture output for storage into a property + */ private ByteArrayOutputStream baos = null; + + /** + * Buffer used to capture error output for storage into a property + */ private ByteArrayOutputStream errorBaos = null; + + /** The name of the property into which output is to be stored */ private String outputProperty; + + /** The name of the property into which error output is to be stored */ private String errorProperty; + + /** String from which input is taken */ private String inputString; + + /** Flag which indicates if error and output files are to be appended. */ private boolean append = false; + /** The task for which this redirector is working */ private Task managingTask; + /** The stream for output data */ private OutputStream outputStream = null; + + /** The stream for error output */ private OutputStream errorStream = null; - private InputStream inputStream = null; + + /** The stream for input */ + private InputStream inputStream = null; + + /** Stream which are used for line oriented output */ private PrintStream outPrintStream = null; + + /** Stream which is used for line oriented error output */ private PrintStream errorPrintStream = null; + /** + * Create a redirector instance for the given task + * + * @param managingTask the task for which the redirector is to work + */ public Redirector(Task managingTask) { this.managingTask = managingTask; } /** * Set the input to use for the task + * + * @param input the file from which input is read. */ public void setInput(File input) { this.input = input; } + /** + * Set the string to use as input + * + * @param inputString the string which is used as the input source + */ public void setInputString(String inputString) { this.inputString = inputString; } @@ -118,6 +173,8 @@ public class Redirector { /** * File the output of the process is redirected to. If error is not * redirected, it too will appear in the output + * + * @param out the file to which output stream is written */ public void setOutput(File out) { this.out = out; @@ -127,14 +184,18 @@ public class Redirector { * Controls whether error output of exec is logged. This is only useful * when output is being redirected and error output is desired in the * Ant log + * + * @param logError if true the standard error is sent to the Ant log system + * and not sent to output. */ public void setLogError(boolean logError) { this.logError = logError; } /** - * File the error stream of the process is redirected to. + * Set the file to which standard error is to be redirected. * + * @param error the file to which error is to be written */ public void setError(File error) { this.error = error; @@ -143,6 +204,9 @@ public class Redirector { /** * Property name whose value should be set to the output of * the process. + * + * @param outputProperty the name of the property to be set with the + * task's output. */ public void setOutputProperty(String outputProperty) { this.outputProperty = outputProperty; @@ -152,6 +216,8 @@ public class Redirector { * Whether output should be appended to or overwrite an existing file. * Defaults to false. * + * @param append if true output and error streams are appended to their + * respective files, if specified. */ public void setAppend(boolean append) { this.append = append; @@ -161,11 +227,21 @@ public class Redirector { * Property name whose value should be set to the error of * the process. * + * @param errorProperty the name of the property to be set + * with the error output. */ public void setErrorProperty(String errorProperty) { this.errorProperty = errorProperty; } + /** + * Set a property from a ByteArrayOutputStream + * + * @param baos contains the property value. + * @param propertyName the property name. + * + * @exception IOException if the value cannot be read form the stream. + */ private void setPropertyFromBAOS(ByteArrayOutputStream baos, String propertyName) throws IOException { @@ -183,6 +259,10 @@ public class Redirector { } + /** + * Create the input, error and output streams based on the + * configuration options. + */ public void createStreams() { if (out == null && outputProperty == null) { outputStream = new LogOutputStream(managingTask, Project.MSG_INFO); @@ -265,6 +345,11 @@ public class Redirector { /** * Create the StreamHandler to use with our Execute instance. + * + * @return the execute stream handler to manage the input, output and + * error streams. + * + * @throws BuildException if the execute stream handler cannot be created. */ public ExecuteStreamHandler createHandler() throws BuildException { createStreams(); @@ -272,8 +357,9 @@ public class Redirector { } /** - * Pass output sent to System.out to specified output file. + * Pass output sent to System.out to specified output. * + * @param line the data to be output */ protected void handleOutput(String line) { if (outPrintStream == null) { @@ -282,9 +368,31 @@ public class Redirector { outPrintStream.println(line); } + /** + * Handle an input request + * + * @param buffer the buffer into which data is to be read. + * @param offset the offset into the buffer at which data is stored. + * @param length the amount of data to read + * + * @return the number of bytes read + * + * @exception IOException if the data cannot be read + */ + protected int handleInput(byte[] buffer, int offset, int length) + throws IOException { + if (inputStream == null) { + return managingTask.getProject().defaultInput(buffer, offset, + length); + } else { + return inputStream.read(buffer, offset, length); + } + } + /** - * Pass output sent to System.out to specified output file. + * Process data due to a flush operation. * + * @param line the data being flushed. */ protected void handleFlush(String line) { if (outPrintStream == null) { @@ -294,8 +402,9 @@ public class Redirector { } /** - * Pass output sent to System.err to specified output file. + * Process error output * + * @param line the error output data. */ protected void handleErrorOutput(String line) { if (errorPrintStream == null) { @@ -305,8 +414,9 @@ public class Redirector { } /** - * Pass output sent to System.err to specified output file. + * Handle a flush operation on the error stream * + * @param line the error information being flushed. */ protected void handleErrorFlush(String line) { if (errorPrintStream == null) { @@ -315,15 +425,45 @@ public class Redirector { errorPrintStream.print(line); } + /** + * Get the output stream for the redirector + * + * @return the redirector's output stream or null if no output + * has been configured + */ public OutputStream getOutputStream() { return outputStream; } + /** + * Get the error stream for the redirector + * + * @return the redirector's error stream or null if no output + * has been configured + */ public OutputStream getErrorStream() { return errorStream; } + /** + * Get the input stream for the redirector + * + * @return the redirector's input stream or null if no output + * has been configured + */ + public InputStream getInputStream() { + return inputStream; + } + /** + * Complete redirection. + * + * This opertaion will close any streams and create any specified + * property values. + * + * @throws IOException if the outptu properties cannot be read from their + * output streams. + */ public void complete() throws IOException { System.out.flush(); System.err.flush(); 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 016784677..09025ef59 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 @@ -713,6 +713,21 @@ public class JUnitTask extends Task { } } + /** + * @see Task#handleInput(byte[], int, int) + * + * @since Ant 1.6 + */ + protected int handleInput(byte[] buffer, int offset, int length) + throws IOException { + if (runner != null) { + return runner.handleInput(buffer, offset, length); + } else { + return super.handleInput(buffer, offset, length); + } + } + + /** * Pass output sent to System.out to the TestRunner so it can * collect ot for the formatters. 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 03186713c..077ba48d7 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 @@ -410,6 +410,11 @@ public class JUnitTestRunner implements TestListener { } } + protected int handleInput(byte[] buffer, int offset, int length) + throws IOException { + return -1; + } + protected void handleErrorOutput(String line) { if (systemError != null) { systemError.println(line); diff --git a/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java b/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java index 68a7f33ac..3374390e6 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java @@ -56,9 +56,10 @@ 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 + * the output which it then checks. If the output does not match, an * exception is thrown * * @since 1.5