Browse Source

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
master
Conor MacNeill 22 years ago
parent
commit
4a9153f9f2
12 changed files with 498 additions and 81 deletions
  1. +100
    -0
      src/main/org/apache/tools/ant/DemuxInputStream.java
  2. +8
    -7
      src/main/org/apache/tools/ant/Main.java
  3. +138
    -56
      src/main/org/apache/tools/ant/Project.java
  4. +20
    -0
      src/main/org/apache/tools/ant/Task.java
  5. +31
    -10
      src/main/org/apache/tools/ant/UnknownElement.java
  6. +14
    -1
      src/main/org/apache/tools/ant/taskdefs/Ant.java
  7. +10
    -0
      src/main/org/apache/tools/ant/taskdefs/CallTarget.java
  8. +9
    -0
      src/main/org/apache/tools/ant/taskdefs/Java.java
  9. +146
    -6
      src/main/org/apache/tools/ant/taskdefs/Redirector.java
  10. +15
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
  11. +5
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
  12. +2
    -1
      src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java

+ 100
- 0
src/main/org/apache/tools/ant/DemuxInputStream.java View File

@@ -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
* <http://www.apache.org/>.
*/

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);
}
}

+ 8
- 7
src/main/org/apache/tools/ant/Main.java View File

@@ -572,6 +572,8 @@ public class Main {
//System.setSecurityManager(new NoExitSecurityManager()); //System.setSecurityManager(new NoExitSecurityManager());
} }
try { try {
project.setDefaultInputStream(System.in);
System.setIn(new DemuxInputStream(project));
System.setOut(new PrintStream(new DemuxOutputStream(project, false))); System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
System.setErr(new PrintStream(new DemuxOutputStream(project, true))); System.setErr(new PrintStream(new DemuxOutputStream(project, true)));


@@ -662,7 +664,7 @@ public class Main {
* @exception BuildException if a specified InputHandler * @exception BuildException if a specified InputHandler
* implementation could not be loaded. * implementation could not be loaded.
*/ */
private void addInputHandler(Project project) {
private void addInputHandler(Project project) throws BuildException {
InputHandler handler = null; InputHandler handler = null;
if (inputHandlerClassname == null) { if (inputHandlerClassname == null) {
handler = new DefaultInputHandler(); handler = new DefaultInputHandler();
@@ -675,8 +677,7 @@ public class Main {
+ inputHandlerClassname + inputHandlerClassname
+ " does not implement the InputHandler interface"; + " does not implement the InputHandler interface";
throw new BuildException(msg); throw new BuildException(msg);
}
catch (Exception e) {
} catch (Exception e) {
String msg = "Unable to instantiate specified input handler " String msg = "Unable to instantiate specified input handler "
+ "class " + inputHandlerClassname + " : " + "class " + inputHandlerClassname + " : "
+ e.getClass().getName(); + e.getClass().getName();
@@ -866,8 +867,8 @@ public class Main {
maxLength); maxLength);
//if there were no main targets, we list all subtargets //if there were no main targets, we list all subtargets
//as it means nothing has a description //as it means nothing has a description
if(topNames.size()==0) {
printSubTargets=true;
if (topNames.size() == 0) {
printSubTargets = true;
} }
if (printSubTargets) { if (printSubTargets) {
printTargets(project, subNames, null, "Subtargets:", 0); printTargets(project, subNames, null, "Subtargets:", 0);
@@ -918,8 +919,8 @@ public class Main {
* position so they line up (so long as the names really * position so they line up (so long as the names really
* <i>are</i> shorter than this). * <i>are</i> 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) { int maxlen) {
// now, start printing the targets and their descriptions // now, start printing the targets and their descriptions
String lSep = System.getProperty("line.separator"); String lSep = System.getProperty("line.separator");


+ 138
- 56
src/main/org/apache/tools/ant/Project.java View File

@@ -90,7 +90,6 @@ import org.apache.tools.ant.util.LazyHashtable;
*/ */


public class Project { public class Project {

/** Message priority of "error". */ /** Message priority of "error". */
public static final int MSG_ERR = 0; public static final int MSG_ERR = 0;
/** Message priority of "warning". */ /** Message priority of "warning". */
@@ -113,6 +112,13 @@ public class Project {
*/ */
private static final String VISITED = "VISITED"; 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 * Version constant for Java 1.0
* *
@@ -204,15 +210,37 @@ public class Project {
*/ */
private InputHandler inputHandler = null; private InputHandler inputHandler = null;


/**
* The default input stream used to read any input
*/
private InputStream defaultInputStream = null;
/** /**
* Sets the input handler * Sets the input handler
*
* @param handler the InputHandler instance to use for gathering input.
*/ */
public void setInputHandler(InputHandler handler) { public void setInputHandler(InputHandler handler) {
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. * Retrieves the current input handler.
*
* @return the InputHandler instance currently in place for the project
* instance.
*/ */
public InputHandler getInputHandler() { public InputHandler getInputHandler() {
return inputHandler; return inputHandler;
@@ -250,7 +278,7 @@ public class Project {
} }
props.load(in); props.load(in);
in.close(); in.close();
((AntTaskTable)taskClassDefinitions).addDefinitions( props );
((AntTaskTable) taskClassDefinitions).addDefinitions(props);




} catch (IOException ioe) { } catch (IOException ioe) {
@@ -268,7 +296,7 @@ public class Project {
props.load(in); props.load(in);
in.close(); in.close();


((AntTaskTable)dataClassDefinitions).addDefinitions(props);
((AntTaskTable) dataClassDefinitions).addDefinitions(props);




} catch (IOException ioe) { } catch (IOException ioe) {
@@ -278,13 +306,18 @@ public class Project {
setSystemProperties(); setSystemProperties();
} }


/**
* Factory method to create a class loader for loading classes
*
* @return an appropriate classloader
*/
private AntClassLoader createClassLoader() { private AntClassLoader createClassLoader() {
AntClassLoader loader = null; AntClassLoader loader = null;
if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) { if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
try { try {
// 1.2+ - create advanced helper dynamically // 1.2+ - create advanced helper dynamically
Class loaderClass Class loaderClass
= Class.forName("org.apache.tools.ant.loader.AntClassLoader2");
= Class.forName(ANTCLASSLOADER_JDK12);
loader = (AntClassLoader) loaderClass.newInstance(); loader = (AntClassLoader) loaderClass.newInstance();
} catch (Exception e) { } catch (Exception e) {
log("Unable to create Class Loader: " log("Unable to create Class Loader: "
@@ -300,6 +333,14 @@ public class Project {
return loader; 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) { public AntClassLoader createClassLoader(Path path) {
AntClassLoader loader = createClassLoader(); AntClassLoader loader = createClassLoader();
loader.setClassPath(path); loader.setClassPath(path);
@@ -434,7 +475,8 @@ public class Project {
* @since 1.5 * @since 1.5
*/ */
public synchronized void setNewProperty(String name, String value) { 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) * @see #setProperty(String,String)
*/ */
public synchronized void setUserProperty(String name, String value) { 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) * @see #setProperty(String,String)
*/ */
public synchronized void setInheritedProperty(String name, String value) { public synchronized void setInheritedProperty(String name, String value) {
PropertyHelper ph=PropertyHelper.getPropertyHelper(this);
PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
ph.setInheritedProperty(null, name, value); ph.setInheritedProperty(null, name, value);
} }


@@ -476,8 +519,8 @@ public class Project {
* @param value The property value. Must not be <code>null</code>. * @param value The property value. Must not be <code>null</code>.
*/ */
private void setPropertyInternal(String name, String value) { 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 <code>null</code> name is provided. * or if a <code>null</code> name is provided.
*/ */
public String getProperty(String name) { 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. <code>${xxx</code> * property name, e.g. <code>${xxx</code>
*/ */
public String replaceProperties(String value) 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); return ph.replaceProperties(null, value, null);
} }


@@ -525,8 +567,8 @@ public class Project {
* or if a <code>null</code> name is provided. * or if a <code>null</code> name is provided.
*/ */
public String getUserProperty(String name) { 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). * (including user properties).
*/ */
public Hashtable getProperties() { public Hashtable getProperties() {
PropertyHelper ph=PropertyHelper.getPropertyHelper(this);
PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
return ph.getProperties(); return ph.getProperties();
} }


@@ -544,7 +586,7 @@ public class Project {
* @return a hashtable containing just the user properties * @return a hashtable containing just the user properties
*/ */
public Hashtable getUserProperties() { public Hashtable getUserProperties() {
PropertyHelper ph=PropertyHelper.getPropertyHelper(this);
PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
return ph.getUserProperties(); return ph.getUserProperties();
} }


@@ -561,7 +603,7 @@ public class Project {
* @since Ant 1.5 * @since Ant 1.5
*/ */
public void copyUserProperties(Project other) { public void copyUserProperties(Project other) {
PropertyHelper ph=PropertyHelper.getPropertyHelper(this);
PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
ph.copyUserProperties(other); ph.copyUserProperties(other);
} }


@@ -578,7 +620,7 @@ public class Project {
* @since Ant 1.5 * @since Ant 1.5
*/ */
public void copyInheritedProperties(Project other) { public void copyInheritedProperties(Project other) {
PropertyHelper ph=PropertyHelper.getPropertyHelper(this);
PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
ph.copyInheritedProperties(other); ph.copyInheritedProperties(other);
} }


@@ -654,8 +696,8 @@ public class Project {
* been set. * been set.
*/ */
public String getDescription() { public String getDescription() {
if( description== null ) {
description=Description.getDescription(this);
if (description == null) {
description = Description.getDescription(this);
} }


return description; return description;
@@ -924,7 +966,7 @@ public class Project {
* Must not be <code>null</code>. * Must not be <code>null</code>.
*/ */
public void addDataTypeDefinition(String typeName, Class typeClass) { public void addDataTypeDefinition(String typeName, Class typeClass) {
synchronized(dataClassDefinitions) {
synchronized (dataClassDefinitions) {
Class old = (Class) dataClassDefinitions.get(typeName); Class old = (Class) dataClassDefinitions.get(typeName);
if (null != old) { if (null != old) {
if (old.equals(typeClass)) { if (old.equals(typeClass)) {
@@ -1043,8 +1085,8 @@ public class Project {
* creation fails. * creation fails.
*/ */
public Task createTask(String taskType) throws BuildException { public Task createTask(String taskType) throws BuildException {
Task task=createNewTask(taskType);
if(task!=null) {
Task task = createNewTask(taskType);
if (task != null) {
addCreatedTask(taskType, task); addCreatedTask(taskType, task);
} }
return task; return task;
@@ -1132,11 +1174,11 @@ public class Project {
if (v != null) { if (v != null) {
Enumeration enum = v.elements(); Enumeration enum = v.elements();
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
WeakishReference ref=
WeakishReference ref =
(WeakishReference) enum.nextElement(); (WeakishReference) enum.nextElement();
Task t = (Task) ref.get(); Task t = (Task) ref.get();
//being a weak ref, it may be null by this point //being a weak ref, it may be null by this point
if(t!=null) {
if (t != null) {
t.markInvalid(); 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 * Demultiplexes flush operation so that each task receives the appropriate
* messages. If the current thread is not currently executing a task, * 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 <code>null</code>. * @param line Message to handle. Should not be <code>null</code>.
* @param isError Whether the text represents an error (<code>true</code>) * @param isError Whether the text represents an error (<code>true</code>)
* or information (<code>false</code>). * or information (<code>false</code>).
* @param terminated true if this line should be terminated with an
* end-of-line marker
*/ */
public void demuxFlush(String line, boolean isError) { public void demuxFlush(String line, boolean isError) {
Task task = (Task) threadTasks.get(Thread.currentThread()); Task task = (Task) threadTasks.get(Thread.currentThread());
@@ -1763,7 +1845,7 @@ public class Project {
*/ */
public void addReference(String name, Object value) { public void addReference(String name, Object value) {
synchronized (references) { synchronized (references) {
Object old = ((AntRefTable)references).getReal(name);
Object old = ((AntRefTable) references).getReal(name);
if (old == value) { if (old == value) {
// no warning, this is not changing anything // no warning, this is not changing anything
return; return;
@@ -1777,7 +1859,7 @@ public class Project {
try { try {
valueAsString = value.toString(); valueAsString = value.toString();
} catch (Throwable t) { } catch (Throwable t) {
log("Caught exception (" + t.getClass().getName() +")"
log("Caught exception (" + t.getClass().getName() + ")"
+ " while expanding " + name + ": " + t.getMessage(), + " while expanding " + name + ": " + t.getMessage(),
MSG_WARN); MSG_WARN);
} }
@@ -2051,7 +2133,7 @@ public class Project {
Project project; Project project;
public AntRefTable(Project project) { public AntRefTable(Project project) {
super(); super();
this.project=project;
this.project = project;
} }


/** Returns the unmodified original object. /** Returns the unmodified original object.
@@ -2061,8 +2143,8 @@ public class Project {
* of UnknownElement ( this is similar with the JDNI * of UnknownElement ( this is similar with the JDNI
* refs behavior ) * 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. /** Get method for the reference table.
@@ -2078,11 +2160,11 @@ public class Project {
*/ */
public Object get(Object key) { public Object get(Object key) {
//System.out.println("AntRefTable.get " + 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 // Make sure that
((UnknownElement)o).maybeConfigure();
o=((UnknownElement)o).getTask();
((UnknownElement) o).maybeConfigure();
o = ((UnknownElement) o).getTask();
} }
return o; return o;
} }
@@ -2091,28 +2173,28 @@ public class Project {
private static class AntTaskTable extends LazyHashtable { private static class AntTaskTable extends LazyHashtable {
Project project; Project project;
Properties props; 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); project.log("InitAll", Project.MSG_DEBUG);
if( props==null ) return;
if (props==null ) return;
Enumeration enum = props.propertyNames(); Enumeration enum = props.propertyNames();
while (enum.hasMoreElements()) { while (enum.hasMoreElements()) {
String key = (String) enum.nextElement(); String key = (String) enum.nextElement();
Class taskClass=getTask( key ); Class taskClass=getTask( key );
if( taskClass!=null ) {
if (taskClass!=null ) {
// This will call a get() and a put() // This will call a get() and a put()
if( tasks )
if (tasks )
project.addTaskDefinition(key, taskClass); project.addTaskDefinition(key, taskClass);
else else
project.addDataTypeDefinition(key, taskClass ); project.addDataTypeDefinition(key, taskClass );
@@ -2122,19 +2204,19 @@ public class Project {
} }


protected Class getTask(String key) { 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); String value=props.getProperty(key);
if( value==null) {
if (value==null) {
//project.log( "No class name for " + key, Project.MSG_VERBOSE ); //project.log( "No class name for " + key, Project.MSG_VERBOSE );
return null; return null;
} }
try { try {
Class taskClass=null; Class taskClass=null;
if( project.getCoreLoader() != null &&
if (project.getCoreLoader() != null &&
!("only".equals(project.getProperty("build.sysclasspath")))) { !("only".equals(project.getProperty("build.sysclasspath")))) {
try { try {
taskClass=project.getCoreLoader().loadClass(value); taskClass=project.getCoreLoader().loadClass(value);
if( taskClass != null ) return taskClass;
if (taskClass != null ) return taskClass;
} catch( Exception ex ) { } catch( Exception ex ) {
} }
} }
@@ -2153,11 +2235,11 @@ public class Project {
// Hashtable implementation // Hashtable implementation
public Object get( Object key ) { public Object get( Object key ) {
Object orig=super.get( 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 ); project.log("Get task " + key, Project.MSG_DEBUG );
Object taskClass=getTask( (String) key); Object taskClass=getTask( (String) key);
if( taskClass != null)
if (taskClass != null)
super.put( key, taskClass ); super.put( key, taskClass );
return taskClass; return taskClass;
} }


+ 20
- 0
src/main/org/apache/tools/ant/Task.java View File

@@ -55,6 +55,7 @@
package org.apache.tools.ant; package org.apache.tools.ant;


import java.util.Enumeration; import java.util.Enumeration;
import java.io.IOException;


/** /**
* Base class for all tasks. * Base class for all tasks.
@@ -312,6 +313,22 @@ public abstract class Task extends ProjectComponent {
handleOutput(line); 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. * 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? * 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 * @since Ant 1.5
*/ */
protected final boolean isInvalid() { protected final boolean isInvalid() {


+ 31
- 10
src/main/org/apache/tools/ant/UnknownElement.java View File

@@ -55,6 +55,7 @@
package org.apache.tools.ant; package org.apache.tools.ant;


import java.util.Vector; import java.util.Vector;
import java.io.IOException;


/** /**
* Wrapper class that holds all the information necessary to create a task * Wrapper class that holds all the information necessary to create a task
@@ -124,7 +125,7 @@ public class UnknownElement extends Task {


getWrapper().setProxy(realThing); getWrapper().setProxy(realThing);
if (realThing instanceof Task) { if (realThing instanceof Task) {
Task task=(Task)realThing;
Task task = (Task) realThing;


task.setProject(project); task.setProject(project);
task.setRuntimeConfigurableWrapper(getWrapper()); task.setRuntimeConfigurableWrapper(getWrapper());
@@ -135,7 +136,7 @@ public class UnknownElement extends Task {


// For Script to work. Ugly // For Script to work. Ugly
// The reference is replaced by RuntimeConfigurable // The reference is replaced by RuntimeConfigurable
this.getOwningTarget().replaceChild(this, (Task)realThing);
this.getOwningTarget().replaceChild(this, (Task) realThing);
} }


handleChildren(realThing, getWrapper()); 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. * 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 ) // the task will not be reused ( a new init() will be called )
// Let GC do its job // Let GC do its job
realThing=null;
realThing = null;
} }


/** /**
@@ -241,8 +262,7 @@ public class UnknownElement extends Task {
*/ */
protected void handleChildren(Object parent, protected void handleChildren(Object parent,
RuntimeConfigurable parentWrapper) RuntimeConfigurable parentWrapper)
throws BuildException
{
throws BuildException {
if (parent instanceof TaskAdapter) { if (parent instanceof TaskAdapter) {
parent = ((TaskAdapter) parent).getProxy(); parent = ((TaskAdapter) parent).getProxy();
} }
@@ -259,15 +279,15 @@ public class UnknownElement extends Task {
//ProjectComponentHelper helper=ProjectComponentHelper.getProjectComponentHelper(); //ProjectComponentHelper helper=ProjectComponentHelper.getProjectComponentHelper();
//realChild = helper.createProjectComponent( child, getProject(), null, //realChild = helper.createProjectComponent( child, getProject(), null,
// child.getTag()); // child.getTag());
realChild=makeTask(child, childWrapper, false);
realChild = makeTask(child, childWrapper, false);


if (realChild == null ) {
if (realChild == null) {
throw getNotFoundException("task", child.getTag()); throw getNotFoundException("task", child.getTag());
} }


// XXX DataTypes will be wrapped or treated like normal components // 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); ((TaskContainer) parent).addTask(task);
task.setLocation(child.getLocation()); task.setLocation(child.getLocation());
// UnknownElement always has an associated target // UnknownElement always has an associated target
@@ -277,7 +297,8 @@ public class UnknownElement extends Task {
// What ? Add data type ? createElement ? // What ? Add data type ? createElement ?
} }
} else { } else {
realChild = ih.createElement(getProject(), parent, child.getTag());
realChild
= ih.createElement(getProject(), parent, child.getTag());
} }


childWrapper.setProxy(realChild); childWrapper.setProxy(realChild);


+ 14
- 1
src/main/org/apache/tools/ant/taskdefs/Ant.java View File

@@ -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. * Pass output sent to System.out to the new project.
* *
@@ -443,7 +455,8 @@ public class Ant extends Task {
* requested. * requested.
*/ */
private void addReferences() throws BuildException { private void addReferences() throws BuildException {
Hashtable thisReferences = (Hashtable) getProject().getReferences().clone();
Hashtable thisReferences
= (Hashtable) getProject().getReferences().clone();
Hashtable newReferences = newProject.getReferences(); Hashtable newReferences = newProject.getReferences();
Enumeration e; Enumeration e;
if (references.size() > 0) { if (references.size() > 0) {


+ 10
- 0
src/main/org/apache/tools/ant/taskdefs/CallTarget.java View File

@@ -56,6 +56,7 @@ package org.apache.tools.ant.taskdefs;


import org.apache.tools.ant.BuildException; import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task; import org.apache.tools.ant.Task;
import java.io.IOException;


/** /**
* Call another target in the same project. * 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. * Pass output sent to System.out to the new project.
* *


+ 9
- 0
src/main/org/apache/tools/ant/taskdefs/Java.java View File

@@ -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. * Pass output sent to System.out to specified output file.
* *


+ 146
- 6
src/main/org/apache/tools/ant/taskdefs/Redirector.java View File

@@ -79,37 +79,92 @@ import org.apache.tools.ant.util.TeeOutputStream;
* @since Ant 1.6 * @since Ant 1.6
*/ */
public class Redirector { 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; private File out;
/**
* The file to which standard error is being redirected
*/
private File error; private File error;
/**
* The file from which standard input is being taken.
*/
private File input; 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; private boolean logError = false;
/**
* Buffer used to capture output for storage into a property
*/
private ByteArrayOutputStream baos = null; private ByteArrayOutputStream baos = null;
/**
* Buffer used to capture error output for storage into a property
*/
private ByteArrayOutputStream errorBaos = null; private ByteArrayOutputStream errorBaos = null;
/** The name of the property into which output is to be stored */
private String outputProperty; private String outputProperty;
/** The name of the property into which error output is to be stored */
private String errorProperty; private String errorProperty;
/** String from which input is taken */
private String inputString; private String inputString;
/** Flag which indicates if error and output files are to be appended. */
private boolean append = false; private boolean append = false;
/** The task for which this redirector is working */
private Task managingTask; private Task managingTask;


/** The stream for output data */
private OutputStream outputStream = null; private OutputStream outputStream = null;
/** The stream for error output */
private OutputStream errorStream = null; 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; private PrintStream outPrintStream = null;
/** Stream which is used for line oriented error output */
private PrintStream errorPrintStream = null; 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) { public Redirector(Task managingTask) {
this.managingTask = managingTask; this.managingTask = managingTask;
} }
/** /**
* Set the input to use for the task * Set the input to use for the task
*
* @param input the file from which input is read.
*/ */
public void setInput(File input) { public void setInput(File input) {
this.input = 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) { public void setInputString(String inputString) {
this.inputString = inputString; this.inputString = inputString;
} }
@@ -118,6 +173,8 @@ public class Redirector {
/** /**
* File the output of the process is redirected to. If error is not * File the output of the process is redirected to. If error is not
* redirected, it too will appear in the output * redirected, it too will appear in the output
*
* @param out the file to which output stream is written
*/ */
public void setOutput(File out) { public void setOutput(File out) {
this.out = out; this.out = out;
@@ -127,14 +184,18 @@ public class Redirector {
* Controls whether error output of exec is logged. This is only useful * Controls whether error output of exec is logged. This is only useful
* when output is being redirected and error output is desired in the * when output is being redirected and error output is desired in the
* Ant log * 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) { public void setLogError(boolean logError) {
this.logError = 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) { public void setError(File error) {
this.error = error; this.error = error;
@@ -143,6 +204,9 @@ public class Redirector {
/** /**
* Property name whose value should be set to the output of * Property name whose value should be set to the output of
* the process. * the process.
*
* @param outputProperty the name of the property to be set with the
* task's output.
*/ */
public void setOutputProperty(String outputProperty) { public void setOutputProperty(String outputProperty) {
this.outputProperty = outputProperty; this.outputProperty = outputProperty;
@@ -152,6 +216,8 @@ public class Redirector {
* Whether output should be appended to or overwrite an existing file. * Whether output should be appended to or overwrite an existing file.
* Defaults to false. * Defaults to false.
* *
* @param append if true output and error streams are appended to their
* respective files, if specified.
*/ */
public void setAppend(boolean append) { public void setAppend(boolean append) {
this.append = append; this.append = append;
@@ -161,11 +227,21 @@ public class Redirector {
* Property name whose value should be set to the error of * Property name whose value should be set to the error of
* the process. * the process.
* *
* @param errorProperty the name of the property to be set
* with the error output.
*/ */
public void setErrorProperty(String errorProperty) { public void setErrorProperty(String errorProperty) {
this.errorProperty = 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, private void setPropertyFromBAOS(ByteArrayOutputStream baos,
String propertyName) throws IOException { 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() { public void createStreams() {
if (out == null && outputProperty == null) { if (out == null && outputProperty == null) {
outputStream = new LogOutputStream(managingTask, Project.MSG_INFO); outputStream = new LogOutputStream(managingTask, Project.MSG_INFO);
@@ -265,6 +345,11 @@ public class Redirector {
/** /**
* Create the StreamHandler to use with our Execute instance. * 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 { public ExecuteStreamHandler createHandler() throws BuildException {
createStreams(); 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) { protected void handleOutput(String line) {
if (outPrintStream == null) { if (outPrintStream == null) {
@@ -282,9 +368,31 @@ public class Redirector {
outPrintStream.println(line); 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) { protected void handleFlush(String line) {
if (outPrintStream == null) { 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) { protected void handleErrorOutput(String line) {
if (errorPrintStream == null) { 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) { protected void handleErrorFlush(String line) {
if (errorPrintStream == null) { if (errorPrintStream == null) {
@@ -315,15 +425,45 @@ public class Redirector {
errorPrintStream.print(line); 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() { public OutputStream getOutputStream() {
return outputStream; 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() { public OutputStream getErrorStream() {
return errorStream; 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 { public void complete() throws IOException {
System.out.flush(); System.out.flush();
System.err.flush(); System.err.flush();


+ 15
- 0
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java View File

@@ -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 * Pass output sent to System.out to the TestRunner so it can
* collect ot for the formatters. * collect ot for the formatters.


+ 5
- 0
src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java View File

@@ -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) { protected void handleErrorOutput(String line) {
if (systemError != null) { if (systemError != null) {
systemError.println(line); systemError.println(line);


+ 2
- 1
src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java View File

@@ -56,9 +56,10 @@ package org.apache.tools.ant.taskdefs;
import org.apache.tools.ant.*; import org.apache.tools.ant.*;
import org.apache.tools.ant.BuildFileTest; import org.apache.tools.ant.BuildFileTest;
import java.util.Random; import java.util.Random;

/** /**
* A simple task that prints to System.out and System.err and then catches * 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 * exception is thrown
* *
* @since 1.5 * @since 1.5


Loading…
Cancel
Save