Skip to content

Commit 4c6fa30

Browse files
committed
Notify listeners of stdout and stderr changes
This change completes the loop, such that stdout and stderr messages are now reported back to all ConsoleService listeners as desired. When output occurs, something needs to tell the ConsoleService, so that it can tell all its listeners what happened. That's where the OutputStreamReporter comes in: it is added to the MultiOutputStream, which is wrapped by a MultiPrintStream, which overrides the default System.out or System.err, such that when output occurs, the OutputStreamReporter finds out and calls ConsoleService#notifyListeners. This change also cleans up after itself when the context is disposed, removing the OutputStreamReporters as appropriate.
1 parent 7d87821 commit 4c6fa30

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

src/main/java/org/scijava/console/DefaultConsoleService.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,20 @@
3131

3232
package org.scijava.console;
3333

34+
import java.io.OutputStream;
3435
import java.io.PrintStream;
3536
import java.util.ArrayList;
3637
import java.util.LinkedList;
3738

39+
import org.scijava.Context;
40+
import org.scijava.console.OutputEvent.Source;
3841
import org.scijava.log.LogService;
3942
import org.scijava.plugin.AbstractHandlerService;
4043
import org.scijava.plugin.Parameter;
4144
import org.scijava.plugin.Plugin;
4245
import org.scijava.service.Service;
46+
import org.scijava.thread.ThreadService;
47+
import org.scijava.thread.ThreadService.ThreadContext;
4348

4449
/**
4550
* Default service for managing interaction with the console.
@@ -52,10 +57,14 @@ public class DefaultConsoleService extends
5257
ConsoleService
5358
{
5459

60+
@Parameter
61+
private ThreadService threadService;
62+
5563
@Parameter
5664
private LogService log;
5765

5866
private MultiPrintStream sysout, syserr;
67+
private OutputStreamReporter out, err;
5968

6069
/** List of listeners for {@code stdout} and {@code stderr} output. */
6170
private ArrayList<OutputListener> listeners;
@@ -124,6 +133,14 @@ public Class<LinkedList<String>> getType() {
124133
return (Class) LinkedList.class;
125134
}
126135

136+
// -- Disposable methods --
137+
138+
@Override
139+
public void dispose() {
140+
if (out != null) sysout.getParent().removeOutputStream(out);
141+
if (err != null) syserr.getParent().removeOutputStream(err);
142+
}
143+
127144
// -- Helper methods - lazy initialization --
128145

129146
/** Initializes {@link #listeners} and related data structures. */
@@ -132,8 +149,13 @@ private synchronized void initListeners() {
132149

133150
sysout = multiPrintStream(System.out);
134151
if (System.out != sysout) System.setOut(sysout);
152+
out = new OutputStreamReporter(Source.STDOUT);
153+
sysout.getParent().addOutputStream(out);
154+
135155
syserr = multiPrintStream(System.err);
136156
if (System.err != syserr) System.setErr(syserr);
157+
err = new OutputStreamReporter(Source.STDERR);
158+
syserr.getParent().addOutputStream(err);
137159

138160
listeners = new ArrayList<OutputListener>();
139161
}
@@ -145,4 +167,50 @@ private MultiPrintStream multiPrintStream(final PrintStream ps) {
145167
return new MultiPrintStream(ps);
146168
}
147169

170+
// -- Helper classes --
171+
172+
/**
173+
* An output stream that publishes its output to the {@link OutputListener}s
174+
* of its associated {@link ConsoleService}.
175+
*/
176+
private class OutputStreamReporter extends OutputStream {
177+
178+
/** Source of the output stream; i.e., {@code stdout} or {@code stderr}. */
179+
private final Source source;
180+
181+
public OutputStreamReporter(final Source source) {
182+
this.source = source;
183+
}
184+
185+
// -- OutputStream methods --
186+
187+
@Override
188+
public void write(final int b) {
189+
final ThreadContext relevance = getRelevance();
190+
if (relevance == ThreadContext.OTHER) return; // different context
191+
publish(relevance, "" + b);
192+
}
193+
194+
@Override
195+
public void write(final byte[] buf, final int off, final int len) {
196+
final ThreadContext relevance = getRelevance();
197+
if (relevance == ThreadContext.OTHER) return; // different context
198+
publish(relevance, new String(buf, off, len));
199+
}
200+
201+
// -- Helper methods --
202+
203+
private ThreadContext getRelevance() {
204+
return threadService.getThreadContext(Thread.currentThread());
205+
}
206+
207+
private void publish(final ThreadContext relevance, final String output) {
208+
final Context context = getContext();
209+
final boolean contextual = relevance == ThreadContext.SAME;
210+
final OutputEvent event =
211+
new OutputEvent(context, source, output, contextual);
212+
notifyListeners(event);
213+
}
214+
}
215+
148216
}

0 commit comments

Comments
 (0)