Index: ant/src/org/apache/tools/ant/module/bridge/AntBridge.java =================================================================== RCS file: /cvs/ant/src/org/apache/tools/ant/module/bridge/AntBridge.java,v --- ant/src/org/apache/tools/ant/module/bridge/AntBridge.java 11 May 2004 22:28:10 -0000 1.14 +++ ant/src/org/apache/tools/ant/module/bridge/AntBridge.java 23 May 2004 22:16:32 -0000 @@ -32,6 +32,7 @@ import org.openide.modules.InstalledFileLocator; import org.openide.modules.ModuleInfo; import org.openide.util.*; +import org.openide.util.io.NullOutputStream; import org.openide.xml.XMLUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -504,48 +505,259 @@ } - // XXX better would be to multiplex to the currently running Ant process, as - // determined by the current thread group - // Better still would be to let the execution engine handle it entirely, - // by creating a custom InputOutput: #1961 - + // I/O redirection impl. Keyed by thread group (each Ant process has its own TG). + // Various Ant tasks (e.g. ) need the system + // I/O streams to be redirected to the demux streams of the project so they can + // be handled properly. Ideally nothing would try to read directly from stdin + // or print directly to stdout/stderr but in fact some tasks do. + // Could also pass a custom InputOutput to ExecutionEngine, perhaps, but this + // seems a lot simpler and probably has the same effect. + + private static int delegating = 0; private static InputStream origIn; private static PrintStream origOut, origErr; + private static Map/**/ delegateIns = new HashMap(); + private static Map/**/ delegateOuts = new HashMap(); + private static Map/**/ delegateErrs = new HashMap(); /** * Handle I/O scoping for overlapping project runs. * You must call {@link #restoreSystemInOutErr} in a finally block. - * @param out new temporary output stream for the VM - * @param err new temporary error stream for the VM + * @param in new temporary input stream for this thread group + * @param out new temporary output stream for this thread group + * @param err new temporary error stream for this thread group * @see "#36396" */ public static synchronized void pushSystemInOutErr(InputStream in, PrintStream out, PrintStream err) { - if (origOut == null) { + if (delegating++ == 0) { origIn = System.in; origOut = System.out; origErr = System.err; - } else { - // Oh well, old output may be sent to the wrong window... - } - System.setIn(in); - System.setOut(out); - System.setErr(err); + System.setIn(new MultiplexInputStream()); + System.setOut(new MultiplexPrintStream(false)); + System.setErr(new MultiplexPrintStream(true)); + } + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + delegateIns.put(tg, in); + delegateOuts.put(tg, out); + delegateErrs.put(tg, err); } /** * Restore original I/O streams after a call to {@link #pushSystemInOutErr}. */ public static synchronized void restoreSystemInOutErr() { - if (origOut != null) { + assert delegating > 0; + if (--delegating == 0) { System.setIn(origIn); - System.setErr(origErr); System.setOut(origOut); + System.setErr(origErr); origIn = null; origOut = null; origErr = null; - } else { - // Again, never mind. } + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + delegateIns.remove(tg); + delegateOuts.remove(tg); + delegateErrs.remove(tg); + } + + private static final class MultiplexInputStream extends InputStream { + + public MultiplexInputStream() {} + + private InputStream delegate() { + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + while (tg != null && !delegateIns.containsKey(tg)) { + tg = tg.getParent(); + } + InputStream is = (InputStream)delegateIns.get(tg); + if (is != null) { + return is; + } else if (delegating > 0) { + assert origIn != null; + return origIn; + } else { + // Probably should not happen? But not sure. + return System.in; + } + } + + public int read() throws IOException { + return delegate().read(); + } + + public int read(byte[] b) throws IOException { + return delegate().read(b); + } + + public int read(byte[] b, int off, int len) throws IOException { + return delegate().read(b, off, len); + } + + public int available() throws IOException { + return delegate().available(); + } + + public boolean markSupported() { + return delegate().markSupported(); + } + + public void mark(int readlimit) { + delegate().mark(readlimit); + } + + public void close() throws IOException { + delegate().close(); + } + + public long skip(long n) throws IOException { + return delegate().skip(n); + } + + public void reset() throws IOException { + delegate().reset(); + } + } + private static final class MultiplexPrintStream extends PrintStream { + + private final boolean err; + + public MultiplexPrintStream(boolean err) { + this(new NullOutputStream(), err); + } + + private MultiplexPrintStream(NullOutputStream nos, boolean err) { + super(nos); + nos.throwException = true; + this.err = err; + } + + private PrintStream delegate() { + ThreadGroup tg = Thread.currentThread().getThreadGroup(); + Map/**/ delegates = err ? delegateErrs : delegateOuts; + while (tg != null && !delegates.containsKey(tg)) { + tg = tg.getParent(); + } + PrintStream ps = (PrintStream)delegates.get(tg); + if (ps != null) { + return ps; + } else if (delegating > 0) { + PrintStream orig = err ? origErr : origOut; + assert orig != null; + return orig; + } else { + // Probably should not happen? But not sure. + return err ? System.err : System.out; + } + } + + public boolean checkError() { + return delegate().checkError(); + } + + public void close() { + delegate().close(); + } + + public void flush() { + delegate().flush(); + } + + public void print(long l) { + delegate().print(l); + } + + public void print(char[] s) { + delegate().print(s); + } + + public void print(int i) { + delegate().print(i); + } + + public void print(boolean b) { + delegate().print(b); + } + + public void print(char c) { + delegate().print(c); + } + + public void print(float f) { + delegate().print(f); + } + + public void print(double d) { + delegate().print(d); + } + + public void print(Object obj) { + delegate().print(obj); + } + + public void print(String s) { + delegate().print(s); + } + + public void println(double x) { + delegate().println(x); + } + + public void println(Object x) { + delegate().println(x); + } + + public void println(float x) { + delegate().println(x); + } + + public void println(int x) { + delegate().println(x); + } + + public void println(char x) { + delegate().println(x); + } + + public void println(boolean x) { + delegate().println(x); + } + + public void println(String x) { + delegate().println(x); + } + + public void println(char[] x) { + delegate().println(x); + } + + public void println() { + delegate().println(); + } + + public void println(long x) { + delegate().println(x); + } + + public void write(int b) { + delegate().write(b); + } + + public void write(byte[] b) throws IOException { + delegate().write(b); + } + + public void write(byte[] b, int off, int len) { + delegate().write(b, off, len); + } + + // XXX printf/format with varargs cannot be overridden here (JDK 1.5 specific) + // nor can append(char,CharSequence) + // probably does not matter however... + + } + }