From c18cb2eaa93cf8a95c691b888035187fbfe877f5 Mon Sep 17 00:00:00 2001 From: Michael Scharf Date: Tue, 15 Apr 2008 20:19:09 +0000 Subject: [PATCH] NEW - bug 224989: Support contributed services over Terminal connections https://bugs.eclipse.org/bugs/show_bug.cgi?id=224989 --- ...inalToRemoteInjectionOutputStreamTest.java | 175 ++++++++++++++++++ ...TerminalToRemoteInjectionOutputStream.java | 163 ++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 terminal/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStreamTest.java create mode 100644 terminal/org.eclipse.tm.terminal/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStream.java diff --git a/terminal/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStreamTest.java b/terminal/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStreamTest.java new file mode 100644 index 00000000000..4b1d1848f4e --- /dev/null +++ b/terminal/org.eclipse.tm.terminal.test/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStreamTest.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.connector; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import junit.framework.TestCase; + +public class TerminalToRemoteInjectionOutputStreamTest extends TestCase { + final static String ENCODING="UTF-8"; + /** + * This class escapes strings coming on the original + * terminal.. + * + */ + class CleverInterceptor extends TerminalToRemoteInjectionOutputStream.Interceptor { + + public void close() throws IOException { + } + public void write(int b) throws IOException { + fOriginal.write('['); + fOriginal.write(b); + fOriginal.write(']'); + } + public void write(byte[] b, int off, int len) throws IOException { + fOriginal.write('['); + fOriginal.write(b,off,len); + fOriginal.write(']'); + } + + } + class NullInterceptor extends TerminalToRemoteInjectionOutputStream.Interceptor { + } + public void testClose() throws UnsupportedEncodingException, IOException { + ByteArrayOutputStream bs=new ByteArrayOutputStream(); + TerminalToRemoteInjectionOutputStream s= new TerminalToRemoteInjectionOutputStream(bs); + s.write("begin:".getBytes(ENCODING)); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + OutputStream os1=s.grabOutput(); + os1.write('x'); + s.write('A'); + os1.write('y'); + s.write('B'); + os1.close(); + + s.write('-'); + OutputStream os=s.grabOutput(); + // make sure the closed output does not inject anything + try { + os1.write('k'); + fail("..."); + } catch (Exception e) { + } + os.write('X'); + s.write('a'); + os.write('Y'); + // make sure the closed output does not inject anything + try { + os1.write('l'); + fail("..."); + } catch (Exception e) { + } + s.write('b'); + os.close(); + assertEquals("begin:xyAB-XYab", new String(bs.toByteArray(),ENCODING)); + } + + public void testFlush() { + } + + public void testWriteInt() throws UnsupportedEncodingException, IOException { + ByteArrayOutputStream bs=new ByteArrayOutputStream(); + TerminalToRemoteInjectionOutputStream s= new TerminalToRemoteInjectionOutputStream(bs); + s.write("begin:".getBytes(ENCODING)); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + OutputStream os=s.grabOutput(); + os.write('x'); + s.write('A'); + os.write('y'); + s.write('B'); + s.close(); + assertEquals("begin:xyAB", new String(bs.toByteArray(),ENCODING)); + + } + + public void testWriteByteArray() { + } + + public void testWriteByteArrayIntInt() { + } + public void testGrabOutput() throws UnsupportedEncodingException, IOException { + ByteArrayOutputStream bs=new ByteArrayOutputStream(); + TerminalToRemoteInjectionOutputStream s= new TerminalToRemoteInjectionOutputStream(bs); + s.write("begin:".getBytes(ENCODING)); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + OutputStream os1=s.grabOutput(); + OutputStream os2; + try { + os2=s.grabOutput(); + fail("should fail until the foirst output is closed"); + } catch (IOException e) { + } + os1.close(); + os2=s.grabOutput(); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + os2.write("Test".getBytes(ENCODING)); + assertEquals("begin:Test", new String(bs.toByteArray(),ENCODING)); + s.write(" west".getBytes(ENCODING)); + assertEquals("begin:Test", new String(bs.toByteArray(),ENCODING)); + os2.write(" the".getBytes(ENCODING)); + assertEquals("begin:Test the", new String(bs.toByteArray(),ENCODING)); + os2.close(); + assertEquals("begin:Test the west", new String(bs.toByteArray(),ENCODING)); + s.write('!'); + assertEquals("begin:Test the west!", new String(bs.toByteArray(),ENCODING)); + + } + public void testGrabOutputWithCleverInterceptor() throws UnsupportedEncodingException, IOException { + ByteArrayOutputStream bs=new ByteArrayOutputStream(); + TerminalToRemoteInjectionOutputStream s= new TerminalToRemoteInjectionOutputStream(bs); + s.write("begin:".getBytes(ENCODING)); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + // the injector escapes the output coming from the main stream + OutputStream os=s.grabOutput(new CleverInterceptor()); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + os.write("Test".getBytes(ENCODING)); + assertEquals("begin:Test", new String(bs.toByteArray(),ENCODING)); + s.write(" west".getBytes(ENCODING)); + assertEquals("begin:Test[ west]", new String(bs.toByteArray(),ENCODING)); + os.write(" the".getBytes(ENCODING)); + assertEquals("begin:Test[ west] the", new String(bs.toByteArray(),ENCODING)); + s.write('x'); + assertEquals("begin:Test[ west] the[x]", new String(bs.toByteArray(),ENCODING)); + os.close(); + assertEquals("begin:Test[ west] the[x]", new String(bs.toByteArray(),ENCODING)); + s.write('!'); + assertEquals("begin:Test[ west] the[x]!", new String(bs.toByteArray(),ENCODING)); + + } + public void testGrabOutputWithNullInterceptor() throws UnsupportedEncodingException, IOException { + ByteArrayOutputStream bs=new ByteArrayOutputStream(); + TerminalToRemoteInjectionOutputStream s= new TerminalToRemoteInjectionOutputStream(bs); + s.write("begin:".getBytes(ENCODING)); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + // bytes written to the main stream are ignored while the injector + // is active + OutputStream os=s.grabOutput(new NullInterceptor()); + assertEquals("begin:", new String(bs.toByteArray(),ENCODING)); + os.write("Test".getBytes(ENCODING)); + assertEquals("begin:Test", new String(bs.toByteArray(),ENCODING)); + s.write(" west".getBytes(ENCODING)); + assertEquals("begin:Test", new String(bs.toByteArray(),ENCODING)); + os.write(" the".getBytes(ENCODING)); + assertEquals("begin:Test the", new String(bs.toByteArray(),ENCODING)); + s.write('x'); + assertEquals("begin:Test the", new String(bs.toByteArray(),ENCODING)); + os.close(); + assertEquals("begin:Test the", new String(bs.toByteArray(),ENCODING)); + s.write('!'); + assertEquals("begin:Test the!", new String(bs.toByteArray(),ENCODING)); + + } + +} diff --git a/terminal/org.eclipse.tm.terminal/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStream.java b/terminal/org.eclipse.tm.terminal/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStream.java new file mode 100644 index 00000000000..f597ce0ce5c --- /dev/null +++ b/terminal/org.eclipse.tm.terminal/src/org/eclipse/tm/internal/terminal/connector/TerminalToRemoteInjectionOutputStream.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Michael Scharf (Wind River) - initial API and implementation + *******************************************************************************/ +package org.eclipse.tm.internal.terminal.connector; + +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +class TerminalToRemoteInjectionOutputStream extends FilterOutputStream { + /** + * This class handles bytes written to the {@link TerminalToRemoteInjectionOutputStream}. + */ + static abstract public class Interceptor { + protected OutputStream fOriginal; + /** + * @param original the injection into the original stream begins + * @throws IOException + */ + public void begin(OutputStream original) throws IOException { + fOriginal=original; + } + /** + * @param b a byte was written to the {@link TerminalToRemoteInjectionOutputStream}. + * @throws IOException + */ + public void write(int b) throws IOException { + } + /** + * @param b bytes written to the {@link TerminalToRemoteInjectionOutputStream}. + * @param off the start offset in the data. + * @param len the number of bytes to write. + * @throws IOException + */ + public void write(byte[] b, int off, int len) throws IOException { + } + /** + * The injection into the normal stream ends. + * @throws IOException + */ + public void close() throws IOException { + } + public void flush() { + } + } + static public class BufferInterceptor extends Interceptor { + private final ByteArrayOutputStream fBuffer=new ByteArrayOutputStream(); + public void close() throws IOException { + fOriginal.write(fBuffer.toByteArray()); + } + public void write(byte[] b, int off, int len) throws IOException { + fBuffer.write(b, off, len); + } + public void write(int b) throws IOException { + fBuffer.write(b); + } + } + private class TerminalFilterOutputStream extends OutputStream { + final private Object fLock=TerminalToRemoteInjectionOutputStream.this; + public void close() throws IOException { + synchronized(fLock) { + if(fInjection==this) { + flush(); + ungrabOutput(); + } + } + } + public void write(byte[] b, int off, int len) throws IOException { + synchronized(fLock) { + checkStream(); + out.write(b, off, len); + } + } + public void write(byte[] b) throws IOException { + synchronized(fLock) { + checkStream(); + out.write(b); + } + } + public void flush() throws IOException { + synchronized(fLock) { + checkStream(); + out.flush(); + } + } + public void write(int b) throws IOException { + synchronized(fLock) { + checkStream(); + out.write(b); + } + } + private void checkStream() throws IOException { + if(fInjection!=this) + throw new IOException("Stream is closed"); //$NON-NLS-1$ + } + } + private Interceptor fInterceptor; + private TerminalFilterOutputStream fInjection; + public TerminalToRemoteInjectionOutputStream(OutputStream out) { + super(out); + } + synchronized protected void ungrabOutput() throws IOException { + if(fInterceptor!=null) { + fInterceptor.close(); + fInterceptor=null; + fInjection=null; + } + } + /** + * There can only be one injection stream active at a time. You must call close on the + * returned output stream to end the injection. + * @param interceptor This is used handle bytes sent while the injection stream is active. + * @return a output stream that can be used to write to the decorated stream. + * @throws IOException + */ + public synchronized OutputStream grabOutput(Interceptor interceptor) throws IOException { + if(fInjection!=null) { + throw new IOException("Buffer in use"); //$NON-NLS-1$ + } + fInterceptor=interceptor; + fInterceptor.begin(out); + fInjection=new TerminalFilterOutputStream(); + return fInjection; + } + /** See {@link #grabOutput(TerminalToRemoteInjectionOutputStream.Interceptor)}. + * @return injection output stream + * @throws IOException + */ + public synchronized OutputStream grabOutput() throws IOException { + return grabOutput(new BufferInterceptor()); + } + synchronized public void close() throws IOException { + if(fInjection!=null) { + fInjection.close(); + } + super.close(); + } + synchronized public void flush() throws IOException { + if(fInterceptor!=null) + fInterceptor.flush(); + out.flush(); + } + synchronized public void write(byte[] b, int off, int len) throws IOException { + if(fInterceptor!=null) + fInterceptor.write(b, off, len); + else + out.write(b, off, len); + } + synchronized public void write(int b) throws IOException { + if(fInterceptor!=null) + fInterceptor.write(b); + else + out.write(b); + } +}