aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java1022
1 files changed, 1022 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java
new file mode 100644
index 00000000..3b35d231
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/taskdefs/Redirector.java
@@ -0,0 +1,1022 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PipedOutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.util.ConcatFileInputStream;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+import org.apache.tools.ant.util.LazyFileOutputStream;
+import org.apache.tools.ant.util.LeadPipeInputStream;
+import org.apache.tools.ant.util.LineOrientedOutputStreamRedirector;
+import org.apache.tools.ant.util.OutputStreamFunneler;
+import org.apache.tools.ant.util.ReaderInputStream;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+/**
+ * The Redirector class manages the setup and connection of input and output
+ * redirection for an Ant project component.
+ *
+ * @since Ant 1.6
+ */
+public class Redirector {
+ private static final int STREAMPUMPER_WAIT_INTERVAL = 1000;
+
+ private static final String DEFAULT_ENCODING = System
+ .getProperty("file.encoding");
+
+ private class PropertyOutputStream extends ByteArrayOutputStream {
+ private final String property;
+
+ private boolean closed = false;
+
+ PropertyOutputStream(final String property) {
+ super();
+ this.property = property;
+ }
+
+ @Override
+ public void close() throws IOException {
+ synchronized (outMutex) {
+ if (!closed && !(appendOut && appendProperties)) {
+ setPropertyFromBAOS(this, property);
+ closed = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * The file(s) from which standard input is being taken. If > 1, files'
+ * content will be concatenated in the order received.
+ */
+ private File[] input;
+
+ /**
+ * The file(s) receiving standard output. Will also receive standard error
+ * unless standard error is redirected or logError is true.
+ */
+ private File[] out;
+
+ /**
+ * The file(s) to which standard error is being redirected
+ */
+ private File[] error;
+
+ /**
+ * Indicates if standard error should be logged to Ant's log system rather
+ * than the output. This has no effect if standard error is redirected to a
+ * file or property.
+ */
+ private boolean logError = false;
+
+ /**
+ * Buffer used to capture output for storage into a property
+ */
+ private PropertyOutputStream baos = null;
+
+ /**
+ * Buffer used to capture error output for storage into a property
+ */
+ private PropertyOutputStream errorBaos = null;
+
+ /** The name of the property into which output is to be stored */
+ private String outputProperty;
+
+ /** The name of the property into which error output is to be stored */
+ private String errorProperty;
+
+ /** String from which input is taken */
+ private String inputString;
+
+ /** Flag which indicates if error and output files are to be appended. */
+ private boolean appendOut = false;
+
+ private boolean appendErr = false;
+
+ /** Flag which indicates that output should be always sent to the log */
+ private boolean alwaysLogOut = false;
+
+ private boolean alwaysLogErr = false;
+
+ /** Flag which indicates whether files should be created even when empty. */
+ private boolean createEmptyFilesOut = true;
+
+ private boolean createEmptyFilesErr = true;
+
+ /** The task for which this redirector is working */
+ private final ProjectComponent managingTask;
+
+ /** The stream for output data */
+ private OutputStream outputStream = null;
+
+ /** The stream for error output */
+ private OutputStream errorStream = null;
+
+ /** The stream for input */
+ private InputStream inputStream = null;
+
+ /** Stream which is used for line oriented output */
+ private PrintStream outPrintStream = null;
+
+ /** Stream which is used for line oriented error output */
+ private PrintStream errorPrintStream = null;
+
+ /** The output filter chains */
+ private Vector<FilterChain> outputFilterChains;
+
+ /** The error filter chains */
+ private Vector<FilterChain> errorFilterChains;
+
+ /** The input filter chains */
+ private Vector<FilterChain> inputFilterChains;
+
+ /** The output encoding */
+ private String outputEncoding = DEFAULT_ENCODING;
+
+ /** The error encoding */
+ private String errorEncoding = DEFAULT_ENCODING;
+
+ /** The input encoding */
+ private String inputEncoding = DEFAULT_ENCODING;
+
+ /** Whether to complete properties settings **/
+ private boolean appendProperties = true;
+
+ /** The thread group used for starting <code>StreamPumper</code> threads */
+ private final ThreadGroup threadGroup = new ThreadGroup("redirector");
+
+ /** whether to log the inputstring */
+ private boolean logInputString = true;
+
+ /** Mutex for in */
+ private final Object inMutex = new Object();
+
+ /** Mutex for out */
+ private final Object outMutex = new Object();
+
+ /** Mutex for err */
+ private final Object errMutex = new Object();
+
+ /** Is the output binary or can we safely split it into lines? */
+ private boolean outputIsBinary = false;
+
+ /**
+ * Create a redirector instance for the given task
+ *
+ * @param managingTask
+ * the task for which the redirector is to work
+ */
+ public Redirector(final Task managingTask) {
+ this((ProjectComponent) managingTask);
+ }
+
+ /**
+ * Create a redirector instance for the given task
+ *
+ * @param managingTask
+ * the project component for which the redirector is to work
+ * @since Ant 1.6.3
+ */
+ public Redirector(final ProjectComponent managingTask) {
+ this.managingTask = managingTask;
+ }
+
+ /**
+ * Set the input to use for the task
+ *
+ * @param input
+ * the file from which input is read.
+ */
+ public void setInput(final File input) {
+ setInput((input == null) ? null : new File[] {input});
+ }
+
+ /**
+ * Set the input to use for the task
+ *
+ * @param input
+ * the files from which input is read.
+ */
+ public void setInput(final File[] input) {
+ synchronized (inMutex) {
+ if (input == null) {
+ this.input = null;
+ } else {
+ this.input = input.clone();
+ }
+ }
+ }
+
+ /**
+ * Set the string to use as input
+ *
+ * @param inputString
+ * the string which is used as the input source
+ */
+ public void setInputString(final String inputString) {
+ synchronized (inMutex) {
+ this.inputString = inputString;
+ }
+ }
+
+ /**
+ * Set whether to include the value of the input string in log messages.
+ * Defaults to true.
+ *
+ * @param logInputString
+ * true or false.
+ * @since Ant 1.7
+ */
+ public void setLogInputString(final boolean logInputString) {
+ this.logInputString = logInputString;
+ }
+
+ /**
+ * Set a stream to use as input.
+ *
+ * @param inputStream
+ * the stream from which input will be read
+ * @since Ant 1.6.3
+ */
+ /* public */void setInputStream(final InputStream inputStream) {
+ synchronized (inMutex) {
+ this.inputStream = inputStream;
+ }
+ }
+
+ /**
+ * File the output of the process is redirected to. If error is not
+ * redirected, it too will appear in the output
+ *
+ * @param out
+ * the file to which output stream is written
+ */
+ public void setOutput(final File out) {
+ setOutput((out == null) ? null : new File[] {out});
+ }
+
+ /**
+ * Files the output of the process is redirected to. If error is not
+ * redirected, it too will appear in the output
+ *
+ * @param out
+ * the files to which output stream is written
+ */
+ public void setOutput(final File[] out) {
+ synchronized (outMutex) {
+ if (out == null) {
+ this.out = null;
+ } else {
+ this.out = out.clone();
+ }
+ }
+ }
+
+ /**
+ * Set the output encoding.
+ *
+ * @param outputEncoding
+ * <code>String</code>.
+ */
+ public void setOutputEncoding(final String outputEncoding) {
+ if (outputEncoding == null) {
+ throw new IllegalArgumentException(
+ "outputEncoding must not be null");
+ }
+ synchronized (outMutex) {
+ this.outputEncoding = outputEncoding;
+ }
+ }
+
+ /**
+ * Set the error encoding.
+ *
+ * @param errorEncoding
+ * <code>String</code>.
+ */
+ public void setErrorEncoding(final String errorEncoding) {
+ if (errorEncoding == null) {
+ throw new IllegalArgumentException("errorEncoding must not be null");
+ }
+ synchronized (errMutex) {
+ this.errorEncoding = errorEncoding;
+ }
+ }
+
+ /**
+ * Set the input encoding.
+ *
+ * @param inputEncoding
+ * <code>String</code>.
+ */
+ public void setInputEncoding(final String inputEncoding) {
+ if (inputEncoding == null) {
+ throw new IllegalArgumentException("inputEncoding must not be null");
+ }
+ synchronized (inMutex) {
+ this.inputEncoding = inputEncoding;
+ }
+ }
+
+ /**
+ * Controls whether error output of exec is logged. This is only useful when
+ * output is being redirected and error output is desired in the Ant log
+ *
+ * @param logError
+ * if true the standard error is sent to the Ant log system and
+ * not sent to output.
+ */
+ public void setLogError(final boolean logError) {
+ synchronized (errMutex) {
+ this.logError = logError;
+ }
+ }
+
+ /**
+ * This <code>Redirector</code>'s subordinate
+ * <code>PropertyOutputStream</code>s will not set their respective
+ * properties <code>while (appendProperties &amp;&amp; append)</code>.
+ *
+ * @param appendProperties
+ * whether to append properties.
+ */
+ public void setAppendProperties(final boolean appendProperties) {
+ synchronized (outMutex) {
+ this.appendProperties = appendProperties;
+ }
+ }
+
+ /**
+ * 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(final File error) {
+ setError((error == null) ? null : new File[] {error});
+ }
+
+ /**
+ * Set the files to which standard error is to be redirected.
+ *
+ * @param error
+ * the file to which error is to be written
+ */
+ public void setError(final File[] error) {
+ synchronized (errMutex) {
+ if (error == null) {
+ this.error = null;
+ } else {
+ this.error = error.clone();
+ }
+ }
+ }
+
+ /**
+ * Property name whose value should be set to the output of the process.
+ *
+ * @param outputProperty
+ * the name of the property to be set with the task's output.
+ */
+ public void setOutputProperty(final String outputProperty) {
+ if (outputProperty == null
+ || !(outputProperty.equals(this.outputProperty))) {
+ synchronized (outMutex) {
+ this.outputProperty = outputProperty;
+ baos = null;
+ }
+ }
+ }
+
+ /**
+ * Whether output should be appended to or overwrite an existing file.
+ * Defaults to false.
+ *
+ * @param append
+ * if true output and error streams are appended to their
+ * respective files, if specified.
+ */
+ public void setAppend(final boolean append) {
+ synchronized (outMutex) {
+ appendOut = append;
+ }
+ synchronized (errMutex) {
+ appendErr = append;
+ }
+ }
+
+ /**
+ * If true, (error and non-error) output will be "teed", redirected as
+ * specified while being sent to Ant's logging mechanism as if no
+ * redirection had taken place. Defaults to false.
+ *
+ * @param alwaysLog
+ * <code>boolean</code>
+ * @since Ant 1.6.3
+ */
+ public void setAlwaysLog(final boolean alwaysLog) {
+ synchronized (outMutex) {
+ alwaysLogOut = alwaysLog;
+ }
+ synchronized (errMutex) {
+ alwaysLogErr = alwaysLog;
+ }
+ }
+
+ /**
+ * Whether output and error files should be created even when empty.
+ * Defaults to true.
+ *
+ * @param createEmptyFiles
+ * <code>boolean</code>.
+ */
+ public void setCreateEmptyFiles(final boolean createEmptyFiles) {
+ synchronized (outMutex) {
+ createEmptyFilesOut = createEmptyFiles;
+ }
+ synchronized (outMutex) {
+ createEmptyFilesErr = createEmptyFiles;
+ }
+ }
+
+ /**
+ * Property name whose value should be set to the error of the process.
+ *
+ * @param errorProperty
+ * the name of the property to be set with the error output.
+ */
+ public void setErrorProperty(final String errorProperty) {
+ synchronized (errMutex) {
+ if (errorProperty == null
+ || !(errorProperty.equals(this.errorProperty))) {
+ this.errorProperty = errorProperty;
+ errorBaos = null;
+ }
+ }
+ }
+
+ /**
+ * Set the input <code>FilterChain</code>s.
+ *
+ * @param inputFilterChains
+ * <code>Vector</code> containing <code>FilterChain</code>.
+ */
+ public void setInputFilterChains(final Vector<FilterChain> inputFilterChains) {
+ synchronized (inMutex) {
+ this.inputFilterChains = inputFilterChains;
+ }
+ }
+
+ /**
+ * Set the output <code>FilterChain</code>s.
+ *
+ * @param outputFilterChains
+ * <code>Vector</code> containing <code>FilterChain</code>.
+ */
+ public void setOutputFilterChains(final Vector<FilterChain> outputFilterChains) {
+ synchronized (outMutex) {
+ this.outputFilterChains = outputFilterChains;
+ }
+ }
+
+ /**
+ * Set the error <code>FilterChain</code>s.
+ *
+ * @param errorFilterChains
+ * <code>Vector</code> containing <code>FilterChain</code>.
+ */
+ public void setErrorFilterChains(final Vector<FilterChain> errorFilterChains) {
+ synchronized (errMutex) {
+ this.errorFilterChains = errorFilterChains;
+ }
+ }
+
+ /**
+ * Whether to consider the output created by the process binary.
+ *
+ * <p>Binary output will not be split into lines which may make
+ * error and normal output look mixed up when they get written to
+ * the same stream.</p>
+ * @since 1.9.4
+ */
+ public void setBinaryOutput(final boolean b) {
+ outputIsBinary = b;
+ }
+
+ /**
+ * 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(final ByteArrayOutputStream baos,
+ final String propertyName) throws IOException {
+
+ final BufferedReader in = new BufferedReader(new StringReader(Execute
+ .toString(baos)));
+ String line = null;
+ final StringBuffer val = new StringBuffer();
+ while ((line = in.readLine()) != null) {
+ if (val.length() != 0) {
+ val.append(StringUtils.LINE_SEP);
+ }
+ val.append(line);
+ }
+ managingTask.getProject().setNewProperty(propertyName, val.toString());
+ }
+
+ /**
+ * Create the input, error and output streams based on the configuration
+ * options.
+ */
+ public void createStreams() {
+
+ synchronized (outMutex) {
+ outStreams();
+ if (alwaysLogOut || outputStream == null) {
+ final OutputStream outputLog = new LogOutputStream(managingTask,
+ Project.MSG_INFO);
+ outputStream = (outputStream == null) ? outputLog
+ : new TeeOutputStream(outputLog, outputStream);
+ }
+
+ if ((outputFilterChains != null && outputFilterChains.size() > 0)
+ || !(outputEncoding.equalsIgnoreCase(inputEncoding))) {
+ try {
+ final LeadPipeInputStream snk = new LeadPipeInputStream();
+ snk.setManagingComponent(managingTask);
+
+ InputStream outPumpIn = snk;
+
+ Reader reader = new InputStreamReader(outPumpIn,
+ inputEncoding);
+
+ if (outputFilterChains != null
+ && outputFilterChains.size() > 0) {
+ final ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setProject(managingTask.getProject());
+ helper.setPrimaryReader(reader);
+ helper.setFilterChains(outputFilterChains);
+ reader = helper.getAssembledReader();
+ }
+ outPumpIn = new ReaderInputStream(reader, outputEncoding);
+
+ final Thread t = new Thread(threadGroup, new StreamPumper(
+ outPumpIn, outputStream, true), "output pumper");
+ t.setPriority(Thread.MAX_PRIORITY);
+ outputStream = new PipedOutputStream(snk);
+ t.start();
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException("error setting up output stream",
+ eyeOhEx);
+ }
+ }
+ }
+
+ synchronized (errMutex) {
+ errorStreams();
+ if (alwaysLogErr || errorStream == null) {
+ final OutputStream errorLog = new LogOutputStream(managingTask,
+ Project.MSG_WARN);
+ errorStream = (errorStream == null) ? errorLog
+ : new TeeOutputStream(errorLog, errorStream);
+ }
+
+ if ((errorFilterChains != null && errorFilterChains.size() > 0)
+ || !(errorEncoding.equalsIgnoreCase(inputEncoding))) {
+ try {
+ final LeadPipeInputStream snk = new LeadPipeInputStream();
+ snk.setManagingComponent(managingTask);
+
+ InputStream errPumpIn = snk;
+
+ Reader reader = new InputStreamReader(errPumpIn,
+ inputEncoding);
+
+ if (errorFilterChains != null
+ && errorFilterChains.size() > 0) {
+ final ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setProject(managingTask.getProject());
+ helper.setPrimaryReader(reader);
+ helper.setFilterChains(errorFilterChains);
+ reader = helper.getAssembledReader();
+ }
+ errPumpIn = new ReaderInputStream(reader, errorEncoding);
+
+ final Thread t = new Thread(threadGroup, new StreamPumper(
+ errPumpIn, errorStream, true), "error pumper");
+ t.setPriority(Thread.MAX_PRIORITY);
+ errorStream = new PipedOutputStream(snk);
+ t.start();
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException("error setting up error stream",
+ eyeOhEx);
+ }
+ }
+ }
+
+ synchronized (inMutex) {
+ // if input files are specified, inputString and inputStream are
+ // ignored;
+ // classes that work with redirector attributes can enforce
+ // whatever warnings are needed
+ if (input != null && input.length > 0) {
+ managingTask
+ .log("Redirecting input from file"
+ + ((input.length == 1) ? "" : "s"),
+ Project.MSG_VERBOSE);
+ try {
+ inputStream = new ConcatFileInputStream(input);
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException(eyeOhEx);
+ }
+ ((ConcatFileInputStream) inputStream)
+ .setManagingComponent(managingTask);
+ } else if (inputString != null) {
+ final StringBuffer buf = new StringBuffer("Using input ");
+ if (logInputString) {
+ buf.append('"').append(inputString).append('"');
+ } else {
+ buf.append("string");
+ }
+ managingTask.log(buf.toString(), Project.MSG_VERBOSE);
+ inputStream = new ByteArrayInputStream(inputString.getBytes());
+ }
+
+ if (inputStream != null && inputFilterChains != null
+ && inputFilterChains.size() > 0) {
+ final ChainReaderHelper helper = new ChainReaderHelper();
+ helper.setProject(managingTask.getProject());
+ try {
+ helper.setPrimaryReader(new InputStreamReader(inputStream,
+ inputEncoding));
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException("error setting up input stream",
+ eyeOhEx);
+ }
+ helper.setFilterChains(inputFilterChains);
+ inputStream = new ReaderInputStream(
+ helper.getAssembledReader(), inputEncoding);
+ }
+ }
+ }
+
+ /** outStreams */
+ private void outStreams() {
+ if (out != null && out.length > 0) {
+ final String logHead = new StringBuffer("Output ").append(
+ ((appendOut) ? "appended" : "redirected")).append(" to ")
+ .toString();
+ outputStream = foldFiles(out, logHead, Project.MSG_VERBOSE,
+ appendOut, createEmptyFilesOut);
+ }
+ if (outputProperty != null) {
+ if (baos == null) {
+ baos = new PropertyOutputStream(outputProperty);
+ managingTask.log("Output redirected to property: "
+ + outputProperty, Project.MSG_VERBOSE);
+ }
+ // shield it from being closed by a filtering StreamPumper
+ final OutputStream keepAliveOutput = new KeepAliveOutputStream(baos);
+ outputStream = (outputStream == null) ? keepAliveOutput
+ : new TeeOutputStream(outputStream, keepAliveOutput);
+ } else {
+ baos = null;
+ }
+ }
+
+ private void errorStreams() {
+ if (error != null && error.length > 0) {
+ final String logHead = new StringBuffer("Error ").append(
+ ((appendErr) ? "appended" : "redirected")).append(" to ")
+ .toString();
+ errorStream = foldFiles(error, logHead, Project.MSG_VERBOSE,
+ appendErr, createEmptyFilesErr);
+ } else if (!(logError || outputStream == null) && errorProperty == null) {
+ final long funnelTimeout = 0L;
+ final OutputStreamFunneler funneler = new OutputStreamFunneler(
+ outputStream, funnelTimeout);
+ try {
+ outputStream = funneler.getFunnelInstance();
+ errorStream = funneler.getFunnelInstance();
+ if (!outputIsBinary) {
+ outputStream = new LineOrientedOutputStreamRedirector(outputStream);
+ errorStream = new LineOrientedOutputStreamRedirector(errorStream);
+ }
+ } catch (final IOException eyeOhEx) {
+ throw new BuildException(
+ "error splitting output/error streams", eyeOhEx);
+ }
+ }
+ if (errorProperty != null) {
+ if (errorBaos == null) {
+ errorBaos = new PropertyOutputStream(errorProperty);
+ managingTask.log("Error redirected to property: "
+ + errorProperty, Project.MSG_VERBOSE);
+ }
+ // shield it from being closed by a filtering StreamPumper
+ final OutputStream keepAliveError = new KeepAliveOutputStream(errorBaos);
+ errorStream = (error == null || error.length == 0) ? keepAliveError
+ : new TeeOutputStream(errorStream, keepAliveError);
+ } else {
+ errorBaos = null;
+ }
+ }
+
+ /**
+ * Create the StreamHandler to use with our Execute instance.
+ *
+ * @return the execute stream handler to manage the input, output and error
+ * streams.
+ *
+ * @throws BuildException
+ * if the execute stream handler cannot be created.
+ */
+ public ExecuteStreamHandler createHandler() throws BuildException {
+ createStreams();
+ final boolean nonBlockingRead = input == null && inputString == null;
+ return new PumpStreamHandler(getOutputStream(), getErrorStream(),
+ getInputStream(), nonBlockingRead);
+ }
+
+ /**
+ * Pass output sent to System.out to specified output.
+ *
+ * @param output
+ * the data to be output
+ */
+ protected void handleOutput(final String output) {
+ synchronized (outMutex) {
+ if (outPrintStream == null) {
+ outPrintStream = new PrintStream(outputStream);
+ }
+ outPrintStream.print(output);
+ }
+ }
+
+ /**
+ * 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(final byte[] buffer, final int offset, final int length)
+ throws IOException {
+ synchronized (inMutex) {
+ if (inputStream == null) {
+ return managingTask.getProject().defaultInput(buffer, offset,
+ length);
+ }
+ return inputStream.read(buffer, offset, length);
+
+ }
+ }
+
+ /**
+ * Process data due to a flush operation.
+ *
+ * @param output
+ * the data being flushed.
+ */
+ protected void handleFlush(final String output) {
+ synchronized (outMutex) {
+ if (outPrintStream == null) {
+ outPrintStream = new PrintStream(outputStream);
+ }
+ outPrintStream.print(output);
+ outPrintStream.flush();
+ }
+ }
+
+ /**
+ * Process error output
+ *
+ * @param output
+ * the error output data.
+ */
+ protected void handleErrorOutput(final String output) {
+ synchronized (errMutex) {
+ if (errorPrintStream == null) {
+ errorPrintStream = new PrintStream(errorStream);
+ }
+ errorPrintStream.print(output);
+ }
+ }
+
+ /**
+ * Handle a flush operation on the error stream
+ *
+ * @param output
+ * the error information being flushed.
+ */
+ protected void handleErrorFlush(final String output) {
+ synchronized (errMutex) {
+ if (errorPrintStream == null) {
+ errorPrintStream = new PrintStream(errorStream);
+ }
+ errorPrintStream.print(output);
+ errorPrintStream.flush();
+ }
+ }
+
+ /**
+ * Get the output stream for the redirector
+ *
+ * @return the redirector's output stream or null if no output has been
+ * configured
+ */
+ public OutputStream getOutputStream() {
+ synchronized (outMutex) {
+ 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() {
+ synchronized (errMutex) {
+ 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() {
+ synchronized (inMutex) {
+ return inputStream;
+ }
+ }
+
+ /**
+ * Complete redirection.
+ *
+ * This operation will close any streams and create any specified property
+ * values.
+ *
+ * @throws IOException
+ * if the output properties cannot be read from their output
+ * streams.
+ */
+ public void complete() throws IOException {
+ System.out.flush();
+ System.err.flush();
+
+ synchronized (inMutex) {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+
+ synchronized (outMutex) {
+ outputStream.flush();
+ outputStream.close();
+ }
+
+ synchronized (errMutex) {
+ errorStream.flush();
+ errorStream.close();
+ }
+
+ // wait for the StreamPumpers to finish
+ synchronized (this) {
+ while (threadGroup.activeCount() > 0) {
+ try {
+ managingTask.log("waiting for " + threadGroup.activeCount()
+ + " Threads:", Project.MSG_DEBUG);
+ final Thread[] thread = new Thread[threadGroup.activeCount()];
+ threadGroup.enumerate(thread);
+ for (int i = 0; i < thread.length && thread[i] != null; i++) {
+ try {
+ managingTask.log(thread[i].toString(),
+ Project.MSG_DEBUG);
+ } catch (final NullPointerException enPeaEx) {
+ // Ignore exception
+ }
+ }
+ wait(STREAMPUMPER_WAIT_INTERVAL);
+ } catch (final InterruptedException eyeEx) {
+ final Thread[] thread = new Thread[threadGroup.activeCount()];
+ threadGroup.enumerate(thread);
+ for (int i = 0; i < thread.length && thread[i] != null; i++) {
+ thread[i].interrupt();
+ }
+ }
+ }
+ }
+
+ setProperties();
+
+ synchronized (inMutex) {
+ inputStream = null;
+ }
+ synchronized (outMutex) {
+ outputStream = null;
+ outPrintStream = null;
+ }
+ synchronized (errMutex) {
+ errorStream = null;
+ errorPrintStream = null;
+ }
+ }
+
+ /**
+ * Notify the <code>Redirector</code> that it is now okay to set any output
+ * and/or error properties.
+ */
+ public void setProperties() {
+ synchronized (outMutex) {
+ if (baos != null) {
+ try {
+ baos.close();
+ } catch (final IOException eyeOhEx) {
+ // Ignore exception
+ }
+ }
+ }
+ synchronized (errMutex) {
+ if (errorBaos != null) {
+ try {
+ errorBaos.close();
+ } catch (final IOException eyeOhEx) {
+ // Ignore exception
+ }
+ }
+ }
+ }
+
+ private OutputStream foldFiles(final File[] file, final String logHead, final int loglevel,
+ final boolean append, final boolean createEmptyFiles) {
+ final OutputStream result = new LazyFileOutputStream(file[0], append,
+ createEmptyFiles);
+
+ managingTask.log(logHead + file[0], loglevel);
+ final char[] c = new char[logHead.length()];
+ Arrays.fill(c, ' ');
+ final String indent = new String(c);
+
+ for (int i = 1; i < file.length; i++) {
+ outputStream = new TeeOutputStream(outputStream,
+ new LazyFileOutputStream(file[i], append, createEmptyFiles));
+ managingTask.log(indent + file[i], loglevel);
+ }
+ return result;
+ }
+}