bug 328566: enhanced telnet support over the equinox console
diff --git a/org.eclipse.virgo.osgi.console/.classpath b/org.eclipse.virgo.osgi.console/.classpath
new file mode 100644
index 0000000..bbd454f
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+	<classpathentry kind="src" path="src/main/java"/>
+	<classpathentry kind="src" path="src/test/java"/>
+	<classpathentry kind="var" path="OSGI_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-4.7.0.jar" sourcepath="/OSGI_IVY_CACHE/org.junit/com.springsource.org.junit/4.7.0/com.springsource.org.junit-sources-4.7.0.jar"/>
+	<classpathentry kind="var" path="OSGI_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.7.0.v20101022/org.eclipse.osgi-3.7.0.v20101022.jar" sourcepath="/OSGI_IVY_CACHE/org.eclipse.osgi/org.eclipse.osgi/3.7.0.v20101022/org.eclipse.osgi-sources-3.7.0.v20101022.jar"/>
+	<classpathentry kind="var" path="OSGI_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-2.3.0.jar" sourcepath="/OSGI_IVY_CACHE/org.easymock/com.springsource.org.easymock/2.3.0/com.springsource.org.easymock-sources-2.3.0.jar"/>
+	<classpathentry kind="var" path="OSGI_IVY_CACHE/org.eclipse.virgo.teststubs/org.eclipse.virgo.teststubs.osgi/2.2.0.D-20101207145338/org.eclipse.virgo.teststubs.osgi-2.2.0.D-20101207145338.jar" sourcepath="OSGI_IVY_CACHE/org.eclipse.virgo.teststubs/org.eclipse.virgo.teststubs.osgi/2.2.0.D-20101207145338/org.eclipse.virgo.teststubs.osgi-sources-2.2.0.D-20101207145338.jar"/>
+	<classpathentry kind="var" path="OSGI_IVY_CACHE/org.aspectj/com.springsource.org.aspectj.runtime/1.6.6.RELEASE/com.springsource.org.aspectj.runtime-1.6.6.RELEASE.jar" sourcepath="/OSGI_IVY_CACHE/org.aspectj/com.springsource.org.aspectj.runtime/1.6.6.RELEASE/com.springsource.org.aspectj.runtime-sources-1.6.6.RELEASE.jar"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/org.eclipse.virgo.osgi.console/.project b/org.eclipse.virgo.osgi.console/.project
new file mode 100644
index 0000000..b8d3068
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>org.eclipse.virgo.osgi.console</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.ManifestBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.pde.SchemaBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.pde.PluginNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/org.eclipse.virgo.osgi.console/build.xml b/org.eclipse.virgo.osgi.console/build.xml
new file mode 100644
index 0000000..0c6ddcb
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/build.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="org.eclipse.virgo.osgi.console">
+
+	<property file="${basedir}/../build.properties"/>
+	<property file="${basedir}/../build.versions"/>
+	<!--import file="${basedir}/smoke-test.xml"/-->
+	<import file="${basedir}/../virgo-build/standard/default.xml"/>
+	
+	<!--target name="test.do" depends="quality-common.test.do, smoke-test"/-->
+
+</project>
diff --git a/org.eclipse.virgo.osgi.console/ivy.xml b/org.eclipse.virgo.osgi.console/ivy.xml
new file mode 100644
index 0000000..10c01c4
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/ivy.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
+<ivy-module
+		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+		xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
+		version="1.3">
+
+	<info organisation="org.eclipse.virgo.osgi" module="${ant.project.name}" />
+
+	<configurations>
+		<include file="${virgo.build.dir}/common/default-ivy-configurations.xml"/>
+	</configurations>
+
+	<publications>
+		<artifact name="${ant.project.name}"/>
+		<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
+	</publications>
+
+	<dependencies>
+		<dependency org="org.eclipse.osgi" name="org.eclipse.osgi" rev="${org.eclipse.osgi}" conf="compile->compile"/>
+		<dependency org="org.junit" name="com.springsource.org.junit" rev="${org.junit}" conf="test->runtime"/>		
+		<dependency org="org.easymock" name="com.springsource.org.easymock" rev="${org.easymock}" conf="test->runtime"/>
+		<dependency org="org.eclipse.virgo.teststubs" name="org.eclipse.virgo.teststubs.osgi" rev="${org.eclipse.virgo.teststubs}" conf="test->runtime"/>
+		
+	</dependencies>
+
+</ivy-module>
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/ConsoleInputStream.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/ConsoleInputStream.java
new file mode 100644
index 0000000..33ea0c0
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/ConsoleInputStream.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+/**
+ * This class serves as an input stream, which wraps the actual input (e.g. from the telnet) and buffers the lines.
+ */
+public class ConsoleInputStream extends InputStream {
+
+    private final ArrayList<byte[]> buffer = new ArrayList<byte[]>();
+
+    private byte[] current;
+
+    private int pos;
+
+    private boolean isClosed;
+
+    public synchronized int read() {
+        while (current == null && buffer.isEmpty() && !isClosed) {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                return -1;
+            }
+        }
+        if (isClosed) {
+            return -1;
+        }
+
+        try {
+            if (current == null) {
+                current = buffer.remove(0);
+                return current[pos++] & 0xFF;
+            } else {
+
+                return current[pos++] & 0xFF;
+            }
+        } finally {
+            if (current != null) {
+                if (pos == current.length) {
+                    current = null;
+                    pos = 0;
+                }
+            }
+        }
+
+    }
+
+    public int read(byte b[], int off, int len) throws IOException {
+        if (len == 0) {
+            return len;
+        }
+        int i = read();
+        if (i == -1) {
+            return -1;
+        }
+        b[off] = (byte) i;
+        return 1;
+    }
+
+    public synchronized void close() throws IOException {
+        isClosed = true;
+        notifyAll();
+    }
+
+    public synchronized void add(byte[] data) {
+        if (data.length > 0) {
+            buffer.add(data);
+            notify();
+        }
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/ConsoleOutputStream.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/ConsoleOutputStream.java
new file mode 100644
index 0000000..16a4d76
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/ConsoleOutputStream.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class wraps the actual output stream (e.g., a socket output stream) and is responsible for buffering and
+ * flushing the characters to the actual output stream.
+ */
+public class ConsoleOutputStream extends OutputStream {
+
+    /**
+     * A size of the used buffer.
+     */
+    public static final int BUFFER_SIZE = 2048;
+
+    public final static byte CR = (byte) '\r';
+
+    public final static byte LF = (byte) '\n';
+
+    OutputStream out;
+
+    OutputStream oldOut;
+
+    private boolean isEcho = true;
+
+    private boolean queueing = false;
+
+    private byte prevByte;
+
+    private byte[] buffer;
+
+    private int pos;
+
+    /**
+     * Initiates with instance of the output stream to which it will send data. Here it writes to a socket output
+     * stream.
+     * 
+     * @param out OutputStream for console output
+     */
+    public ConsoleOutputStream(OutputStream out) {
+        this.out = out;
+        buffer = new byte[BUFFER_SIZE];
+        pos = 0;
+    }
+
+    /**
+     * An implementation of the corresponding abstract method in OutputStream.
+     */
+    public synchronized void write(int i) throws IOException {
+
+        if (!queueing) {
+            if (isEcho) {
+                if (i == '\r' || i == '\0') {
+                    queueing = true;
+                    prevByte = (byte) i;
+                } else if (i == '\n') {
+                    add(CR);
+                    add(LF);
+                } else {
+                    add(i);
+                }
+            }
+        } else { // awaiting '\n' AFTER '\r', and '\b' AFTER '\0'
+            if (prevByte == '\0' && i == '\b') {
+                isEcho = !isEcho;
+            } else if (isEcho) {
+                if (prevByte == '\r' && i == '\n') {
+                    add(CR);
+                    add(LF);
+                } else {
+                    add(CR);
+                    add(LF);
+                    add(i);
+                }
+            }
+
+            queueing = false;
+            flush();
+        }
+
+    }
+
+    /**
+     * Empties the buffer and sends data to the socket output stream.
+     * 
+     * @throws IOException
+     */
+    public synchronized void flush() throws IOException {
+        if (pos > 0) {
+            out.write(buffer, 0, pos);
+            pos = 0;
+        }
+    }
+
+    /**
+     * Adds a variable of type integer to the buffer.
+     * 
+     * @param i integer to add
+     * @throws java.io.IOException if there are problems adding the integer
+     */
+    private void add(int i) throws IOException {
+        buffer[pos] = (byte) i;
+        pos++;
+
+        if (pos == buffer.length) {
+            flush();
+        }
+    }
+
+    /**
+     * Closes this OutputStream.
+     * 
+     * @throws IOException
+     */
+    public void close() throws IOException {
+        out.close();
+    }
+
+    /**
+     * Substitutes the output stream. The old one is stored so that it can be restored later.
+     * 
+     * @param newOut new output stream to use.
+     */
+    public void setOutput(OutputStream newOut) {
+        if (newOut != null) {
+            oldOut = out;
+            out = newOut;
+        } else {
+            out = oldOut;
+        }
+
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/InputHandler.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/InputHandler.java
new file mode 100644
index 0000000..06bd6e6
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/InputHandler.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.Scanner;
+
+/**
+ * This class represents a generic handler of content, read from some input stream. It reads from the stream, and passes
+ * what is read to a processor, which performs some actions on the content, eventually writing to an output stream. This
+ * handler should be customized with a concrete content processor.
+ */
+public abstract class InputHandler extends Thread {
+
+    protected Scanner inputScanner;
+
+    protected OutputStream out;
+
+    protected ConsoleInputStream in;
+
+    protected InputStream input;
+
+    protected byte[] buffer;
+
+    protected static final int MAX_SIZE = 2048;
+
+    public InputHandler(InputStream input, ConsoleInputStream in, OutputStream out) {
+        this.input = input;
+        this.in = in;
+        this.out = out;
+        buffer = new byte[MAX_SIZE];
+    }
+
+    public void run() {
+        int count;
+        try {
+            while ((count = input.read(buffer)) > -1) {
+                for (int i = 0; i < count; i++) {
+                    inputScanner.scan(buffer[i]);
+                }
+            }
+        } catch (IOException e) {
+            // Printing stack trace is not needed since the streams are closed immediately
+            // do nothing
+        } finally {
+            try {
+                in.close();
+            } catch (IOException e1) {
+                // do nothing
+            }
+            try {
+                out.close();
+            } catch (IOException e1) {
+                // do nothing
+            }
+        }
+    }
+
+    public Scanner getScanner() {
+        return inputScanner;
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/KEYS.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/KEYS.java
new file mode 100644
index 0000000..0a1636a
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/KEYS.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+public enum KEYS {
+    UP, DOWN, RIGHT, LEFT, CENTER, HOME, END, PGUP, PGDN, INS, DEL, UNFINISHED, UNKNOWN
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/Scanner.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/Scanner.java
new file mode 100644
index 0000000..903f480
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/Scanner.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+import org.eclipse.virgo.osgi.console.telnet.ANSITerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.SCOTerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.VT100TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.VT220TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.VT320TerminalTypeMappings;
+
+/**
+ * A common superclass for content processor for the telnet protocol and for command line editing (processing delete,
+ * backspace, arrows, command history, etc.).
+ */
+public abstract class Scanner {
+
+    protected static final byte BS = 8;
+
+    private byte BACKSPACE;
+
+    protected static final byte LF = 10;
+
+    protected static final byte CR = 13;
+
+    protected static final byte ESC = 27;
+
+    protected static final byte SPACE = 32;
+
+    private byte DEL;
+
+    protected static final byte MAX_CHAR = 127;
+
+    protected static final String DEFAULT_TTYPE = File.separatorChar == '/' ? "XTERM" : "ANSI";
+
+    protected OutputStream toTelnet;
+
+    protected ConsoleInputStream toShell;
+
+    protected Map<String, KEYS> currentEscapesToKey;
+
+    protected final Map<String, TerminalTypeMappings> supportedEscapeSequences;
+
+    protected String[] escapes;
+
+    public Scanner(ConsoleInputStream toShell, OutputStream toTelnet) {
+        this.toShell = toShell;
+        this.toTelnet = toTelnet;
+        supportedEscapeSequences = new HashMap<String, TerminalTypeMappings>();
+        supportedEscapeSequences.put("ANSI", new ANSITerminalTypeMappings());
+        supportedEscapeSequences.put("VT100", new VT100TerminalTypeMappings());
+        VT220TerminalTypeMappings vtMappings = new VT220TerminalTypeMappings();
+        supportedEscapeSequences.put("VT220", new VT220TerminalTypeMappings());
+        supportedEscapeSequences.put("XTERM", vtMappings);
+        supportedEscapeSequences.put("VT320", new VT320TerminalTypeMappings());
+        supportedEscapeSequences.put("SCO", new SCOTerminalTypeMappings());
+    }
+
+    public abstract void scan(int b) throws IOException;
+
+    protected void echo(int b) throws IOException {
+        toTelnet.write(b);
+    }
+
+    protected void flush() throws IOException {
+        toTelnet.flush();
+    }
+
+    protected KEYS checkEscape(String possibleEsc) {
+        if (currentEscapesToKey.get(possibleEsc) != null) {
+            return currentEscapesToKey.get(possibleEsc);
+        }
+
+        for (String escape : escapes) {
+            if (escape.startsWith(possibleEsc)) {
+                return KEYS.UNFINISHED;
+            }
+        }
+        return KEYS.UNKNOWN;
+    }
+
+    protected String esc;
+
+    protected boolean isEsc = false;
+
+    protected void startEsc() {
+        isEsc = true;
+        esc = "";
+    }
+
+    protected abstract void scanEsc(final int b) throws IOException;
+
+    public byte getBackspace() {
+        return BACKSPACE;
+    }
+
+    public void setBackspace(byte backspace) {
+        BACKSPACE = backspace;
+    }
+
+    public byte getDel() {
+        return DEL;
+    }
+
+    public void setDel(byte del) {
+        DEL = del;
+    }
+
+    public Map<String, KEYS> getCurrentEscapesToKey() {
+        return currentEscapesToKey;
+    }
+
+    public void setCurrentEscapesToKey(Map<String, KEYS> currentEscapesToKey) {
+        this.currentEscapesToKey = currentEscapesToKey;
+    }
+
+    public String[] getEscapes() {
+        if (escapes != null) {
+            return Arrays.copyOf(escapes, escapes.length);
+        } else {
+            return null;
+        }
+    }
+
+    public void setEscapes(String[] escapes) {
+        if (escapes != null) {
+            this.escapes = Arrays.copyOf(escapes, escapes.length);
+        } else {
+            this.escapes = null;
+        }
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/SimpleByteBuffer.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/SimpleByteBuffer.java
new file mode 100644
index 0000000..d00ca53
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/common/SimpleByteBuffer.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+/**
+ * This is a helper class, which buffers one line of input. It provides for simple line editing - insertion, deletion,
+ * left and right movement, deletion through the backspace key.
+ */
+public class SimpleByteBuffer {
+
+    private static int INITAL_SIZE = 13;
+
+    private byte[] buffer;
+
+    private int pos = 0;
+
+    private int size = 0;
+
+    public SimpleByteBuffer() {
+        buffer = new byte[INITAL_SIZE];
+    }
+
+    public void add(final int b) {
+        if (size >= buffer.length) {
+            rezize();
+        }
+        buffer[size++] = (byte) b;
+    }
+
+    private void rezize() {
+        final byte[] newbuffeer = new byte[buffer.length << 1];
+        System.arraycopy(buffer, 0, newbuffeer, 0, buffer.length);
+        buffer = newbuffeer;
+    }
+
+    public void insert(int b) {
+        if (size >= buffer.length) {
+            rezize();
+        }
+        final int forCopy = size - pos;
+        if (forCopy > 0) {
+            System.arraycopy(buffer, pos, buffer, pos + 1, forCopy);
+        }
+        buffer[pos++] = (byte) b;
+        size++;
+    }
+
+    public int goRight() {
+        if (pos < size) {
+            return buffer[pos++] & 0xFF;
+        }
+        return -1;
+    }
+
+    public boolean goLeft() {
+        if (pos > 0) {
+            pos--;
+            return true;
+        }
+        return false;
+    }
+
+    public void delete() {
+        if (pos < size) {
+            final int forCopy = size - pos;
+            System.arraycopy(buffer, pos + 1, buffer, pos, forCopy);
+            size--;
+        }
+    }
+
+    public boolean backSpace() {
+        if (pos > 0 && size > 0) {
+            final int forCopy = size - pos;
+            System.arraycopy(buffer, pos, buffer, pos - 1, forCopy);
+            size--;
+            pos--;
+            return true;
+        }
+        return false;
+    }
+
+    public void delAll() {
+        pos = 0;
+        size = 0;
+    }
+
+    public byte[] getCurrentData() {
+        byte[] res = new byte[size];
+        System.arraycopy(buffer, 0, res, 0, size);
+        pos = 0;
+        size = 0;
+        return res;
+    }
+
+    public void set(byte[] newData) {
+        pos = 0;
+        size = 0;
+        if (newData != null) {
+            for (byte data : newData) {
+                insert(data);
+            }
+        }
+    }
+
+    public int getPos() {
+        return pos;
+    }
+
+    public byte[] copyCurrentData() {
+        byte[] res = new byte[size];
+        System.arraycopy(buffer, 0, res, 0, size);
+        return res;
+    }
+
+    public int getCurrentChar() {
+        if (pos < size) {
+            return buffer[pos] & 0xFF;
+        } else {
+            return -1;
+        }
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public int resetPos() {
+        int res = pos;
+        pos = 0;
+        return res;
+    }
+
+    public void replace(int b) {
+        if (pos == size) {
+            insert(b);
+        } else {
+            buffer[pos++] = (byte) b;
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/CommandCompleter.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/CommandCompleter.java
new file mode 100644
index 0000000..42bea12
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/CommandCompleter.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class implements basic command completion. It can complete only OSGi commands, not command parameters. It
+ * registers a tracker, with which it tracks all CommandProvider services, and when a new one becomes available, it adds
+ * its command methods in a local cache, which the completer uses to complete the command name given the first letters
+ * of the command.
+ */
+public class CommandCompleter {
+
+    private Set<String> availableCommands;
+
+    private ServiceTracker<CommandProvider, Object> cpTracker;
+
+    private BundleContext context = null;
+
+    public CommandCompleter(BundleContext context) {
+        this.context = context;
+        availableCommands = new HashSet<String>();
+        availableCommands = Collections.synchronizedSet(availableCommands);
+        availableCommands.add("more");
+        availableCommands.add("disconnect");
+        availableCommands.add("grep");
+        if (context != null) {
+            cpTracker = new ServiceTracker<CommandProvider, Object>(context, CommandProvider.class.getName(), new CommandProviderCustomizer());
+            cpTracker.open();
+        }
+    }
+
+    public String[] complete(String prefix) {
+        ArrayList<String> candidates = new ArrayList<String>();
+        for (String command : availableCommands) {
+            if (command.startsWith(prefix)) {
+                candidates.add(command);
+            }
+        }
+
+        return candidates.toArray(new String[candidates.size()]);
+    }
+
+    class CommandProviderCustomizer implements ServiceTrackerCustomizer<CommandProvider, Object> {
+
+        public Object addingService(ServiceReference<CommandProvider> reference) {
+            CommandProvider provider = context.getService(reference);
+            Method[] methods = provider.getClass().getMethods();
+            for (Method method : methods) {
+                if (method.getName().startsWith("_")) {
+                    availableCommands.add(method.getName().substring(1));
+                }
+            }
+            return null;
+        }
+
+        public void modifiedService(ServiceReference<CommandProvider> reference, Object service) {
+            // do nothing
+        }
+
+        public void removedService(ServiceReference<CommandProvider> reference, Object service) {
+            CommandProvider provider = context.getService(reference);
+            Method[] methods = provider.getClass().getMethods();
+            for (Method method : methods) {
+                if (method.getName().startsWith("_")) {
+                    availableCommands.remove(method.getName());
+                }
+            }
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputHandler.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputHandler.java
new file mode 100644
index 0000000..234b522
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputHandler.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2010 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import org.eclipse.virgo.osgi.console.supportability.ConsoleInputScanner;
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.InputHandler;
+import org.osgi.framework.BundleContext;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class customizes the generic handler with a concrete content processor, which provides command line editing.
+ */
+public class ConsoleInputHandler extends InputHandler {
+
+    public ConsoleInputHandler(InputStream input, ConsoleInputStream in, OutputStream out, BundleContext context) {
+        super(input, in, out);
+        inputScanner = new ConsoleInputScanner(in, out, context);
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputScanner.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputScanner.java
new file mode 100644
index 0000000..7dcab17
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputScanner.java
@@ -0,0 +1,374 @@
+/*******************************************************************************
+ * Copyright (c) 2010 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import org.eclipse.virgo.osgi.console.supportability.CommandCompleter;
+import org.eclipse.virgo.osgi.console.supportability.Grep;
+import org.eclipse.virgo.osgi.console.supportability.HistoryHolder;
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+import org.eclipse.virgo.osgi.console.common.Scanner;
+import org.eclipse.virgo.osgi.console.common.SimpleByteBuffer;
+import org.osgi.framework.BundleContext;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * This class performs the processing of the input special characters, and updates respectively what is displayed in the
+ * output. It handles escape sequences, delete, backspace, arrows, and provides command history and grep.
+ */
+public class ConsoleInputScanner extends Scanner {
+
+    private static final byte TAB = 9;
+
+    private boolean isCR = false;
+
+    private boolean replace = false;
+
+    private final HistoryHolder history;
+
+    private final SimpleByteBuffer buffer;
+
+    private CommandCompleter completer;
+
+    public ConsoleInputScanner(ConsoleInputStream toShell, OutputStream toTelnet, BundleContext context) {
+        super(toShell, toTelnet);
+        history = new HistoryHolder();
+        buffer = new SimpleByteBuffer();
+        completer = new CommandCompleter(context);
+    }
+
+    public void scan(int b) throws IOException {
+        b &= 0xFF;
+        if (isCR) {
+            isCR = false;
+            if (b == LF) {
+                return;
+            }
+        }
+        if (isEsc) {
+            scanEsc(b);
+        } else {
+            if (b == getBackspace()) {
+                backSpace();
+            } else if (b == TAB) {
+                tab();
+            } else if (b == CR) {
+                isCR = true;
+                processData();
+            } else if (b == LF) {
+                processData();
+            } else if (b == ESC) {
+                startEsc();
+            } else if (b == getDel()) {
+                delete();
+            } else {
+                if (b >= SPACE && b < MAX_CHAR) {
+                    newChar(b);
+                }
+            }
+        }
+    }
+
+    private void delete() throws IOException {
+        clearLine();
+        buffer.delete();
+        echoBuff();
+        flush();
+    }
+
+    private void backSpace() throws IOException {
+        clearLine();
+        buffer.backSpace();
+        echoBuff();
+        flush();
+    }
+
+    private void tab() throws IOException {
+        byte[] cur = buffer.copyCurrentData();
+        String currentInput = new String(cur).trim();
+        String[] completionCandidates = completer.complete(currentInput);
+
+        if (completionCandidates.length == 1) {
+            String suffix = completionCandidates[0].substring(currentInput.length());
+            byte[] completion = suffix.getBytes();
+            for (byte symbol : completion) {
+                buffer.insert(symbol);
+                echo(symbol);
+
+            }
+            flush();
+            return;
+        }
+
+        echo(CR);
+        echo(LF);
+        flush();
+
+        if (completionCandidates.length == 0) {
+            buffer.getCurrentData();
+            String errorMessage = "No such command";
+            for (byte symbol : errorMessage.getBytes()) {
+                echo(symbol);
+            }
+        } else {
+            for (String candidate : completionCandidates) {
+                for (byte symbol : candidate.getBytes()) {
+                    echo(symbol);
+                }
+                echo(SPACE);
+                echo(SPACE);
+            }
+        }
+
+        echo(CR);
+        echo(LF);
+        flush();
+        echo('o');
+        echo('s');
+        echo('g');
+        echo('i');
+        echo('>');
+        echo(SPACE);
+        echoBuff();
+        flush();
+    }
+
+    protected void clearLine() throws IOException {
+        int size = buffer.getSize();
+        int pos = buffer.getPos();
+        for (int i = size - pos; i < size; i++) {
+            echo(BS);
+        }
+        for (int i = 0; i < size; i++) {
+            echo(SPACE);
+        }
+        for (int i = 0; i < size; i++) {
+            echo(BS);
+        }
+    }
+
+    protected void echoBuff() throws IOException {
+        byte[] data = buffer.copyCurrentData();
+        for (byte b : data) {
+            echo(b);
+        }
+        int pos = buffer.getPos();
+        for (int i = data.length; i > pos; i--) {
+            echo(BS);
+        }
+    }
+
+    protected void newChar(int b) throws IOException {
+        if (buffer.getPos() < buffer.getSize()) {
+            if (replace) {
+                buffer.replace(b);
+            } else {
+                buffer.insert(b);
+            }
+            clearLine();
+            echoBuff();
+            flush();
+        } else {
+            if (replace) {
+                buffer.replace(b);
+            } else {
+                buffer.insert(b);
+            }
+        }
+    }
+
+    private void processData() throws IOException {
+        buffer.add(CR);
+        buffer.add(LF);
+        echo(CR);
+        echo(LF);
+        flush();
+        byte[] curr = buffer.getCurrentData();
+        history.add(curr);
+
+        int index = 0;
+        boolean isGrep = false;
+        for (; index < curr.length; index++) {
+            if (curr[index] == '|') {
+                isGrep = true;
+                break;
+            }
+        }
+
+        if (isGrep) {
+            byte[] grepExpression = new byte[curr.length - index - 1];
+            System.arraycopy(curr, index + 1, grepExpression, 0, grepExpression.length);
+            Grep grep = new Grep(grepExpression, toTelnet);
+            grep.start();
+            byte[] array = Arrays.copyOf(curr, index + 2);
+            array[index] = CR;
+            array[index + 1] = LF;
+            toShell.add(array);
+        } else {
+            toShell.add(curr);
+        }
+    }
+
+    public void resetHistory() {
+        history.reset();
+    }
+
+    protected void scanEsc(final int b) throws IOException {
+        esc += (char) b;
+        KEYS key = checkEscape(esc);
+        if (key == KEYS.UNFINISHED) {
+            return;
+        }
+        if (key == KEYS.UNKNOWN) {
+            isEsc = false;
+            scan(b);
+            return;
+        }
+        isEsc = false;
+        switch (key) {
+            case UP:
+                processUpArrow();
+                break;
+            case DOWN:
+                processDownArrow();
+                break;
+            case RIGHT:
+                processRightArrow();
+                break;
+            case LEFT:
+                processLeftArrow();
+                break;
+            case HOME:
+                processHome();
+                break;
+            case END:
+                processEnd();
+                break;
+            case PGUP:
+                processPgUp();
+                break;
+            case PGDN:
+                processPgDn();
+                break;
+            case INS:
+                processIns();
+                break;
+            case DEL:
+                delete();
+                break;
+            default: // CENTER
+                break;
+        }
+    }
+
+    private static final byte[] INVERSE_ON = { ESC, '[', '7', 'm' };
+
+    private static final byte[] INVERSE_OFF = { ESC, '[', '2', '7', 'm' };
+
+    private void echo(byte[] data) throws IOException {
+        for (byte b : data) {
+            echo(b);
+        }
+    }
+
+    private void processIns() throws IOException {
+        replace = !replace;
+        int b = buffer.getCurrentChar();
+        echo(INVERSE_ON);
+        echo(replace ? 'R' : 'I');
+        flush();
+        try {
+            Thread.sleep(300);
+        } catch (InterruptedException e) {
+            // do not care $JL-EXC$
+        }
+        echo(INVERSE_OFF);
+        echo(BS);
+        echo(b == -1 ? SPACE : b);
+        echo(BS);
+        flush();
+    }
+
+    private void processPgDn() throws IOException {
+        byte[] last = history.last();
+        if (last != null) {
+            clearLine();
+            buffer.set(last);
+            echoBuff();
+            flush();
+        }
+    }
+
+    private void processPgUp() throws IOException {
+        byte[] first = history.first();
+        if (first != null) {
+            clearLine();
+            buffer.set(first);
+            echoBuff();
+            flush();
+        }
+    }
+
+    private void processHome() throws IOException {
+        int pos = buffer.resetPos();
+        if (pos > 0) {
+            for (int i = 0; i < pos; i++) {
+                echo(BS);
+            }
+            flush();
+        }
+    }
+
+    private void processEnd() throws IOException {
+        int b;
+        while ((b = buffer.goRight()) != -1) {
+            echo(b);
+        }
+        flush();
+    }
+
+    private void processLeftArrow() throws IOException {
+        if (buffer.goLeft()) {
+            echo(BS);
+            flush();
+        }
+    }
+
+    private void processRightArrow() throws IOException {
+        int b = buffer.goRight();
+        if (b != -1) {
+            echo(b);
+            flush();
+        }
+    }
+
+    private void processDownArrow() throws IOException {
+        byte[] next = history.next();
+        if (next != null) {
+            clearLine();
+            buffer.set(next);
+            echoBuff();
+            flush();
+        }
+    }
+
+    private void processUpArrow() throws IOException {
+        clearLine();
+        byte[] prev = history.prev();
+        buffer.set(prev);
+        echoBuff();
+        flush();
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/Grep.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/Grep.java
new file mode 100644
index 0000000..2f26001
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/Grep.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/**
+ * This class implements grep. Since in Equinox 3.6 there is not piping support, grep cannot be implemented as a shell
+ * command. That is why it is implemented as part of the command line editing features. The socket output stream inside
+ * ConsoleOutputStream is substituted with a PipedOutputStream, so that what the command writes to the output stream
+ * does not go to the console, but is read through a PipedInputStream and is filtered for the searched expression. After
+ * all output of the command is read and filtered, the socket output stream inside ConsoleOutputStream is restored and
+ * the lines of the command output, which match the grep expression, are written to it.
+ */
+public class Grep extends Thread {
+
+    private String expression;
+
+    private ConsoleOutputStream out;
+
+    private PipedInputStream input;
+
+    private PipedOutputStream output;
+
+    private BufferedReader reader;
+
+    private ArrayList<String> filteredOutput;
+
+    private static int LENGTH = 4;
+
+    public Grep(byte[] expression, OutputStream out) {
+        String expr = (new String(expression)).trim();
+        int index = expr.indexOf("grep");
+        this.expression = expr.substring(index + LENGTH).trim();
+        this.out = (ConsoleOutputStream) out;
+        input = new PipedInputStream();
+        try {
+            output = new PipedOutputStream(input);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        this.out.setOutput(output);
+        filteredOutput = new ArrayList<String>();
+    }
+
+    public void run() {
+        reader = new BufferedReader(new InputStreamReader(input));
+        boolean hasMore = true;
+        try {
+            while (hasMore) {
+                String line = getLine();
+                hasMore = line != null;
+                if (hasMore) {
+                    // last line containing the osgi prompt should be output although it does not contain the grep
+                    // expression
+                    if (line.contains(expression) || line.contains("osgi>")) {
+                        filteredOutput.add(line);
+                    }
+
+                    if (line.contains("osgi>")) {
+                        hasMore = false;
+                    }
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        out.setOutput(null);
+        PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(out)));
+        for (int i = 0; i < filteredOutput.size(); i++) {
+            if (i == filteredOutput.size() - 1 && filteredOutput.get(i).contains("osgi>")) {
+                writer.print(filteredOutput.get(i));
+                writer.print(" ");
+            } else {
+                writer.println(filteredOutput.get(i));
+            }
+            writer.flush();
+        }
+    }
+
+    private String getLine() throws IOException {
+        StringBuilder line = new StringBuilder();
+        boolean quit = false;
+        while (!quit) {
+            int c = reader.read();
+            if (c < 0) {
+                quit = true;
+            } else {
+                switch (c) {
+                    case '\r':
+                        break;
+                    case '\n':
+                        return line.toString();
+                    default:
+                        line.append((char) c);
+                        if (line.toString().contains("osgi>")) {
+                            return line.toString();
+                        }
+                        break;
+                }
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/HistoryHolder.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/HistoryHolder.java
new file mode 100644
index 0000000..dc39af9
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/supportability/HistoryHolder.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+
+/**
+ * A helper class, which implements history.
+ */
+public class HistoryHolder {
+
+    private static final int MAX = 100;
+
+    private final byte[][] history;
+
+    private int size;
+
+    private int pos;
+
+    public HistoryHolder() {
+        history = new byte[MAX][];
+    }
+
+    public synchronized void reset() {
+        size = 0;
+        pos = 0;
+        for (int i = 0; i < MAX; i++) {
+            history[i] = null;
+        }
+    }
+
+    public synchronized void add(byte[] data) {
+        try {
+            data = new String(data, "US-ASCII").trim().getBytes("US-ASCII");
+        } catch (UnsupportedEncodingException e) {
+
+        }
+        if (data.length == 0) {
+            pos = size;
+            return;
+        }
+        for (int i = 0; i < size; i++) {
+            if (Arrays.equals(history[i], data)) {
+                System.arraycopy(history, i + 1, history, i, size - i - 1);
+                history[size - 1] = data;
+                pos = size;
+                return;
+            }
+        }
+        if (size >= MAX) {
+            System.arraycopy(history, 1, history, 0, size - 1);
+            size--;
+        }
+        history[size++] = data;
+        pos = size;
+    }
+
+    public synchronized byte[] next() {
+        if (pos >= size - 1) {
+            return null;
+        }
+        return history[++pos];
+    }
+
+    public synchronized byte[] last() {
+        if (size > 0) {
+            pos = size - 1;
+            return history[pos];
+        } else {
+            return null;
+        }
+    }
+
+    public synchronized byte[] first() {
+        if (size > 0) {
+            pos = 0;
+            return history[pos];
+        } else {
+            return null;
+        }
+    }
+
+    public synchronized byte[] prev() {
+        if (size == 0) {
+            return null;
+        }
+        if (pos == 0) {
+            return history[pos];
+        } else {
+            return history[--pos];
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/ANSITerminalTypeMappings.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/ANSITerminalTypeMappings.java
new file mode 100644
index 0000000..9468f7f
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/ANSITerminalTypeMappings.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+
+public class ANSITerminalTypeMappings extends TerminalTypeMappings {
+
+    public ANSITerminalTypeMappings() {
+        super();
+        BACKSPACE = 8;
+        DEL = 127;
+    }
+
+    public void setKeypadMappings() {
+        escapesToKey.put("[1~", KEYS.HOME); //$NON-NLS-1$
+        escapesToKey.put("[4~", KEYS.END); //$NON-NLS-1$
+        escapesToKey.put("[5~", KEYS.PGUP); //$NON-NLS-1$
+        escapesToKey.put("[6~", KEYS.PGDN); //$NON-NLS-1$
+        escapesToKey.put("[2~", KEYS.INS); //$NON-NLS-1$
+        escapesToKey.put("[3~", KEYS.DEL); //$NON-NLS-1$
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/Callback.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/Callback.java
new file mode 100644
index 0000000..6b2474e
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/Callback.java
@@ -0,0 +1,17 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+public interface Callback {
+
+    public void finished();
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/NegotiationFinishedCallback.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/NegotiationFinishedCallback.java
new file mode 100644
index 0000000..883c6ea
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/NegotiationFinishedCallback.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.Callback;
+import org.eclipse.virgo.osgi.console.telnet.TelnetConsoleSession;
+
+public class NegotiationFinishedCallback implements Callback {
+
+    private TelnetConsoleSession consoleSession;
+
+    public NegotiationFinishedCallback(TelnetConsoleSession consoleSession) {
+        this.consoleSession = consoleSession;
+    }
+
+    @Override
+    public void finished() {
+        consoleSession.telnetNegotiationFinished();
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/SCOTerminalTypeMappings.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/SCOTerminalTypeMappings.java
new file mode 100644
index 0000000..b337e99
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/SCOTerminalTypeMappings.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+
+public class SCOTerminalTypeMappings extends TerminalTypeMappings {
+
+    public SCOTerminalTypeMappings() {
+        super();
+
+        BACKSPACE = -1;
+        DEL = 127;
+    }
+
+    @Override
+    public void setKeypadMappings() {
+        escapesToKey.put("[H", KEYS.HOME);
+        escapesToKey.put("F", KEYS.END);
+        escapesToKey.put("[L", KEYS.INS);
+        escapesToKey.put("[I", KEYS.PGUP);
+        escapesToKey.put("[G", KEYS.PGDN);
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetConsoleSession.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetConsoleSession.java
new file mode 100644
index 0000000..22758c9
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetConsoleSession.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.Callback;
+import org.eclipse.virgo.osgi.console.telnet.NegotiationFinishedCallback;
+import org.eclipse.virgo.osgi.console.telnet.TelnetInputHandler;
+import org.eclipse.virgo.osgi.console.telnet.TelnetOutputStream;
+import org.eclipse.osgi.framework.console.ConsoleSession;
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.supportability.ConsoleInputHandler;
+import org.osgi.framework.BundleContext;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * This class provides an implementation of a ConsoleSession. It creates a handler for the input from telnet and wraps
+ * its streams to add handling for command line editing.
+ */
+public class TelnetConsoleSession extends ConsoleSession {
+
+    private final Socket s;
+
+    private InputStream input;
+
+    private final ConsoleInputStream in;
+
+    private final TelnetOutputStream out;
+
+    protected boolean isTelnetNegotiationFinished = false;
+
+    private Callback callback;
+
+    private static final long TIMEOUT = 1000;
+
+    private static final long NEGOTIATION_TIMEOUT = 60000;
+
+    private final BundleContext context;
+
+    public TelnetConsoleSession(Socket s, BundleContext context) throws IOException {
+        in = new ConsoleInputStream();
+        out = new TelnetOutputStream(s.getOutputStream());
+        out.autoSend();
+        this.s = s;
+        this.context = context;
+
+        callback = new NegotiationFinishedCallback(this);
+    }
+
+    public synchronized void start() throws IOException {
+        TelnetInputHandler telnetInputHandler = new TelnetInputHandler(s.getInputStream(), in, out, callback);
+        telnetInputHandler.start();
+        long start = System.currentTimeMillis();
+        while (isTelnetNegotiationFinished == false && System.currentTimeMillis() - start < NEGOTIATION_TIMEOUT) {
+            try {
+                wait(TIMEOUT);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+
+        ConsoleInputStream inp = new ConsoleInputStream();
+
+        ConsoleInputHandler consoleInputHandler = new ConsoleInputHandler(in, inp, out, context);
+        consoleInputHandler.getScanner().setBackspace(telnetInputHandler.getScanner().getBackspace());
+        consoleInputHandler.getScanner().setDel(telnetInputHandler.getScanner().getDel());
+        consoleInputHandler.getScanner().setCurrentEscapesToKey(telnetInputHandler.getScanner().getCurrentEscapesToKey());
+        consoleInputHandler.getScanner().setEscapes(telnetInputHandler.getScanner().getEscapes());
+
+        consoleInputHandler.start();
+        input = inp;
+    }
+
+    public synchronized void telnetNegotiationFinished() {
+        isTelnetNegotiationFinished = true;
+        notify();
+
+    }
+
+    public synchronized InputStream getInput() {
+        return input;
+    }
+
+    public synchronized OutputStream getOutput() {
+        return out;
+    }
+
+    public synchronized void doClose() {
+        if (s != null) {
+            try {
+                s.close();
+            } catch (IOException ioe) {
+                // do nothing
+            }
+        }
+
+        if (out != null) {
+            try {
+                out.close();
+            } catch (IOException e) {
+                // do nothing
+            }
+        }
+
+        if (in != null) {
+            try {
+                in.close();
+            } catch (IOException ioe) {
+                // do nothing
+            }
+        }
+
+        if (input != null) {
+            try {
+                input.close();
+            } catch (IOException ioe) {
+                // do nothing
+            }
+        }
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputHandler.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputHandler.java
new file mode 100644
index 0000000..8ab3f3c
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputHandler.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.Callback;
+import org.eclipse.virgo.osgi.console.telnet.TelnetInputScanner;
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+import org.eclipse.virgo.osgi.console.common.InputHandler;
+
+import java.io.InputStream;
+
+/**
+ * This class customizes the generic handler with a concrete content processor, which provides telnet protocol handling.
+ */
+public class TelnetInputHandler extends InputHandler {
+
+    public TelnetInputHandler(InputStream input, ConsoleInputStream in, ConsoleOutputStream out, Callback callback) {
+        super(input, in, out);
+        inputScanner = new TelnetInputScanner(in, out, callback);
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputScanner.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputScanner.java
new file mode 100644
index 0000000..8f865b9
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputScanner.java
@@ -0,0 +1,296 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.Callback;
+import org.eclipse.virgo.osgi.console.telnet.TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+import org.eclipse.virgo.osgi.console.common.Scanner;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class performs the processing of the telnet commands, and updates respectively what is displayed in the output.
+ */
+public class TelnetInputScanner extends Scanner {
+
+    private boolean isCommand = false;
+
+    private boolean isReadingTtype = false;
+
+    private boolean shouldFinish = false;
+
+    private boolean tTypeNegotiationStarted = false;
+
+    private int lastRead = -1;
+
+    private ArrayList<Integer> currentTerminalType = new ArrayList<Integer>();
+
+    private ArrayList<Integer> lastTerminalType = null;
+
+    private Set<String> supportedTerminalTypes = new HashSet<String>();
+
+    private Callback callback;
+
+    public TelnetInputScanner(ConsoleInputStream toShell, ConsoleOutputStream toTelnet, Callback callback) {
+        super(toShell, toTelnet);
+        initializeSupportedTerminalTypes();
+        TerminalTypeMappings currentMapping = supportedEscapeSequences.get(DEFAULT_TTYPE);
+        currentEscapesToKey = currentMapping.getEscapesToKey();
+        escapes = currentMapping.getEscapes();
+        setBackspace(currentMapping.getBackspace());
+        setDel(currentMapping.getDel());
+        this.callback = callback;
+    }
+
+    private void initializeSupportedTerminalTypes() {
+        supportedTerminalTypes.add("ANSI");
+        supportedTerminalTypes.add("VT100");
+        supportedTerminalTypes.add("VT220");
+        supportedTerminalTypes.add("VT320");
+        supportedTerminalTypes.add("XTERM");
+        supportedTerminalTypes.add("SCO");
+    }
+
+    public void scan(int b) throws IOException {
+        b &= 0xFF;
+
+        if (isEsc) {
+            scanEsc(b);
+        } else if (isCommand) {
+            scanCommand(b);
+        } else if (b == IAC) {
+            startCommand();
+        } else {
+            switch (b) {
+                case ESC:
+                    startEsc();
+                    toShell.add(new byte[] { (byte) b });
+                    break;
+                default:
+                    if (b >= SPACE && b < MAX_CHAR) {
+                        echo((byte) b);
+                        flush();
+                    }
+                    toShell.add(new byte[] { (byte) b });
+            }
+
+        }
+        lastRead = b;
+    }
+
+    /*
+     * Telnet command codes are described in RFC 854, TELNET PROTOCOL SPECIFICATION available at
+     * http://www.ietf.org/rfc/rfc854.txt
+     * 
+     * Telnet terminal type negotiation option is described in RFC 1091, Telnet Terminal-Type Option available at
+     * http://www.ietf.org/rfc/rfc1091.txt
+     */
+    private static final int SE = 240;
+
+    private static final int EC = 247;
+
+    private static final int EL = 248;
+
+    private static final int SB = 250;
+
+    private static final int WILL = 251;
+
+    private static final int WILL_NOT = 252;
+
+    private static final int DO = 253;
+
+    private static final int DO_NOT = 254;
+
+    private static final int TTYPE = 24;
+
+    private static final int SEND = 1;
+
+    private static final int IAC = 255;
+
+    private static final int IS = 0;
+
+    private boolean isNegotiation;
+
+    private boolean isWill;
+
+    private byte[] tTypeRequest = { (byte) IAC, (byte) SB, (byte) TTYPE, (byte) SEND, (byte) IAC, (byte) SE };
+
+    private void scanCommand(final int b) throws IOException {
+        if (isNegotiation) {
+            scanNegotiation(b);
+        } else if (isWill) {
+            isWill = false;
+            isCommand = false;
+            if (b == TTYPE && tTypeNegotiationStarted == false) {
+                sendRequest();
+            }
+        } else {
+            switch (b) {
+                case WILL:
+                    isWill = true;
+                    break;
+                case WILL_NOT:
+                    break;
+                case DO:
+                    break;
+                case DO_NOT:
+                    break;
+                case SB:
+                    isNegotiation = true;
+                    break;
+                case EC:
+                    eraseChar();
+                    isCommand = false;
+                    break;
+                case EL:
+                default:
+                    isCommand = false;
+                    break;
+            }
+        }
+    }
+
+    private void scanNegotiation(final int b) {
+        if (lastRead == SB && b == TTYPE) {
+            isReadingTtype = true;
+        } else if (b == IS) {
+
+        } else if (b == IAC) {
+
+        } else if (b == SE) {
+            isNegotiation = false;
+            isCommand = false;
+            if (isReadingTtype == true) {
+                isReadingTtype = false;
+                if (shouldFinish == true) {
+                    setCurrentTerminalType();
+                    shouldFinish = false;
+                    return;
+                }
+                boolean isMatch = isTerminalTypeSupported();
+                boolean isLast = isLast();
+                if (isMatch == true) {
+                    setCurrentTerminalType();
+                    return;
+                }
+                lastTerminalType = currentTerminalType;
+                currentTerminalType = new ArrayList<Integer>();
+                if (isLast == true && isMatch == false) {
+                    shouldFinish = true;
+                    sendRequest();
+                } else if (isLast == false && isMatch == false) {
+                    sendRequest();
+                }
+            }
+        } else if (isReadingTtype == true) {
+            currentTerminalType.add(b);
+        }
+    }
+
+    private boolean isTerminalTypeSupported() {
+        byte[] tmp = new byte[currentTerminalType.size()];
+        int idx = 0;
+        for (Integer i : currentTerminalType) {
+            tmp[idx] = i.byteValue();
+            idx++;
+        }
+        String tType = new String(tmp);
+
+        for (String terminal : supportedTerminalTypes) {
+            if (tType.toUpperCase().contains(terminal)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private boolean isLast() {
+        if (currentTerminalType.equals(lastTerminalType)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void setCurrentTerminalType() {
+        byte[] tmp = new byte[currentTerminalType.size()];
+        int idx = 0;
+        for (Integer i : currentTerminalType) {
+            tmp[idx] = i.byteValue();
+            idx++;
+        }
+        String tType = new String(tmp);
+        String term = null;
+        for (String terminal : supportedTerminalTypes) {
+            if (tType.toUpperCase().contains(terminal)) {
+                term = terminal;
+            }
+        }
+        TerminalTypeMappings currentMapping = supportedEscapeSequences.get(term);
+        if (currentMapping == null) {
+            currentMapping = supportedEscapeSequences.get(DEFAULT_TTYPE);
+        }
+        currentEscapesToKey = currentMapping.getEscapesToKey();
+        escapes = currentMapping.getEscapes();
+        setBackspace(currentMapping.getBackspace());
+        setDel(currentMapping.getDel());
+        if (callback != null) {
+            callback.finished();
+        }
+    }
+
+    private void sendRequest() {
+        try {
+            toTelnet.write(tTypeRequest);
+            toTelnet.flush();
+            if (tTypeNegotiationStarted == false) {
+                tTypeNegotiationStarted = true;
+            }
+        } catch (IOException e) {
+
+            e.printStackTrace();
+        }
+    }
+
+    private void startCommand() {
+        isCommand = true;
+        isNegotiation = false;
+        isWill = false;
+    }
+
+    private void eraseChar() throws IOException {
+        toShell.add(new byte[] { BS });
+    }
+
+    protected void scanEsc(int b) throws IOException {
+        esc += (char) b;
+        toShell.add(new byte[] { (byte) b });
+        KEYS key = checkEscape(esc);
+        if (key == KEYS.UNFINISHED) {
+            return;
+        }
+        if (key == KEYS.UNKNOWN) {
+            isEsc = false;
+            scan(b);
+            return;
+        }
+        isEsc = false;
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetManager.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetManager.java
new file mode 100644
index 0000000..16c870c
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetManager.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.TelnetConsoleSession;
+import org.eclipse.osgi.framework.console.ConsoleSession;
+import org.osgi.framework.BundleContext;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+public class TelnetManager {
+
+    private ConsoleSocketGetter csg;
+
+    private int telnetPort = -1;
+
+    private String host = null;
+
+    private BundleContext context = null;
+
+    private static final String PROP_CONSOLE = "osgi.console";
+
+    public TelnetManager(BundleContext bundleContext) {
+        String consoleValue = null;
+        try {
+            consoleValue = bundleContext.getProperty(PROP_CONSOLE);
+            if (consoleValue != null && !"".equals(consoleValue) && !"none".equals(consoleValue)) {
+                parseHostAndPort(consoleValue);
+            }
+        } catch (NumberFormatException e) {
+            System.out.println("Invalid host/port in " + consoleValue + "; " + e.getMessage());
+            e.printStackTrace();
+        }
+
+        context = bundleContext;
+    }
+
+    public void startConsoleListener() {
+        if (telnetPort != -1) {
+            try {
+                if (host != null) {
+                    csg = new ConsoleSocketGetter(new ServerSocket(telnetPort, 0, InetAddress.getByName(host)), context);
+                } else {
+                    csg = new ConsoleSocketGetter(new ServerSocket(telnetPort), context);
+                }
+            } catch (IOException e) {
+                System.out.println("Unable to open telnet. Reason: " + e.getMessage());
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public void stop() {
+        if (csg != null) {
+            csg.shutdown();
+        }
+    }
+
+    public BundleContext getContext() {
+        return context;
+    }
+
+    private void parseHostAndPort(String consoleValue) {
+        int index = consoleValue.lastIndexOf(":");
+        if (index > -1) {
+            host = consoleValue.substring(0, index);
+        }
+
+        telnetPort = Integer.parseInt(consoleValue.substring(index + 1));
+    }
+
+    /**
+     * ConsoleSocketGetter - provides a Thread that listens on the port for telnet connections.
+     */
+    static class ConsoleSocketGetter implements Runnable {
+
+        /**
+         * The ServerSocket to accept connections from
+         */
+        private final ServerSocket server;
+
+        private final BundleContext context;
+
+        private volatile boolean shutdown = false;
+
+        /**
+         * Constructor - sets the server and starts the thread to listen for connections.
+         * 
+         * @param server a ServerSocket to accept connections from
+         * @param context Bundle context
+         */
+        ConsoleSocketGetter(ServerSocket server, BundleContext context) {
+            this.server = server;
+            this.context = context;
+            try {
+                Method reuseAddress = server.getClass().getMethod("setReuseAddress", new Class[] { boolean.class }); //$NON-NLS-1$
+                reuseAddress.invoke(server, Boolean.TRUE);
+            } catch (Exception ex) {
+                // try to set the socket re-use property, it isn't a problem if it can't be set
+            }
+            Thread t = new Thread(this, "ConsoleSocketGetter"); //$NON-NLS-1$
+            t.setDaemon(true);
+            t.start();
+        }
+
+        public void run() {
+            // Print message containing port console actually bound to..
+            System.out.println("Listening on port: " + Integer.toString(server.getLocalPort()));
+            while (!shutdown) {
+                try {
+                    Socket socket = server.accept();
+                    if (socket == null)
+                        throw new IOException("No socket available.  Probably caused by a shutdown."); //$NON-NLS-1$
+
+                    TelnetConsoleSession session = new TelnetConsoleSession(socket, context);
+                    session.start(); // start the Input Handler
+                    context.registerService(ConsoleSession.class.getName(), session, null);
+                } catch (Exception e) {
+                    if (!shutdown)
+                        e.printStackTrace();
+                }
+            }
+        }
+
+        public void shutdown() {
+            if (shutdown)
+                return;
+            shutdown = true;
+            try {
+                server.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetOutputStream.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetOutputStream.java
new file mode 100644
index 0000000..9dced41
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TelnetOutputStream.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class adds to the output stream wrapper initial negotiation of telnet communication.
+ */
+public class TelnetOutputStream extends ConsoleOutputStream {
+
+    static final byte[] autoMessage = new byte[] { (byte) 255, (byte) 251, (byte) 1, // IAC WILL ECHO
+        (byte) 255, (byte) 251, (byte) 3, // IAC WILL SUPPRESS GO_AHEAD
+        (byte) 255, (byte) 253, (byte) 31, // IAC DO NAWS
+        (byte) 255, (byte) 253, (byte) 24 }; // IAC DO TTYPE
+
+    public TelnetOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    /**
+     * Sends the options which a server wants to negotiate with a telnet client.
+     */
+    public synchronized void autoSend() throws IOException {
+        write(autoMessage);
+        flush();
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TerminalTypeMappings.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TerminalTypeMappings.java
new file mode 100644
index 0000000..16e8fc3
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/TerminalTypeMappings.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.virgo.osgi.console.common.KEYS;
+
+public abstract class TerminalTypeMappings {
+
+    protected Map<String, KEYS> escapesToKey;
+
+    protected String[] escapes;
+
+    protected byte BACKSPACE;
+
+    protected byte DEL;
+
+    public TerminalTypeMappings() {
+        escapesToKey = new HashMap<String, KEYS>();
+        escapesToKey.put("[A", KEYS.UP); //$NON-NLS-1$
+        escapesToKey.put("[B", KEYS.DOWN); //$NON-NLS-1$
+        escapesToKey.put("[C", KEYS.RIGHT); //$NON-NLS-1$
+        escapesToKey.put("[D", KEYS.LEFT); //$NON-NLS-1$
+        escapesToKey.put("[G", KEYS.CENTER); //$NON-NLS-1$
+        setKeypadMappings();
+        createEscapes();
+    }
+
+    public Map<String, KEYS> getEscapesToKey() {
+        return escapesToKey;
+    }
+
+    public String[] getEscapes() {
+        if (escapes != null) {
+            return Arrays.copyOf(escapes, escapes.length);
+        } else {
+            return null;
+        }
+    }
+
+    public byte getBackspace() {
+        return BACKSPACE;
+    }
+
+    public byte getDel() {
+        return DEL;
+    }
+
+    public abstract void setKeypadMappings();
+
+    private void createEscapes() {
+        escapes = new String[escapesToKey.size()];
+        Object[] temp = escapesToKey.keySet().toArray();
+        for (int i = 0; i < escapes.length; i++) {
+            escapes[i] = (String) temp[i];
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT100TerminalTypeMappings.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT100TerminalTypeMappings.java
new file mode 100644
index 0000000..375452c
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT100TerminalTypeMappings.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+
+public class VT100TerminalTypeMappings extends TerminalTypeMappings {
+
+    public VT100TerminalTypeMappings() {
+        super();
+        BACKSPACE = 127;
+        DEL = -1;
+    }
+
+    @Override
+    public void setKeypadMappings() {
+        escapesToKey.put("[H", KEYS.HOME); //$NON-NLS-1$
+        escapesToKey.put("[4~", KEYS.END); //$NON-NLS-1$
+        escapesToKey.put("[5~", KEYS.PGUP); //$NON-NLS-1$
+        escapesToKey.put("[6~", KEYS.PGDN); //$NON-NLS-1$
+        escapesToKey.put("[2~", KEYS.INS); //$NON-NLS-1$
+        escapesToKey.put("[3~", KEYS.DEL); //$NON-NLS-1$
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT220TerminalTypeMappings.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT220TerminalTypeMappings.java
new file mode 100644
index 0000000..12075f3
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT220TerminalTypeMappings.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.ANSITerminalTypeMappings;
+
+public class VT220TerminalTypeMappings extends ANSITerminalTypeMappings {
+
+    public VT220TerminalTypeMappings() {
+        super();
+
+        BACKSPACE = 127;
+        DEL = -1;
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT320TerminalTypeMappings.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT320TerminalTypeMappings.java
new file mode 100644
index 0000000..9a83537
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/VT320TerminalTypeMappings.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.telnet.TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+
+public class VT320TerminalTypeMappings extends TerminalTypeMappings {
+
+    public VT320TerminalTypeMappings() {
+        super();
+        BACKSPACE = 8;
+        DEL = 127;
+    }
+
+    @Override
+    public void setKeypadMappings() {
+        escapesToKey.put("[H", KEYS.HOME);
+        escapesToKey.put("[F", KEYS.END);
+        escapesToKey.put("[5~", KEYS.PGUP); //$NON-NLS-1$
+        escapesToKey.put("[6~", KEYS.PGDN); //$NON-NLS-1$
+        escapesToKey.put("[2~", KEYS.INS); //$NON-NLS-1$
+        escapesToKey.put("[3~", KEYS.DEL); //$NON-NLS-1$
+
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHook.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHook.java
new file mode 100644
index 0000000..8c2b71e
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHook.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet.hook;
+
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.eclipse.osgi.baseadaptor.BaseAdaptor;
+import org.eclipse.osgi.baseadaptor.hooks.AdaptorHook;
+import org.eclipse.osgi.framework.internal.core.FrameworkProperties;
+import org.eclipse.osgi.framework.log.FrameworkLog;
+import org.eclipse.virgo.osgi.console.telnet.TelnetManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+import java.io.IOException;
+import java.net.URLConnection;
+import java.util.Properties;
+
+/**
+ * This adaptor hook starts the telnet implementation on the port, specified by the OSGi property osgi.console.
+ * Principally, on this port listens the Equinox shell. In order to avoid this, the value of the property (if integer)
+ * is stored, and the property is removed. In this way, when the Equinox ConsoleManager starts, it cannot retrieve any
+ * port and does not start a console on it. After that, during the start of the framework, the hook recovers the
+ * property and starts the telnet on this port.
+ * <p/>
+ * It is possible to pass not only the port, but also the host, in order to restrict the opened server socket to a
+ * particular network address on the host.
+ */
+public class TelnetHook implements AdaptorHook {
+
+    private boolean consolePortAvailable;
+
+    private String consoleValue;
+
+    private TelnetManager telnetManager = null;
+
+    public void initialize(BaseAdaptor adaptor) {
+        consoleValue = FrameworkProperties.getProperty(EclipseStarter.PROP_CONSOLE);
+        if (consoleValue == null) {
+            return;
+        }
+        if (consoleValue.contains(":")) {
+            consolePortAvailable = true;
+            FrameworkProperties.getProperties().remove(EclipseStarter.PROP_CONSOLE);
+        } else {
+            try {
+                Integer.parseInt(consoleValue);
+                consolePortAvailable = true;
+                FrameworkProperties.getProperties().remove(EclipseStarter.PROP_CONSOLE);
+            } catch (NumberFormatException ex) {
+                // do nothing
+            }
+        }
+    }
+
+    public void frameworkStart(BundleContext context) throws BundleException {
+        if (consolePortAvailable == true) {
+            FrameworkProperties.setProperty(EclipseStarter.PROP_CONSOLE, String.valueOf(consoleValue));
+            telnetManager = new TelnetManager(context);
+            telnetManager.startConsoleListener();
+        }
+    }
+
+    public void frameworkStop(BundleContext context) throws BundleException {
+        if (telnetManager != null) {
+            telnetManager.stop();
+        }
+    }
+
+    public void frameworkStopping(BundleContext context) {
+        // Do nothing - we don't care about stopping event
+    }
+
+    public void addProperties(Properties properties) {
+        // Do nothing - we don't care about addProperties event
+    }
+
+    public URLConnection mapLocationToURLConnection(String location) throws IOException {
+        return null; // not supported
+    }
+
+    public void handleRuntimeError(Throwable error) {
+        // Do nothing - we don't know how to handle runtime errors
+    }
+
+    public FrameworkLog createFrameworkLog() {
+        return null; // not supported
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHookConfigurator.java b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHookConfigurator.java
new file mode 100644
index 0000000..c64c78d
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHookConfigurator.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet.hook;
+
+import org.eclipse.osgi.baseadaptor.HookConfigurator;
+import org.eclipse.osgi.baseadaptor.HookRegistry;
+
+import org.eclipse.virgo.osgi.console.telnet.hook.TelnetHook;
+
+public class TelnetHookConfigurator implements HookConfigurator {
+
+    public void addHooks(HookRegistry hookRegistry) {
+        hookRegistry.addAdaptorHook(new TelnetHook());
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/main/resources/hookconfigurators.properties b/org.eclipse.virgo.osgi.console/src/main/resources/hookconfigurators.properties
new file mode 100644
index 0000000..afd7017
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/main/resources/hookconfigurators.properties
@@ -0,0 +1 @@
+hook.configurators=org.eclipse.virgo.osgi.console.telnet.hook.TelnetHookConfigurator
\ No newline at end of file
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/ConsoleInputStreamTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/ConsoleInputStreamTests.java
new file mode 100644
index 0000000..7f63b14
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/ConsoleInputStreamTests.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ConsoleInputStreamTests {
+
+    private static final int DATA_LENGTH = 4;
+
+    @Test
+    public void addReadBufferTest() throws Exception {
+        ConsoleInputStream in = new ConsoleInputStream();
+        byte[] data = new byte[] { (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd' };
+        in.add(data);
+        byte[] read = new byte[DATA_LENGTH];
+        for (int i = 0; i < DATA_LENGTH; i++) {
+            in.read(read, i, 1);
+            Assert.assertEquals("Incorrect char read; position " + i + " expected: " + data[i] + ", actual: " + read[i], read[i], data[i]);
+        }
+    }
+
+    @Test
+    public void addReadTest() throws Exception {
+        ConsoleInputStream in = new ConsoleInputStream();
+        byte[] data = new byte[] { (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd' };
+        in.add(data);
+        for (int i = 0; i < DATA_LENGTH; i++) {
+            byte symbol = (byte) in.read();
+            Assert.assertEquals("Incorrect char read; position " + i + " expected: " + data[i] + ", actual: " + symbol, symbol, data[i]);
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/ConsoleOutputStreamTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/ConsoleOutputStreamTests.java
new file mode 100644
index 0000000..5bbee2f
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/ConsoleOutputStreamTests.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+
+public class ConsoleOutputStreamTests {
+
+    private static final int DATA_LENGTH = 4;
+
+    @Test
+    public void testWrite() throws Exception {
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+        byte[] data = new byte[] { 'a', 'b', 'c', 'd' };
+        for (byte b : data) {
+            out.write(b);
+        }
+        out.flush();
+        byte[] res = byteOut.toByteArray();
+
+        Assert.assertNotNull("Bytes not written; result null", res);
+        Assert.assertFalse("Bytes not written; result empty", res.length == 0);
+
+        for (int i = 0; i < DATA_LENGTH; i++) {
+            Assert.assertEquals("Wrong char read. Position " + i + ", expected " + data[i] + ", read " + res[i], data[i], res[i]);
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/HistoryHolderTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/HistoryHolderTests.java
new file mode 100644
index 0000000..1633418
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/HistoryHolderTests.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import org.junit.Assert;
+
+import org.eclipse.virgo.osgi.console.supportability.HistoryHolder;
+import org.junit.Test;
+
+public class HistoryHolderTests {
+
+    @Test
+    public void test() {
+        HistoryHolder historyHolder = new HistoryHolder();
+        byte[] line1 = new byte[] { 'a', 'b', 'c', 'd' };
+        byte[] line2 = new byte[] { 'x', 'y', 'z' };
+        byte[] line3 = new byte[] { 'k', 'l', 'm', 'n' };
+
+        historyHolder.add(line1);
+        historyHolder.add(line2);
+        historyHolder.add(line3);
+
+        byte[] first = historyHolder.first();
+        Assert.assertEquals("Wrong length of first member", line1.length, first.length);
+        Assert.assertArrayEquals("Wrong first member", line1, first);
+
+        byte[] last = historyHolder.last();
+        Assert.assertEquals("Wrong length of last member", line3.length, last.length);
+        Assert.assertArrayEquals("Wrong last member", line3, last);
+
+        byte[] prev = historyHolder.prev();
+        Assert.assertEquals("Wrong length of previous member", line2.length, prev.length);
+        Assert.assertArrayEquals("Wrong previous member", line2, prev);
+
+        byte[] next = historyHolder.next();
+        Assert.assertEquals("Wrong length of next member", line3.length, next.length);
+        Assert.assertArrayEquals("Wrong next member", line3, next);
+
+        historyHolder.first();
+        historyHolder.add(new byte[] {});
+        byte[] current = historyHolder.prev();
+        Assert.assertEquals("Wrong length of next member", line3.length, current.length);
+        Assert.assertArrayEquals("Wrong next member", line3, current);
+
+        historyHolder.first();
+        historyHolder.add(line1);
+        current = historyHolder.prev();
+        Assert.assertEquals("Wrong length of next member", line1.length, current.length);
+        Assert.assertArrayEquals("Wrong next member", line1, current);
+        Assert.assertArrayEquals("Second line should now be first", line2, historyHolder.first());
+
+        historyHolder.reset();
+        Assert.assertNull("History should be empty", historyHolder.first());
+        Assert.assertNull("History should be empty", historyHolder.last());
+        Assert.assertNull("History should be empty", historyHolder.next());
+        Assert.assertNull("History should be empty", historyHolder.prev());
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/SimpleByteBufferTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/SimpleByteBufferTests.java
new file mode 100644
index 0000000..0f1c716
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/common/SimpleByteBufferTests.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.common;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SimpleByteBufferTests {
+
+    @Test
+    public void testBuffer() throws Exception {
+        SimpleByteBuffer buffer = new SimpleByteBuffer();
+        buffer.add('a');
+        buffer.add('b');
+        buffer.add('c');
+        buffer.add('d');
+
+        Assert.assertTrue("Wrong buffer size; expected 4, actual " + buffer.getSize(), buffer.getSize() == 4);
+
+        check(buffer, new byte[] { 'a', 'b', 'c', 'd' });
+
+        byte[] data = buffer.getCurrentData();
+        byte[] expected = new byte[] { 'a', 'b', 'c', 'd' };
+
+        Assert.assertTrue("Data not as expected: expected length " + expected.length + ", actual length " + data.length,
+            data.length == expected.length);
+
+        for (int i = 0; i < data.length; i++) {
+            Assert.assertEquals("Incorrect data read. Position " + i + ", expected " + expected[i] + ", read " + data[i], expected[i], data[i]);
+        }
+
+        buffer.insert('a');
+        buffer.insert('b');
+        buffer.insert('c');
+        buffer.insert('d');
+
+        int pos = buffer.getPos();
+        buffer.goLeft();
+        int newPos = buffer.getPos();
+        Assert.assertEquals("Error while moving left; old pos: " + pos + ", new pos: ", pos - 1, newPos);
+
+        buffer.insert('e');
+        check(buffer, new byte[] { 'a', 'b', 'c', 'e', 'd' });
+
+        buffer.goLeft();
+        buffer.delete();
+        check(buffer, new byte[] { 'a', 'b', 'c', 'd' });
+
+        pos = buffer.getPos();
+        buffer.goRight();
+        newPos = buffer.getPos();
+        Assert.assertEquals("Error while moving right; old pos: " + pos + ", new pos: ", pos + 1, newPos);
+
+        buffer.backSpace();
+        check(buffer, new byte[] { 'a', 'b', 'c' });
+
+        buffer.delAll();
+        Assert.assertTrue("Bytes in buffer not correctly deleted", (buffer.getSize() == 0) && (buffer.getPos() == 0));
+
+        buffer.set(new byte[] { 'a', 'b', 'c', 'd' });
+        check(buffer, new byte[] { 'a', 'b', 'c', 'd' });
+
+        data = buffer.copyCurrentData();
+        Assert.assertArrayEquals("Buffer copy does not work properly", new byte[] { 'a', 'b', 'c', 'd' }, data);
+
+        buffer.goLeft();
+        buffer.replace('e');
+        check(buffer, new byte[] { 'a', 'b', 'c', 'e' });
+
+        buffer.resetPos();
+        Assert.assertTrue("Resetting position does not work properly", buffer.getPos() == 0);
+
+        Assert.assertEquals("Wrong current char", 'a', buffer.getCurrentChar());
+    }
+
+    private void check(SimpleByteBuffer buffer, byte[] expected) throws Exception {
+        byte[] data = buffer.copyCurrentData();
+
+        Assert.assertTrue("Data not as expected: expected length " + expected.length + ", actual length " + data.length,
+            data.length == expected.length);
+
+        for (int i = 0; i < data.length; i++) {
+            Assert.assertEquals("Incorrect data read. Position " + i + ", expected " + expected[i] + ", read " + data[i], expected[i], data[i]);
+        }
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/CommandCompleterTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/CommandCompleterTests.java
new file mode 100644
index 0000000..a320b0a
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/CommandCompleterTests.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.junit.Test;
+import org.junit.Assert;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext;
+import org.eclipse.virgo.teststubs.osgi.support.ObjectClassFilter;
+
+public class CommandCompleterTests {
+
+    @Test
+    public void testCompleter() throws Exception {
+        StubBundleContext context = new StubBundleContext();
+        TestCommandProvider commandProvider = new TestCommandProvider();
+        context.registerService(CommandProvider.class.getName(), commandProvider, null);
+        context.addFilter(new ObjectClassFilter(CommandProvider.class.getName()));
+        CommandCompleter completer = new CommandCompleter(context);
+        String[] matches = completer.complete("test");
+        Set<String> setMatches = convertToSet(matches);
+        Assert.assertEquals("Incorrect number of matches:", 3, setMatches.size());
+        Assert.assertTrue("Matches does not contain test", setMatches.contains("test"));
+        Assert.assertTrue("Matches does not contain testMethod", setMatches.contains("testMethod"));
+        Assert.assertTrue("Matches does not contain testMethod1", setMatches.contains("testMethod1"));
+
+        matches = completer.complete("dum");
+        setMatches = convertToSet(matches);
+        Assert.assertEquals("Incorrect number of matches:", 2, setMatches.size());
+        Assert.assertTrue("Matches does not contain dummy", setMatches.contains("dummy"));
+        Assert.assertTrue("Matches does not contain dummyMethod", setMatches.contains("dummyMethod"));
+
+        matches = completer.complete("fake");
+        setMatches = convertToSet(matches);
+        Assert.assertEquals("Incorrect number of matches:", 2, setMatches.size());
+        Assert.assertTrue("Matches does not contain fake", setMatches.contains("fake"));
+        Assert.assertTrue("Matches does not contain fake1", setMatches.contains("fake1"));
+
+        matches = completer.complete("help");
+        Assert.assertEquals("Matches should be empty", 0, matches.length);
+    }
+
+    private Set<String> convertToSet(String[] matches) {
+        HashSet<String> set = new HashSet<String>();
+        for (String match : matches) {
+            set.add(match);
+        }
+
+        return set;
+    }
+
+    class TestCommandProvider implements CommandProvider {
+
+        @Override
+        public String getHelp() {
+            return null;
+        }
+
+        public void _test() {
+
+        }
+
+        public void _testMethod() {
+
+        }
+
+        public void _testMethod1() {
+
+        }
+
+        public void _dummy() {
+
+        }
+
+        public void _dummyMethod() {
+
+        }
+
+        public void _fake() {
+
+        }
+
+        public void _fake1() {
+
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputHandlerTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputHandlerTests.java
new file mode 100644
index 0000000..80d2042
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputHandlerTests.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+public class ConsoleInputHandlerTests {
+
+    private static final long WAIT_TIME = 10000;
+
+    @Test
+    public void testHandler() throws Exception {
+        PipedInputStream input = new PipedInputStream();
+        PipedOutputStream output = new PipedOutputStream(input);
+        ConsoleInputStream in = new ConsoleInputStream();
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ConsoleInputHandler handler = new ConsoleInputHandler(input, in, byteOut, null);
+        byte[] expected = new byte[] { 'a', 'b', 'c', 'd', 'e', '\r', '\n' };
+        output.write(expected);
+        output.flush();
+        handler.start();
+
+        try {
+            Thread.sleep(WAIT_TIME);
+        } catch (Exception e) {
+            // do nothing
+        }
+
+        byte[] read = new byte[expected.length];
+        for (int i = 0; i < expected.length; i++) {
+            in.read(read, i, 4);
+            Assert.assertEquals("Incorrect char read. Position " + i + ", expected " + expected[i] + ", read " + read[i], expected[i], read[i]);
+        }
+
+        output.close();
+        input.close();
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputScannerTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputScannerTests.java
new file mode 100644
index 0000000..a9d5c7d
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/ConsoleInputScannerTests.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import org.eclipse.osgi.framework.console.CommandProvider;
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+import org.eclipse.virgo.osgi.console.telnet.ANSITerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.SCOTerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.VT100TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.VT220TerminalTypeMappings;
+import org.eclipse.virgo.osgi.console.telnet.VT320TerminalTypeMappings;
+import org.eclipse.virgo.teststubs.osgi.framework.StubBundleContext;
+import org.eclipse.virgo.teststubs.osgi.support.ObjectClassFilter;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+public class ConsoleInputScannerTests {
+
+    private static int BS;
+
+    private static final int LF = 10;
+
+    private static final int CR = 13;
+
+    private static final int ESC = 27;
+
+    private static int DELL;
+
+    @Test
+    public void test() throws Exception {
+        Set<TerminalTypeMappings> supportedEscapeSequences = new HashSet<TerminalTypeMappings>();
+        supportedEscapeSequences.add(new ANSITerminalTypeMappings());
+        supportedEscapeSequences.add(new VT100TerminalTypeMappings());
+        supportedEscapeSequences.add(new VT220TerminalTypeMappings());
+        supportedEscapeSequences.add(new VT320TerminalTypeMappings());
+        supportedEscapeSequences.add(new SCOTerminalTypeMappings());
+
+        for (TerminalTypeMappings ttMappings : supportedEscapeSequences) {
+            Map<String, KEYS> escapesToKey = ttMappings.getEscapesToKey();
+            Map<KEYS, byte[]> keysToEscapes = new HashMap<KEYS, byte[]>();
+            for (Entry<String, KEYS> entry : escapesToKey.entrySet()) {
+                keysToEscapes.put(entry.getValue(), entry.getKey().getBytes());
+            }
+
+            BS = ttMappings.getBackspace();
+            DELL = ttMappings.getDel();
+
+            testScan(ttMappings, keysToEscapes);
+        }
+    }
+
+    private void testScan(TerminalTypeMappings mappings, Map<KEYS, byte[]> keysToEscapes) throws Exception {
+        ConsoleInputStream in = new ConsoleInputStream();
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+        StubBundleContext context = new StubBundleContext();
+        TestCommandProvider commandProvider = new TestCommandProvider();
+        context.registerService(CommandProvider.class.getName(), commandProvider, null);
+        context.addFilter(new ObjectClassFilter(CommandProvider.class.getName()));
+        ConsoleInputScanner scanner = new ConsoleInputScanner(in, out, context);
+        scanner.setBackspace(mappings.getBackspace());
+        scanner.setCurrentEscapesToKey(mappings.getEscapesToKey());
+        scanner.setDel(mappings.getDel());
+        scanner.setEscapes(mappings.getEscapes());
+
+        byte[] line1 = new byte[] { 'a', 'b', 'c', 'd', 'e' };
+        byte[] line2 = new byte[] { 't', 'e', 's', 't' };
+        byte[] line3 = new byte[] { 'l', 'a', 's', 't' };
+
+        addLine(scanner, line1);
+        checkInpusStream(in, line1);
+
+        addLine(scanner, line2);
+        checkInpusStream(in, line2);
+
+        addLine(scanner, line3);
+        checkInpusStream(in, line3);
+
+        add(scanner, keysToEscapes.get(KEYS.UP));
+        add(scanner, keysToEscapes.get(KEYS.UP));
+        String res = byteOut.toString();
+        Assert.assertTrue("Error processing up arrow; expected test, actual " + res.substring(res.length() - 4), res.endsWith("test"));
+
+        add(scanner, keysToEscapes.get(KEYS.DOWN));
+        res = byteOut.toString();
+        Assert.assertTrue("Error processing down arrow; expected last, actual " + res.substring(res.length() - 4), res.endsWith("last"));
+
+        add(scanner, keysToEscapes.get(KEYS.PGUP));
+        res = byteOut.toString();
+        Assert.assertTrue("Error processing PageUp; expected abcde, actual " + res.substring(res.length() - 4), res.endsWith("abcde"));
+
+        add(scanner, keysToEscapes.get(KEYS.PGDN));
+        res = byteOut.toString();
+        Assert.assertTrue("Error processing PageDown; expected last, actual " + res.substring(res.length() - 4), res.endsWith("last"));
+
+        if (BS > 0) {
+            scanner.scan(BS);
+            res = byteOut.toString();
+            Assert.assertTrue("Error processing backspace; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+            scanner.scan('t');
+        }
+
+        if (DELL > 0) {
+            add(scanner, keysToEscapes.get(KEYS.LEFT));
+            scanner.scan(DELL);
+            res = byteOut.toString();
+            Assert.assertTrue("Error processing del; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+            scanner.scan('t');
+        }
+
+        add(scanner, keysToEscapes.get(KEYS.LEFT));
+        add(scanner, keysToEscapes.get(KEYS.LEFT));
+        add(scanner, keysToEscapes.get(KEYS.RIGHT));
+        if (DELL > 0) {
+            scanner.scan(DELL);
+        } else {
+            add(scanner, keysToEscapes.get(KEYS.DEL));
+        }
+        res = byteOut.toString();
+        Assert.assertTrue("Error processing arrows; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+        scanner.scan('t');
+
+        if (keysToEscapes.get(KEYS.DEL) != null) {
+            add(scanner, keysToEscapes.get(KEYS.LEFT));
+            add(scanner, keysToEscapes.get(KEYS.DEL));
+            res = byteOut.toString();
+            Assert.assertTrue("Error processing delete; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+            scanner.scan('t');
+        }
+
+        add(scanner, keysToEscapes.get(KEYS.HOME));
+        if (DELL > 0) {
+            scanner.scan(DELL);
+        } else {
+            add(scanner, keysToEscapes.get(KEYS.DEL));
+        }
+        res = byteOut.toString();
+        res = res.substring(res.length() - 6, res.length() - 3);
+        Assert.assertTrue("Error processing Home; expected ast, actual " + res, res.equals("ast"));
+        scanner.scan('l');
+
+        add(scanner, keysToEscapes.get(KEYS.END));
+        add(scanner, keysToEscapes.get(KEYS.LEFT));
+        if (DELL > 0) {
+            scanner.scan(DELL);
+        } else {
+            add(scanner, keysToEscapes.get(KEYS.DEL));
+        }
+        res = byteOut.toString();
+        Assert.assertTrue("Error processing End; expected las, actual " + res.substring(res.length() - 3), res.endsWith("las"));
+        scanner.scan('t');
+
+        add(scanner, keysToEscapes.get(KEYS.LEFT));
+        add(scanner, keysToEscapes.get(KEYS.INS));
+        scanner.scan('a');
+        res = byteOut.toString();
+        Assert.assertTrue("Error processing Ins; expected las, actual " + res.substring(res.length() - 4), res.endsWith("lasa"));
+
+        scanner.scan(CR);
+        scanner.scan(LF);
+        scanner.scan('t');
+        scanner.scan('e');
+        scanner.scan(9);
+        res = byteOut.toString();
+        Assert.assertTrue("Expected completion suggestions are not contained in the output", res.contains("test  testMethod"));
+    }
+
+    private static void addLine(ConsoleInputScanner scanner, byte[] line) throws Exception {
+        for (byte b : line) {
+            try {
+                scanner.scan(b);
+            } catch (Exception e) {
+                System.out.println("Error scanning symbol " + b);
+                throw new Exception("Error scanning symbol" + b);
+            }
+        }
+
+        try {
+            scanner.scan(CR);
+        } catch (Exception e) {
+            System.out.println("Error scanning symbol " + CR);
+            throw new Exception("Error scanning symbol " + CR);
+        }
+
+        try {
+            scanner.scan(LF);
+        } catch (Exception e) {
+            System.out.println("Error scanning symbol " + LF);
+            throw new Exception("Error scanning symbol " + LF);
+        }
+    }
+
+    private void add(ConsoleInputScanner scanner, byte[] sequence) throws Exception {
+        scanner.scan(ESC);
+        for (byte b : sequence) {
+            scanner.scan(b);
+        }
+    }
+
+    private void checkInpusStream(ConsoleInputStream in, byte[] expected) throws Exception {
+        // the actual number of bytes in the stream is two more than the bytes in the array, because of the CR and LF
+        // symbols, added after the array
+        byte[] read = new byte[expected.length + 2];
+        for (int i = 0; i < expected.length; i++) {
+            in.read(read, i, 1);
+            Assert.assertEquals("Incorrect char read. Position " + i + ", expected " + expected[i] + ", read " + read[i], expected[i], read[i]);
+        }
+        in.read(read, expected.length, 1);
+        in.read(read, expected.length + 1, 1);
+    }
+
+    class TestCommandProvider implements CommandProvider {
+
+        @Override
+        public String getHelp() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public void _test() {
+
+        }
+
+        public void _testMethod() {
+
+        }
+
+        public void _dummy() {
+
+        }
+
+        public void _fake() {
+
+        }
+
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/GrepTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/GrepTests.java
new file mode 100644
index 0000000..0cbbfe3
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/supportability/GrepTests.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.supportability;
+
+import java.io.ByteArrayOutputStream;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class GrepTests {
+
+    private static final int CR = 13;
+
+    private static final int LF = 10;
+
+    @Test
+    public void testGrep() throws Exception {
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        ConsoleOutputStream consoleOut = new ConsoleOutputStream(output);
+        byte[] expression = "test".getBytes();
+        Grep grep = new Grep(expression, consoleOut);
+        grep.start();
+        byte[] line1 = new byte[] { 't', 'e', 's', 't' };
+        byte[] line2 = new byte[] { 'a', 'b', 'c', 'd' };
+        byte[] line3 = new byte[] { 't', 'e', 's', 't', 'e', 'r' };
+        byte[] line4 = new byte[] { 'k', 'l', 'm', 'n' };
+        byte[] line5 = new byte[] { 't', 'e', 's', 't', 'i', 'n', 'g' };
+        byte[] line6 = new byte[] { 'o', 's', 'g', 'i', '>' };
+
+        addLine(consoleOut, line1);
+        addLine(consoleOut, line2);
+        addLine(consoleOut, line3);
+        addLine(consoleOut, line4);
+        addLine(consoleOut, line5);
+
+        for (byte b : line6) {
+            consoleOut.write(b);
+        }
+        consoleOut.flush();
+
+        grep.join();
+
+        byte[] expectedResult = new byte[] { 't', 'e', 's', 't', CR, LF, 't', 'e', 's', 't', 'e', 'r', CR, LF, 't', 'e', 's', 't', 'i', 'n', 'g', CR,
+            LF, 'o', 's', 'g', 'i', '>', ' ' };
+        byte[] result = output.toByteArray();
+
+        Assert.assertNotNull("Bytes not written; result null", result);
+        Assert.assertFalse("Bytes not written; result empty", result.length == 0);
+        Assert.assertTrue("Bytes written to output differ in number from expected", result.length == expectedResult.length);
+
+        for (int i = 0; i < result.length; i++) {
+            Assert.assertEquals("Wrong char read. Position " + i + ", expected " + expectedResult[i] + ", read " + result[i], expectedResult[i],
+                result[i]);
+        }
+    }
+
+    private static void addLine(ConsoleOutputStream out, byte[] line) throws Exception {
+        for (byte b : line) {
+            try {
+                out.write(b);
+            } catch (Exception e) {
+                System.out.println("Error writing symbol " + b);
+                throw new Exception("Error writing symbol" + b);
+            }
+        }
+
+        try {
+            out.write(CR);
+        } catch (Exception e) {
+            System.out.println("Error writing symbol " + CR);
+            throw new Exception("Error writing symbol " + CR);
+        }
+
+        try {
+            out.write(LF);
+        } catch (Exception e) {
+            System.out.println("Error writing symbol " + LF);
+            throw new Exception("Error writing symbol " + LF);
+        }
+
+        out.flush();
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/NegotiationFinishedCallbackTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/NegotiationFinishedCallbackTests.java
new file mode 100644
index 0000000..c065840
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/NegotiationFinishedCallbackTests.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NegotiationFinishedCallbackTests {
+
+    @Test
+    public void finishTest() throws Exception {
+        ServerSocket servSocket = null;
+        Socket socketClient = null;
+        Socket socketServer = null;
+        TelnetConsoleSession consoleSession = null;
+        try {
+            servSocket = new ServerSocket(0);
+            socketClient = new Socket("localhost", servSocket.getLocalPort());
+            socketServer = servSocket.accept();
+
+            consoleSession = new TelnetConsoleSession(socketServer, null);
+            NegotiationFinishedCallback callback = new NegotiationFinishedCallback(consoleSession);
+            callback.finished();
+            Assert.assertTrue("Finished not called on console session", consoleSession.isTelnetNegotiationFinished);
+        } finally {
+            if (socketClient != null) {
+                socketClient.close();
+            }
+            if (consoleSession != null) {
+                consoleSession.doClose();
+            }
+
+            try {
+                if (socketServer != null) {
+                    Assert.assertTrue("Server telnet socket is not closed.", socketServer.isClosed());
+                }
+            } finally {
+                if (servSocket != null) {
+                    servSocket.close();
+                }
+            }
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetConsoleSessionTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetConsoleSessionTests.java
new file mode 100644
index 0000000..92569b8
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetConsoleSessionTests.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+public class TelnetConsoleSessionTests {
+
+    private static final int TEST_CONTENT = 100;
+
+    private static final int IAC = 255;
+
+    @Test
+    public void testConsoleSession() throws Exception {
+        ServerSocket servSocket = null;
+        Socket socketClient = null;
+        Socket socketServer = null;
+        TelnetConsoleSession consoleServer = null;
+        OutputStream outClient = null;
+        OutputStream outServer = null;
+
+        try {
+            servSocket = new ServerSocket(0);
+            socketClient = new Socket("localhost", servSocket.getLocalPort());
+            socketServer = servSocket.accept();
+
+            consoleServer = new TelnetConsoleSession(socketServer, null);
+            consoleServer.start();
+
+            outClient = socketClient.getOutputStream();
+            outClient.write(TEST_CONTENT);
+            outClient.write('\n');
+            outClient.flush();
+
+            InputStream input = consoleServer.getInput();
+            int in = input.read();
+            Assert.assertTrue("Server received [" + in + "] instead of " + TEST_CONTENT + " from the telnet client.", in == TEST_CONTENT);
+
+            input = socketClient.getInputStream();
+            in = input.read();
+            // here IAC is expected, since when the output stream in TelnetConsoleSession is created, several telnet
+            // commands are written to it, each of them starting with IAC
+            Assert.assertTrue("Client receive telnet responses from the server unexpected value [" + in + "] instead of " + IAC + ".", in == IAC);
+        } finally {
+            if (socketClient != null) {
+                socketClient.close();
+            }
+            if (consoleServer != null) {
+                consoleServer.doClose();
+            }
+            if (outClient != null) {
+                outClient.close();
+            }
+            if (outServer != null) {
+                outServer.close();
+            }
+
+            try {
+                if (socketServer != null) {
+                    Assert.assertTrue("Server telnet socket is not closed.", socketServer.isClosed());
+                }
+            } finally {
+                if (servSocket != null) {
+                    servSocket.close();
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testConsoleSessionVoidWrapper() throws Exception {
+        ServerSocket servSocket = null;
+        Socket socketClient = null;
+        Socket socketServer = null;
+        TelnetConsoleSession consoleServer = null;
+
+        try {
+            servSocket = new ServerSocket(0);
+            socketClient = new Socket("localhost", servSocket.getLocalPort());
+            socketServer = servSocket.accept();
+
+            consoleServer = new TelnetConsoleSession(socketServer, null);
+            consoleServer.start();
+
+            OutputStream outClient = socketClient.getOutputStream();
+            outClient.write(TEST_CONTENT);
+            outClient.write('\n');
+            outClient.flush();
+
+            InputStream input = consoleServer.getInput();
+            int in = input.read();
+
+            Assert.assertTrue("Server received [" + in + "] instead of " + TEST_CONTENT + " from the telnet client.", in == TEST_CONTENT);
+        } finally {
+            if (socketClient != null) {
+                socketClient.close();
+            }
+            if (consoleServer != null) {
+                consoleServer.doClose();
+            }
+
+            try {
+                if (socketServer != null) {
+                    Assert.assertTrue("Server telnet socket is not closed.", socketServer.isClosed());
+                }
+            } finally {
+                if (servSocket != null) {
+                    servSocket.close();
+                }
+            }
+        }
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputHandlerTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputHandlerTests.java
new file mode 100644
index 0000000..d89fec1
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputHandlerTests.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.StringBufferInputStream;
+
+public class TelnetInputHandlerTests {
+
+    private static final long WAIT_TIME = 10000;
+
+    @Test
+    public void testHandler() throws Exception {
+        StringBufferInputStream input = new StringBufferInputStream("abcde");
+        ConsoleInputStream in = new ConsoleInputStream();
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+        Callback callback = createMock(Callback.class);
+        TelnetInputHandler handler = new TelnetInputHandler(input, in, out, callback);
+        handler.start();
+
+        // wait for the accept thread to start execution
+        try {
+            Thread.sleep(WAIT_TIME);
+        } catch (InterruptedException ie) {
+            // do nothing
+        }
+
+        String res = byteOut.toString();
+        Assert.assertTrue("Wrong input. Expected abcde, read " + res, res.equals("abcde"));
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputScannerTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputScannerTests.java
new file mode 100644
index 0000000..feda304
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetInputScannerTests.java
@@ -0,0 +1,212 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.eclipse.virgo.osgi.console.common.ConsoleInputStream;
+import org.eclipse.virgo.osgi.console.common.ConsoleOutputStream;
+import org.eclipse.virgo.osgi.console.common.KEYS;
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class TelnetInputScannerTests {
+
+    private static final int IAC = 255;
+
+    private static final int DO = 253;
+
+    private static final int TTYPE = 24;
+
+    private static final int WILL = 251;
+
+    private static final int SB = 250;
+
+    private static final int SE = 240;
+
+    private static final int SEND = 1;
+
+    private static final int IS = 0;
+
+    protected static final byte ESC = 27;
+
+    @Test
+    public void testScan() throws Exception {
+        ConsoleInputStream in = new ConsoleInputStream();
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+        Callback callback = createMock(Callback.class);
+        TelnetInputScanner scanner = new TelnetInputScanner(in, out, callback);
+        try {
+            scanner.scan((byte) 240);
+            scanner.scan((byte) 248);
+            scanner.scan((byte) 250);
+            scanner.scan((byte) 251);
+            scanner.scan((byte) 252);
+            scanner.scan((byte) 253);
+            scanner.scan((byte) 254);
+            scanner.scan((byte) 'a');
+            scanner.scan((byte) 'b');
+            scanner.scan((byte) 'c');
+        } catch (IOException e) {
+            System.out.println("Error while scanning: " + e.getMessage());
+            e.printStackTrace();
+            throw e;
+        }
+
+        String output = byteOut.toString();
+        Assert.assertTrue("Output incorrect. Expected abc, but read " + output, output.equals("abc"));
+    }
+
+    @Test
+    public void testScanESC() throws Exception {
+        ConsoleInputStream in = new ConsoleInputStream();
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        ConsoleOutputStream out = new ConsoleOutputStream(byteOut);
+        Callback callback = createMock(Callback.class);
+        TelnetInputScanner scanner = new TelnetInputScanner(in, out, callback);
+
+        try {
+            scanner.scan((byte) 'a');
+            scanner.scan((byte) ESC);
+            scanner.scan((byte) 'b');
+        } catch (IOException e) {
+            System.out.println("Error while scanning: " + e.getMessage());
+            e.printStackTrace();
+            throw e;
+        }
+
+        String output = byteOut.toString();
+        Assert.assertTrue("Output incorrect. Expected abc, but read " + output, output.equals("ab"));
+    }
+
+    @Test
+    public void testTTNegotiations() throws Exception {
+        Map<byte[], TerminalTypeMappings> ttMappings = new HashMap<byte[], TerminalTypeMappings>();
+        ttMappings.put(new byte[] { 'A', 'N', 'S', 'I' }, new ANSITerminalTypeMappings());
+        ttMappings.put(new byte[] { 'V', 'T', '1', '0', '0' }, new VT100TerminalTypeMappings());
+        ttMappings.put(new byte[] { 'V', 'T', '2', '2', '0' }, new VT220TerminalTypeMappings());
+        ttMappings.put(new byte[] { 'X', 'T', 'E', 'R', 'M' }, new VT220TerminalTypeMappings());
+        ttMappings.put(new byte[] { 'V', 'T', '3', '2', '0' }, new VT320TerminalTypeMappings());
+        ttMappings.put(new byte[] { 'S', 'C', 'O' }, new SCOTerminalTypeMappings());
+
+        for (byte[] ttype : ttMappings.keySet()) {
+            testTerminalTypeNegotiation(ttype, ttMappings.get(ttype));
+        }
+    }
+
+    private void testTerminalTypeNegotiation(byte[] terminalType, TerminalTypeMappings mappings) throws Exception {
+        PipedInputStream clientIn = new PipedInputStream();
+        PipedOutputStream serverOut = new PipedOutputStream(clientIn);
+
+        byte[] requestNegotiation = { (byte) IAC, (byte) DO, (byte) TTYPE };
+
+        TestCallback testCallback = new TestCallback();
+        TelnetOutputStream out = new TelnetOutputStream(serverOut);
+        TelnetInputScanner scanner = new TelnetInputScanner(new ConsoleInputStream(), out, testCallback);
+        out.write(requestNegotiation);
+        out.flush();
+
+        int read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", IAC, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", DO, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", TTYPE, read);
+
+        scanner.scan(IAC);
+        scanner.scan(WILL);
+        scanner.scan(TTYPE);
+
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", IAC, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", SB, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", TTYPE, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", SEND, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", IAC, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", SE, read);
+
+        scanner.scan(IAC);
+        scanner.scan(SB);
+        scanner.scan(TTYPE);
+        scanner.scan(IS);
+        scanner.scan('A');
+        scanner.scan('B');
+        scanner.scan('C');
+        scanner.scan('D');
+        scanner.scan('E');
+        scanner.scan(IAC);
+        scanner.scan(SE);
+
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", IAC, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", SB, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", TTYPE, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", SEND, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", IAC, read);
+        read = clientIn.read();
+        Assert.assertEquals("Unexpected input ", SE, read);
+
+        scanner.scan(IAC);
+        scanner.scan(SB);
+        scanner.scan(TTYPE);
+        scanner.scan(IS);
+        for (byte symbol : terminalType) {
+            scanner.scan(symbol);
+        }
+        scanner.scan(IAC);
+        scanner.scan(SE);
+
+        Assert.assertEquals("Incorrect BACKSPACE: ", mappings.getBackspace(), scanner.getBackspace());
+        Assert.assertEquals("Incorrect DELL: ", mappings.getDel(), scanner.getDel());
+
+        Map<String, KEYS> currentEscapesToKey = scanner.getCurrentEscapesToKey();
+        Map<String, KEYS> expectedEscapesToKey = mappings.getEscapesToKey();
+        for (String escape : expectedEscapesToKey.keySet()) {
+            KEYS key = expectedEscapesToKey.get(escape);
+            Assert.assertEquals("Incorrect " + key.name(), key, currentEscapesToKey.get(escape));
+        }
+
+        Assert.assertTrue("Callback not called ", testCallback.getState());
+    }
+
+    class TestCallback implements Callback {
+
+        private boolean isCalled = false;
+
+        @Override
+        public void finished() {
+            isCalled = true;
+        }
+
+        public boolean getState() {
+            return isCalled;
+        }
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetManagerTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetManagerTests.java
new file mode 100644
index 0000000..cf46a24
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetManagerTests.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2010 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.Socket;
+
+import static org.easymock.EasyMock.*;
+
+public class TelnetManagerTests {
+
+    private static final String PROP_CONSOLE = "osgi.console";
+
+    private static final String PROP_CONSOLE_VALUE_POSITIVE = "localhost:38888";
+
+    private static final String PROP_CONSOLE_VALUE_NEGATIVE = "localhost38889";
+
+    private static final int PORT_POSITIVE_TEST = 38888;
+
+    private static final int PORT_NEGATIVE_TEST = 38889;
+
+    private static final long WAIT_TIME = 5000;
+
+    private static final int TEST_CONTENT = 100;
+
+    @Test
+    public void testTelnetManager() throws Exception {
+
+        BundleContext bundleContext = createMock(BundleContext.class);
+
+        expect(bundleContext.getProperty(PROP_CONSOLE)).andReturn(PROP_CONSOLE_VALUE_POSITIVE);
+
+        replay(bundleContext);
+
+        TelnetManager telnetMngr = new TelnetManager(bundleContext);
+        telnetMngr.startConsoleListener();
+        Socket socketClient = null;
+
+        try {
+            socketClient = new Socket("localhost", PORT_POSITIVE_TEST);
+            OutputStream outClient = socketClient.getOutputStream();
+            outClient.write(TEST_CONTENT);
+            outClient.write('\n');
+            outClient.flush();
+
+            // wait for the accept thread to finish execution
+            try {
+                Thread.sleep(WAIT_TIME);
+            } catch (InterruptedException ie) {
+                // do nothing
+            }
+        } finally {
+            if (socketClient != null) {
+                socketClient.close();
+            }
+            telnetMngr.stop();
+        }
+
+        verify(bundleContext);
+    }
+
+    @Test
+    public void testTelnetManagerNegative() throws Exception {
+        BundleContext bundleContext = createMock(BundleContext.class);
+
+        expect(bundleContext.getProperty(PROP_CONSOLE)).andReturn(PROP_CONSOLE_VALUE_NEGATIVE);
+
+        replay(bundleContext);
+
+        TelnetManager telnetMngr = new TelnetManager(bundleContext);
+        telnetMngr.startConsoleListener();
+
+        boolean serverSocketNotCreated = false;
+        try {
+            Socket socketClient = new Socket("localhost", PORT_NEGATIVE_TEST);
+            socketClient.close();
+        } catch (ConnectException ec) {
+            serverSocketNotCreated = true;
+        }
+
+        telnetMngr.stop();
+        verify(bundleContext);
+
+        Assert.assertTrue("Telnet console socket getter is created on localhost", serverSocketNotCreated);
+    }
+
+    @Test
+    public void testTelnetManagerWithoutHost() throws Exception {
+        BundleContext bundleContext = createMock(BundleContext.class);
+
+        expect(bundleContext.getProperty(PROP_CONSOLE)).andReturn(String.valueOf(PORT_POSITIVE_TEST));
+
+        replay(bundleContext);
+
+        TelnetManager telnetMngr = new TelnetManager(bundleContext);
+        telnetMngr.startConsoleListener();
+        Socket socketClient = null;
+
+        try {
+            socketClient = new Socket("localhost", PORT_POSITIVE_TEST);
+            OutputStream outClient = socketClient.getOutputStream();
+            outClient.write(TEST_CONTENT);
+            outClient.write('\n');
+            outClient.flush();
+
+            // wait for the accept thread to finish execution
+            try {
+                Thread.sleep(WAIT_TIME);
+            } catch (InterruptedException ie) {
+                // do nothing
+            }
+        } finally {
+            if (socketClient != null) {
+                socketClient.close();
+            }
+            telnetMngr.stop();
+        }
+
+        verify(bundleContext);
+    }
+
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetOutputStreamTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetOutputStreamTests.java
new file mode 100644
index 0000000..7753e4a
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/TelnetOutputStreamTests.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+
+public class TelnetOutputStreamTests {
+
+    @Test
+    public void testAutoSend() throws Exception {
+        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+        TelnetOutputStream out = new TelnetOutputStream(byteOut);
+        out.autoSend();
+        out.flush();
+        byte[] message = byteOut.toByteArray();
+
+        Assert.assertNotNull("Auto message not sent", message);
+        Assert.assertFalse("Auto message not sent", message.length == 0);
+        Assert.assertTrue("Error sending auto message. Expected length: " + TelnetOutputStream.autoMessage.length + ", actual length: "
+            + message.length, message.length == TelnetOutputStream.autoMessage.length);
+
+        for (int i = 0; i < message.length; i++) {
+            Assert.assertEquals("Wrong char in auto message. Position: " + i + ", expected: " + TelnetOutputStream.autoMessage[i] + ", read: "
+                + message[i], TelnetOutputStream.autoMessage[i], message[i]);
+        }
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHookTests.java b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHookTests.java
new file mode 100644
index 0000000..5d53d2f
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/src/test/java/org/eclipse/virgo/osgi/console/telnet/hook/TelnetHookTests.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2011 SAP AG
+ * 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:
+ *    Lazar Kirchev, SAP AG - initial contribution
+ ******************************************************************************/
+
+package org.eclipse.virgo.osgi.console.telnet.hook;
+
+import java.io.OutputStream;
+import java.net.ConnectException;
+import java.net.Socket;
+
+import org.eclipse.core.runtime.adaptor.EclipseStarter;
+import org.eclipse.osgi.baseadaptor.BaseAdaptor;
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+
+import static org.easymock.EasyMock.*;
+
+public class TelnetHookTests {
+
+    @Test
+    public void testHostAndPort() throws Exception {
+        System.setProperty(EclipseStarter.PROP_CONSOLE, "localhost:3333");
+        System.setProperty("osgi.configuration.area", ".");
+        BaseAdaptor adaptor = new BaseAdaptor(null);
+        TelnetHook telnetHook = new TelnetHook();
+        telnetHook.initialize(adaptor);
+        String consoleProp = System.getProperty(EclipseStarter.PROP_CONSOLE);
+        Assert.assertNull("Console port should not be present in the properties", consoleProp);
+
+        BundleContext bundleContext = createMock(BundleContext.class);
+
+        expect(bundleContext.getProperty(EclipseStarter.PROP_CONSOLE)).andReturn("localhost:3333");
+        replay(bundleContext);
+
+        telnetHook.frameworkStart(bundleContext);
+
+        consoleProp = System.getProperty(EclipseStarter.PROP_CONSOLE);
+        Assert.assertEquals("Console port should not be present in the properties", "localhost:3333", consoleProp);
+
+        telnetHook.frameworkStop(bundleContext);
+
+        telnetHook.frameworkStopping(bundleContext);
+        telnetHook.addProperties(null);
+        telnetHook.mapLocationToURLConnection(null);
+        telnetHook.handleRuntimeError(null);
+        Assert.assertNull(telnetHook.createFrameworkLog());
+
+        Socket socketClient = null;
+        try {
+            socketClient = new Socket("localhost", 3333);
+            OutputStream outClient = socketClient.getOutputStream();
+            outClient.write(100);
+            outClient.write('\n');
+            outClient.flush();
+            Assert.fail("Server socket should be closed, no connection should be established");
+        } catch (ConnectException ex) {
+            // this is expected
+        } finally {
+            if (socketClient != null) {
+                socketClient.close();
+            }
+        }
+
+        verify(bundleContext);
+    }
+
+    @Test
+    public void testPort() {
+        BaseAdaptor adaptor = new BaseAdaptor(null);
+        TelnetHook telnetHook = new TelnetHook();
+        System.setProperty(EclipseStarter.PROP_CONSOLE, "3333");
+        telnetHook.initialize(adaptor);
+        String consoleProp = System.getProperty(EclipseStarter.PROP_CONSOLE);
+        Assert.assertNull("Console port should not be present in the properties", consoleProp);
+    }
+}
diff --git a/org.eclipse.virgo.osgi.console/template.mf b/org.eclipse.virgo.osgi.console/template.mf
new file mode 100644
index 0000000..adf77b4
--- /dev/null
+++ b/org.eclipse.virgo.osgi.console/template.mf
@@ -0,0 +1,19 @@
+Bundle-ManifestVersion: 2
+Bundle-Name: Console
+Bundle-Vendor: SpringSource Inc.
+Bundle-SymbolicName: org.eclipse.virgo.osgi.console
+Bundle-Version: 0
+Fragment-Host: org.eclipse.osgi; extension:=framework
+Excluded-Exports:
+ *.internal.*,
+ org.eclipse.core.*,
+ org.eclipse.osgi.*,
+ org.osgi.framework.*,
+ org.osgi.service.*
+Excluded-Imports:
+ org.eclipse.core.*;version="0",
+ org.eclipse.osgi.*;version="0",
+ org.osgi.framework.*;version="0",
+ org.osgi.service.*;version="0",
+ org.osgi.util.*;version="0"
+