git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271542 13f79535-47bb-0310-9956-ffa450edef68master
@@ -54,12 +54,15 @@ | |||
package org.apache.ant.antcore.execution; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.Map; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import org.apache.ant.common.model.ModelElement; | |||
import org.apache.ant.common.event.BuildListener; | |||
import org.apache.ant.common.event.BuildEvent; | |||
import org.apache.ant.common.antlib.Task; | |||
import org.apache.ant.common.event.BuildEvent; | |||
import org.apache.ant.common.event.BuildListener; | |||
import org.apache.ant.common.model.ModelElement; | |||
import org.apache.ant.common.util.DemuxOutputReceiver; | |||
import org.apache.ant.common.event.MessageLevel; | |||
/** | |||
* BuildEventSupport is used by classes which which to send build events to | |||
@@ -68,13 +71,16 @@ import org.apache.ant.common.antlib.Task; | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
* @created 15 January 2002 | |||
*/ | |||
public class BuildEventSupport { | |||
public class BuildEventSupport implements DemuxOutputReceiver { | |||
/** | |||
* The listeners attached to the object which contains this support | |||
* object | |||
*/ | |||
private List listeners = new ArrayList(); | |||
/** Records the latest task to be executed on a thread (Thread to Task). */ | |||
private Map threadTasks = new HashMap(); | |||
/** | |||
* Gets the listeners of the BuildEventSupport | |||
* | |||
@@ -166,6 +172,9 @@ public class BuildEventSupport { | |||
* @param task the task with which the event is associated | |||
*/ | |||
public void fireTaskStarted(Task task) { | |||
synchronized (this) { | |||
threadTasks.put(Thread.currentThread(), task); | |||
} | |||
BuildEvent event = new BuildEvent(task, BuildEvent.TASK_STARTED); | |||
for (Iterator i = listeners.iterator(); i.hasNext(); ) { | |||
BuildListener listener = (BuildListener)i.next(); | |||
@@ -181,6 +190,11 @@ public class BuildEventSupport { | |||
*/ | |||
public void fireTaskFinished(Task task, | |||
Throwable cause) { | |||
System.out.flush(); | |||
System.err.flush(); | |||
synchronized (this) { | |||
threadTasks.remove(Thread.currentThread()); | |||
} | |||
BuildEvent event = new BuildEvent(task, BuildEvent.TASK_FINISHED, | |||
cause); | |||
for (Iterator i = listeners.iterator(); i.hasNext(); ) { | |||
@@ -204,5 +218,28 @@ public class BuildEventSupport { | |||
listener.messageLogged(event); | |||
} | |||
} | |||
/** | |||
* Demultiplexes output so that each task receives the appropriate | |||
* messages. If the current thread is not currently executing a task, | |||
* the message is logged directly. | |||
* | |||
* @param line Message to handle. Should not be <code>null</code>. | |||
* @param isError Whether the text represents an error (<code>true</code> | |||
* ) or information (<code>false</code>). | |||
*/ | |||
public void threadOutput(String line, boolean isError) { | |||
Task task = (Task)threadTasks.get(Thread.currentThread()); | |||
if (task == null) { | |||
fireMessageLogged(this, line, | |||
isError ? MessageLevel.MSG_ERR : MessageLevel.MSG_INFO); | |||
} else { | |||
if (isError) { | |||
task.handleSystemErr(line); | |||
} else { | |||
task.handleSystemOut(line); | |||
} | |||
} | |||
} | |||
} | |||
@@ -422,7 +422,12 @@ public class ComponentManager implements ComponentService { | |||
throws ExecutionException { | |||
ImportInfo definition = getDefinition(componentName); | |||
if (definition == null) { | |||
throw new ExecutionException("There is no definition of the <" | |||
+ componentName + "> component"); | |||
} | |||
String className = definition.getClassName(); | |||
ComponentLibrary componentLibrary | |||
= definition.getComponentLibrary(); | |||
String localName = definition.getLocalName(); | |||
@@ -64,6 +64,7 @@ import org.apache.ant.common.event.BuildListener; | |||
import org.apache.ant.common.model.Project; | |||
import org.apache.ant.common.util.AntException; | |||
import org.apache.ant.common.util.ExecutionException; | |||
import org.apache.ant.common.util.DemuxOutputReceiver; | |||
import org.apache.ant.init.InitConfig; | |||
/** | |||
@@ -75,7 +76,7 @@ import org.apache.ant.init.InitConfig; | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
* @created 12 January 2002 | |||
*/ | |||
public class ExecutionManager { | |||
public class ExecutionManager implements DemuxOutputReceiver { | |||
/** The AntLibraries built from Ant's Populated Task Libraries. */ | |||
private Map antLibraries = new HashMap(); | |||
@@ -212,5 +213,17 @@ public class ExecutionManager { | |||
} | |||
} | |||
/** | |||
* Handle the content from a single thread. This method will be called | |||
* by the thread producing the content. The content is broken up into | |||
* separate lines | |||
* | |||
* @param line the content produce by the current thread. | |||
* @param isErr true if this content is from the thread's error stream. | |||
*/ | |||
public void threadOutput(String line, boolean isErr) { | |||
eventSupport.threadOutput(line, isErr); | |||
} | |||
} | |||
@@ -74,6 +74,7 @@ import org.apache.ant.common.service.FileService; | |||
import org.apache.ant.common.service.MagicProperties; | |||
import org.apache.ant.common.util.AntException; | |||
import org.apache.ant.common.util.ConfigException; | |||
import org.apache.ant.common.util.DemuxOutputReceiver; | |||
import org.apache.ant.common.util.ExecutionException; | |||
import org.apache.ant.common.util.FileUtils; | |||
import org.apache.ant.init.InitConfig; | |||
@@ -86,7 +87,7 @@ import org.apache.ant.init.InitConfig; | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
* @created 14 January 2002 | |||
*/ | |||
public class Frame { | |||
public class Frame implements DemuxOutputReceiver { | |||
/** the base dir of the project */ | |||
private File baseDir; | |||
@@ -143,11 +144,9 @@ public class Frame { | |||
*/ | |||
private ComponentManager componentManager; | |||
/** | |||
* The core's execution Service | |||
*/ | |||
/** The core's execution Service */ | |||
private CoreExecService execService; | |||
/** | |||
* Create an Execution Frame for the given project | |||
* | |||
@@ -165,6 +164,18 @@ public class Frame { | |||
this.initConfig = initConfig; | |||
} | |||
/** | |||
* Replace ${} style constructions in the given value with the string | |||
* value of the corresponding data values in the frame | |||
* | |||
* @param value the string to be scanned for property references. | |||
* @return the string with all property references replaced | |||
* @exception ExecutionException if any of the properties do not exist | |||
*/ | |||
public String replacePropertyRefs(String value) throws ExecutionException { | |||
return dataService.replacePropertyRefs(value); | |||
} | |||
/** | |||
* Sets the Project of the Frame | |||
* | |||
@@ -189,18 +200,6 @@ public class Frame { | |||
setMagicProperties(); | |||
} | |||
/** | |||
* Replace ${} style constructions in the given value with the string | |||
* value of the corresponding data values in the frame | |||
* | |||
* @param value the string to be scanned for property references. | |||
* @return the string with all property references replaced | |||
* @exception ExecutionException if any of the properties do not exist | |||
*/ | |||
public String replacePropertyRefs(String value) throws ExecutionException { | |||
return dataService.replacePropertyRefs(value); | |||
} | |||
/** | |||
* Set a value in this frame or any of its imported frames. | |||
* | |||
@@ -603,7 +602,6 @@ public class Frame { | |||
} catch (ConfigException e) { | |||
throw new ExecutionException(e); | |||
} | |||
} | |||
/** | |||
@@ -623,8 +621,8 @@ public class Frame { | |||
if (component instanceof Task) { | |||
execService.executeTask((Task)component); | |||
} else { | |||
String typeId | |||
= model.getAspectValue(Constants.ANT_ASPECT, "id"); | |||
String typeId | |||
= model.getAspectValue(Constants.ANT_ASPECT, "id"); | |||
if (typeId != null) { | |||
setDataValue(typeId, component, true); | |||
} | |||
@@ -709,6 +707,18 @@ public class Frame { | |||
executeTasks(taskIterator); | |||
} | |||
/** | |||
* Handle the content from a single thread. This method will be called | |||
* by the thread producing the content. The content is broken up into | |||
* separate lines | |||
* | |||
* @param line the content produce by the current thread. | |||
* @param isErr true if this content is from the thread's error stream. | |||
*/ | |||
public void threadOutput(String line, boolean isErr) { | |||
eventSupport.threadOutput(line, isErr); | |||
} | |||
/** | |||
* Determine the base directory for each frame in the frame hierarchy | |||
* | |||
@@ -755,7 +765,7 @@ public class Frame { | |||
dataService = new CoreDataService(this, | |||
config.isUnsetPropertiesAllowed()); | |||
execService = new CoreExecService(this); | |||
services.put(FileService.class, fileService); | |||
services.put(ComponentService.class, componentManager); | |||
services.put(DataService.class, dataService); | |||
@@ -76,6 +76,15 @@ public class ProjectHandler extends ElementHandler { | |||
/** The default attribute name */ | |||
public static final String DEFAULT_ATTR = "default"; | |||
/** The name of the element used to define references */ | |||
public static final String REF_ELEMENT = "ant:ref"; | |||
/** The name of the element used to define references */ | |||
public static final String INCLUDE_ELEMENT = "ant:include"; | |||
/** The name of the element used to define references */ | |||
public static final String TARGET_ELEMENT = "target"; | |||
/** The project being parsed. */ | |||
private Project project; | |||
@@ -148,8 +157,8 @@ public class ProjectHandler extends ElementHandler { | |||
public void startElement(String uri, String localName, String qualifiedName, | |||
Attributes attributes) | |||
throws SAXParseException { | |||
if (qualifiedName.equals("ref")) { | |||
if (qualifiedName.equals(REF_ELEMENT)) { | |||
RefHandler refHandler = new RefHandler(); | |||
refHandler.start(getParseContext(), getXMLReader(), this, | |||
getLocator(), attributes, getElementSource(), | |||
@@ -160,12 +169,12 @@ public class ProjectHandler extends ElementHandler { | |||
} catch (ModelException e) { | |||
throw new SAXParseException(e.getMessage(), getLocator(), e); | |||
} | |||
} else if (qualifiedName.equals("include")) { | |||
} else if (qualifiedName.equals(INCLUDE_ELEMENT)) { | |||
IncludeHandler includeHandler = new IncludeHandler(project); | |||
includeHandler.start(getParseContext(), getXMLReader(), | |||
this, getLocator(), attributes, getElementSource(), | |||
qualifiedName); | |||
} else if (qualifiedName.equals("target")) { | |||
} else if (qualifiedName.equals(TARGET_ELEMENT)) { | |||
TargetHandler targetHandler = new TargetHandler(); | |||
targetHandler.start(getParseContext(), getXMLReader(), | |||
this, getLocator(), attributes, | |||
@@ -175,13 +184,17 @@ public class ProjectHandler extends ElementHandler { | |||
} catch (ModelException e) { | |||
throw new SAXParseException(e.getMessage(), getLocator(), e); | |||
} | |||
} else { | |||
} else if (localName != null) { | |||
// everything else is a task | |||
BuildElementHandler buildElementHandler = new BuildElementHandler(); | |||
buildElementHandler.start(getParseContext(), getXMLReader(), | |||
this, getLocator(), attributes, getElementSource(), | |||
qualifiedName); | |||
project.addTask(buildElementHandler.getBuildElement()); | |||
} else { | |||
// ignore namespaced elements | |||
throw new SAXParseException("Only the \"ant\" namespace is " | |||
+ "currently recognized (" + qualifiedName + ")", getLocator()); | |||
} | |||
} | |||
@@ -164,6 +164,31 @@ public abstract class Task extends ProjectComponent | |||
} | |||
} | |||
/** | |||
* Handle Output produced by the task. When a task prints to System.out | |||
* the container may catch this and redirect the content back to the | |||
* task by invoking this method. This method must NOT call System.out, | |||
* directly or indirectly. | |||
* | |||
* @param line The line of content produce by the task | |||
*/ | |||
public void handleSystemOut(String line) { | |||
handleOutput(line); | |||
} | |||
/** | |||
* Handle error information produced by the task. When a task prints to | |||
* System.err the container may catch this and redirect the content back | |||
* to the task by invoking this method. This method must NOT call | |||
* System.err, directly or indirectly. | |||
* | |||
* @param line The line of error info produce by the task | |||
*/ | |||
public void handleSystemErr(String line) { | |||
// default behaviout is to log at WARN level | |||
handleErrorOutput(line); | |||
} | |||
/** | |||
* Handle output captured for this task | |||
* | |||
@@ -57,6 +57,7 @@ import java.io.FileNotFoundException; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
import java.io.PrintStream; | |||
import java.io.OutputStream; | |||
import java.net.MalformedURLException; | |||
import java.net.URL; | |||
import java.util.ArrayList; | |||
@@ -75,6 +76,7 @@ import org.apache.ant.common.event.BuildListener; | |||
import org.apache.ant.common.event.MessageLevel; | |||
import org.apache.ant.common.model.Project; | |||
import org.apache.ant.common.util.ConfigException; | |||
import org.apache.ant.common.util.DemuxOutputStream; | |||
import org.apache.ant.init.InitConfig; | |||
import org.apache.ant.init.InitUtils; | |||
@@ -298,6 +300,12 @@ public class Commandline { | |||
// create the execution manager to execute the build | |||
executionManager = new ExecutionManager(initConfig, config); | |||
OutputStream demuxOut | |||
= new DemuxOutputStream(executionManager, false); | |||
OutputStream demuxErr | |||
= new DemuxOutputStream(executionManager, true); | |||
System.setOut(new PrintStream(demuxOut)); | |||
System.setErr(new PrintStream(demuxErr)); | |||
addBuildListeners(executionManager); | |||
} catch (Throwable e) { | |||
if (logger != null) { | |||
@@ -52,6 +52,7 @@ | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.ant.common.antlib; | |||
import org.apache.ant.common.event.MessageLevel; | |||
/** | |||
* Abstract implementation of the Task interface | |||
@@ -81,5 +82,30 @@ public abstract class AbstractTask extends AbstractComponent implements Task { | |||
return taskName; | |||
} | |||
/** | |||
* Handle Output produced by the task. When a task prints to System.out | |||
* the container may catch this and redirect the content back to the | |||
* task by invoking this method. This method must NOT call System.out, | |||
* directly or indirectly. | |||
* | |||
* @param line The line of content produce by the task | |||
*/ | |||
public void handleSystemOut(String line) { | |||
// default behaviout is to log at INFO level | |||
log(line, MessageLevel.MSG_INFO); | |||
} | |||
/** | |||
* Handle error information produced by the task. When a task prints to | |||
* System.err the container may catch this and redirect the content back | |||
* to the task by invoking this method. This method must NOT call | |||
* System.err, directly or indirectly. | |||
* | |||
* @param line The line of error info produce by the task | |||
*/ | |||
public void handleSystemErr(String line) { | |||
// default behaviout is to log at WARN level | |||
log(line, MessageLevel.MSG_WARN); | |||
} | |||
} | |||
@@ -81,5 +81,25 @@ public interface Task extends ExecutionComponent { | |||
* @return the taskName value | |||
*/ | |||
String getTaskName(); | |||
/** | |||
* Handle Output produced by the task. When a task prints to System.out | |||
* the container may catch this and redirect the content back to the | |||
* task by invoking this method. This method must NOT call System.out, | |||
* directly or indirectly. | |||
* | |||
* @param line The line of content produce by the task | |||
*/ | |||
void handleSystemOut(String line); | |||
/** | |||
* Handle error information produced by the task. When a task prints to | |||
* System.err the container may catch this and redirect the content back | |||
* to the task by invoking this method. This method must NOT call | |||
* System.err, directly or indirectly. | |||
* | |||
* @param line The line of error info produce by the task | |||
*/ | |||
void handleSystemErr(String line); | |||
} | |||
@@ -253,9 +253,16 @@ public class Project extends ModelElement { | |||
* | |||
* @param fullTargetName The name of the target relative to this project | |||
* @return the Target object with the given name | |||
* @exception ModelException if the given target does not exist in this | |||
* project | |||
*/ | |||
public Target getRefTarget(String fullTargetName) { | |||
public Target getRefTarget(String fullTargetName) throws ModelException { | |||
Project containingProject = getRefProject(fullTargetName); | |||
if (containingProject == null) { | |||
throw new ModelException("The target name \"" + fullTargetName | |||
+ "\" does not exist in this project"); | |||
} | |||
if (containingProject == this) { | |||
return getTarget(fullTargetName); | |||
} | |||
@@ -504,19 +511,23 @@ public class Project extends ModelElement { | |||
if (flattenedList.contains(fullTargetName)) { | |||
return; | |||
} | |||
String fullProjectName = getFullProjectName(fullTargetName); | |||
Target target = getRefTarget(fullTargetName); | |||
if (target == null) { | |||
throw new ConfigException("Target " + fullTargetName | |||
+ " does not exist"); | |||
} | |||
for (Iterator i = target.getDependencies(); i.hasNext(); ) { | |||
String localDependencyName = (String)i.next(); | |||
String fullDependencyName | |||
= fullProjectName == null ? localDependencyName | |||
: fullProjectName + REF_DELIMITER + localDependencyName; | |||
flattenDependency(flattenedList, fullDependencyName); | |||
flattenedList.add(fullDependencyName); | |||
try { | |||
String fullProjectName = getFullProjectName(fullTargetName); | |||
Target target = getRefTarget(fullTargetName); | |||
if (target == null) { | |||
throw new ConfigException("Target " + fullTargetName | |||
+ " does not exist"); | |||
} | |||
for (Iterator i = target.getDependencies(); i.hasNext(); ) { | |||
String localDependencyName = (String)i.next(); | |||
String fullDependencyName | |||
= fullProjectName == null ? localDependencyName | |||
: fullProjectName + REF_DELIMITER + localDependencyName; | |||
flattenDependency(flattenedList, fullDependencyName); | |||
flattenedList.add(fullDependencyName); | |||
} | |||
} catch (ModelException e) { | |||
throw new ConfigException(e); | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,74 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001-2002 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.ant.common.util; | |||
/** | |||
* A Demux output receiver receives buffered content which has been | |||
* demultiplexed from the output potentially generated by multiple threads | |||
* | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
* @created 22 February 2002 | |||
*/ | |||
public interface DemuxOutputReceiver { | |||
/** | |||
* Handle the content from a single thread. This method will be called | |||
* by the thread producing the content. The content is broken up into | |||
* separate lines | |||
* | |||
* @param line the content produce by the current thread. | |||
* @param isErr true if this content is from the thread's error stream. | |||
*/ | |||
void threadOutput(String line, boolean isErr); | |||
} | |||
@@ -0,0 +1,198 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2001-2002 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.ant.common.util; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.util.Hashtable; | |||
/** | |||
* Buffers content written per thread and forwards the buffers onto the | |||
* given receiver | |||
* | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
* @created 22 February 2002 | |||
*/ | |||
public class DemuxOutputStream extends OutputStream { | |||
/** | |||
* A data class to store information about a buffer. Such informatio is | |||
* stored on a per-thread basis. | |||
* | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
* @created 22 February 2002 | |||
*/ | |||
private static class BufferInfo { | |||
/** The per-thread output stream */ | |||
private ByteArrayOutputStream buffer; | |||
/** | |||
* Whether the next line-terminator should be skipped in terms of | |||
* processing the buffer or not. Used to avoid \r\n invoking | |||
* processBuffer twice. | |||
*/ | |||
private boolean skip = false; | |||
} | |||
/** Maximum buffer size */ | |||
private static final int MAX_SIZE = 1024; | |||
/** Mapping from thread to buffer (Thread to BufferInfo) */ | |||
private Hashtable buffers = new Hashtable(); | |||
/** The object which receives the output */ | |||
private DemuxOutputReceiver receiver; | |||
/** Whether or not this stream represents an error stream */ | |||
private boolean isErrorStream; | |||
/** | |||
* Creates a new instance of this class. | |||
* | |||
* @param isErrorStream true if this is the error string, otherwise a | |||
* normal output stream. This is passed to the project so it knows | |||
* which stream it is receiving. | |||
* @param receiver The receiver to which demux'd content is sent. | |||
*/ | |||
public DemuxOutputStream(DemuxOutputReceiver receiver, | |||
boolean isErrorStream) { | |||
this.receiver = receiver; | |||
this.isErrorStream = isErrorStream; | |||
} | |||
/** | |||
* Writes the data to the buffer and flushes the buffer if a line | |||
* separator is detected or if the buffer has reached its maximum size. | |||
* | |||
* @param cc data to log (byte). | |||
* @exception IOException if the data cannot be written to the stream | |||
*/ | |||
public void write(int cc) throws IOException { | |||
final byte c = (byte)cc; | |||
BufferInfo bufferInfo = getBufferInfo(); | |||
if ((c == '\n') || (c == '\r')) { | |||
if (!bufferInfo.skip) { | |||
processBuffer(bufferInfo.buffer); | |||
} | |||
} else { | |||
bufferInfo.buffer.write(cc); | |||
if (bufferInfo.buffer.size() > MAX_SIZE) { | |||
processBuffer(bufferInfo.buffer); | |||
} | |||
} | |||
bufferInfo.skip = (c == '\r'); | |||
} | |||
/** | |||
* Equivalent to calling {@link #flush flush} on the stream. | |||
* | |||
* @exception IOException if there is a problem closing the stream. | |||
*/ | |||
public void close() throws IOException { | |||
flush(); | |||
} | |||
/** | |||
* Writes all remaining data in the buffer associated with the current | |||
* thread to the project. | |||
* | |||
* @exception IOException if there is a problem flushing the stream. | |||
*/ | |||
public void flush() throws IOException { | |||
BufferInfo bufferInfo = getBufferInfo(); | |||
if (bufferInfo.buffer.size() > 0) { | |||
processBuffer(bufferInfo.buffer); | |||
} | |||
} | |||
/** | |||
* Converts the buffer to a string and sends it to {@link | |||
* Project#demuxOutput(String,boolean) Project.demuxOutput}. | |||
* | |||
* @param buffer the ByteArrayOutputStream used to collect the output | |||
* until a line separator is seen. | |||
*/ | |||
protected void processBuffer(ByteArrayOutputStream buffer) { | |||
String output = buffer.toString(); | |||
receiver.threadOutput(output, isErrorStream); | |||
resetBufferInfo(); | |||
} | |||
/** | |||
* Returns the buffer associated with the current thread. | |||
* | |||
* @return a ByteArrayOutputStream for the current thread to write data | |||
* to | |||
*/ | |||
private BufferInfo getBufferInfo() { | |||
Thread current = Thread.currentThread(); | |||
BufferInfo bufferInfo = (BufferInfo)buffers.get(current); | |||
if (bufferInfo == null) { | |||
bufferInfo = new BufferInfo(); | |||
bufferInfo.buffer = new ByteArrayOutputStream(); | |||
bufferInfo.skip = false; | |||
buffers.put(current, bufferInfo); | |||
} | |||
return bufferInfo; | |||
} | |||
/** Resets the buffer for the current thread. */ | |||
private void resetBufferInfo() { | |||
Thread current = Thread.currentThread(); | |||
buffers.remove(current); | |||
} | |||
} | |||
@@ -0,0 +1,71 @@ | |||
/* | |||
* The Apache Software License, Version 1.1 | |||
* | |||
* Copyright (c) 2002 The Apache Software Foundation. All rights | |||
* reserved. | |||
* | |||
* Redistribution and use in source and binary forms, with or without | |||
* modification, are permitted provided that the following conditions | |||
* are met: | |||
* | |||
* 1. Redistributions of source code must retain the above copyright | |||
* notice, this list of conditions and the following disclaimer. | |||
* | |||
* 2. Redistributions in binary form must reproduce the above copyright | |||
* notice, this list of conditions and the following disclaimer in | |||
* the documentation and/or other materials provided with the | |||
* distribution. | |||
* | |||
* 3. The end-user documentation included with the redistribution, if | |||
* any, must include the following acknowlegement: | |||
* "This product includes software developed by the | |||
* Apache Software Foundation (http://www.apache.org/)." | |||
* Alternately, this acknowlegement may appear in the software itself, | |||
* if and wherever such third-party acknowlegements normally appear. | |||
* | |||
* 4. The names "The Jakarta Project", "Ant", and "Apache Software | |||
* Foundation" must not be used to endorse or promote products derived | |||
* from this software without prior written permission. For written | |||
* permission, please contact apache@apache.org. | |||
* | |||
* 5. Products derived from this software may not be called "Apache" | |||
* nor may "Apache" appear in their names without prior written | |||
* permission of the Apache Group. | |||
* | |||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
* SUCH DAMAGE. | |||
* ==================================================================== | |||
* | |||
* This software consists of voluntary contributions made by many | |||
* individuals on behalf of the Apache Software Foundation. For more | |||
* information on the Apache Software Foundation, please see | |||
* <http://www.apache.org/>. | |||
*/ | |||
package org.apache.tools.ant; | |||
/** | |||
* Old Ant1 entry point | |||
* | |||
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a> | |||
*/ | |||
public class Main { | |||
/** | |||
* Entry point for starting command line Ant | |||
* | |||
* @param args commandline arguments | |||
*/ | |||
public static void main(String[] args) { | |||
org.apache.ant.start.Main.main(args); | |||
} | |||
} | |||