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