You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by de...@apache.org on 2013/12/23 10:13:15 UTC

[21/22] CLOUDSTACK-5344: Update to allow rdp console to access hyper-v vm virtual framebuffer.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java
old mode 100644
new mode 100755
index d658d1b..0be8761
--- a/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/SizeChangeListener.java
@@ -18,5 +18,5 @@ package common;
 
 public interface SizeChangeListener {
 
-  public void sizeChanged(int width, int height);
+    public void sizeChanged(int width, int height);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtBellAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtBellAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtBellAdapter.java
new file mode 100755
index 0000000..7a1feb1
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtBellAdapter.java
@@ -0,0 +1,76 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.adapter;
+
+import java.awt.Toolkit;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+import streamer.debug.FakeSource;
+
+public class AwtBellAdapter extends BaseElement {
+
+    public AwtBellAdapter(String id) {
+        super(id);
+        declarePads();
+    }
+
+    private void declarePads() {
+        inputPads.put(STDIN, null);
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        if (buf == null)
+            return;
+
+        Toolkit.getDefaultToolkit().beep();
+    }
+
+    public String toString() {
+        return "Bell(" + id + ")";
+    }
+
+    /**
+     * Example.
+     */
+    public static void main(String args[]) {
+        System.setProperty("streamer.Element.debug", "true");
+
+        Element source = new FakeSource("source") {
+            {
+                this.incommingBufLength = 0;
+                this.delay = 1000;
+                this.numBuffers = 3;
+            }
+        };
+
+        Element sink = new AwtBellAdapter("sink");
+
+        Pipeline pipeline = new PipelineImpl("test");
+        pipeline.addAndLink(source, sink);
+        pipeline.runMainLoop("source", STDOUT, false, false);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtCanvasAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtCanvasAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtCanvasAdapter.java
new file mode 100755
index 0000000..788d370
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtCanvasAdapter.java
@@ -0,0 +1,171 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.adapter;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.WritableRaster;
+
+import common.BitmapOrder;
+import common.BitmapRectangle;
+import common.BufferedImageCanvas;
+import common.CopyRectOrder;
+import common.OrderType;
+import common.ScreenDescription;
+
+import rdpclient.rdp.ServerBitmapUpdate;
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Element;
+import streamer.Link;
+import streamer.Order;
+import streamer.Pipeline;
+import streamer.PipelineImpl;
+
+public class AwtCanvasAdapter extends BaseElement {
+
+    protected ScreenDescription screen;
+
+    public AwtCanvasAdapter(String id, BufferedImageCanvas canvas, ScreenDescription screen) {
+        super(id);
+        this.canvas = canvas;
+        this.screen = screen;
+    }
+
+    protected BufferedImageCanvas canvas;
+
+    public String toString() {
+        return "AwtRdpAdapter(" + id + ")";
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        Order order = (Order)buf.getOrder();
+        switch ((OrderType)order.type) {
+
+        case BITMAP_UPDATE:
+            handleBitmap((BitmapOrder)order, buf);
+            break;
+
+        case COPY_RECT:
+            handleCopyRect((CopyRectOrder)order, buf);
+            break;
+
+        default:
+            throw new RuntimeException("Order is not implemented: " + buf + ".");
+            // break;
+        }
+
+        buf.unref();
+    }
+
+    private void handleCopyRect(CopyRectOrder order, ByteBuffer buf) {
+        // TODO Auto-generated method stub
+        // Copy image
+        canvas.getOfflineGraphics().copyArea(order.srcX, order.srcY, order.width, order.height, order.x - order.srcX, order.y - order.srcY);
+
+        // Request update of repainted area
+        canvas.repaint(order.x, order.y, order.width, order.height);
+
+    }
+
+    private void handleBitmap(BitmapOrder order, ByteBuffer buf) {
+        // Draw rectangle on offline buffer
+        BufferedImage image = canvas.getOfflineImage();
+        Graphics2D g = (Graphics2D)image.getGraphics();
+
+        for (BitmapRectangle rectangle : order.rectangles) {
+            // *DEBUG*/System.out.println("["+this+"] DEBUG: Rectangle: " +
+            // rectangle.toString());
+
+            int x = rectangle.x;
+            int y = rectangle.y;
+            int width = rectangle.width;
+            int height = rectangle.height;
+            int bufferWidth = rectangle.bufferWidth;
+            int bufferHeight = rectangle.bufferHeight;
+
+            BufferedImage rectImage;
+            switch (rectangle.colorDepth) {
+            case 8: {
+                rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_BYTE_INDEXED, screen.colorMap);
+                WritableRaster raster = rectImage.getRaster();
+                raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toByteArray());
+                break;
+            }
+            case 15: {
+                rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_555_RGB);
+                WritableRaster raster = rectImage.getRaster();
+                raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray());
+                break;
+            }
+            case 16: {
+                rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_USHORT_565_RGB);
+                WritableRaster raster = rectImage.getRaster();
+                raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toShortArray());
+                break;
+            }
+            case 24:
+            case 32: {
+                rectImage = new BufferedImage(bufferWidth, height, BufferedImage.TYPE_INT_RGB);
+                WritableRaster raster = rectImage.getRaster();
+                raster.setDataElements(0, 0, bufferWidth, bufferHeight, rectangle.bitmapDataStream.toIntLEArray());
+                break;
+            }
+            default:
+                throw new RuntimeException("Unsupported color depth: " + rectangle.colorDepth + ".");
+            }
+
+            g.setClip(x, y, width, height);
+            g.drawImage(rectImage, x, y, null);
+
+            // Request update of repainted area
+            canvas.repaint(x, y, width, height);
+        }
+
+    }
+
+    /**
+     * Example.
+     */
+    public static void main(String args[]) {
+        // System.setProperty("streamer.Link.debug", "true");
+        // System.setProperty("streamer.Element.debug", "true");
+        // System.setProperty("streamer.Pipeline.debug", "true");
+        ByteBuffer packet = new ByteBuffer(new byte[] {0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x10, 0x00,
+                0x01, 0x04, 0x0a, 0x00, 0x0c, (byte)0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00});
+
+        Pipeline pipeline = new PipelineImpl("test");
+
+        Element bitmap = new ServerBitmapUpdate("bitmap");
+
+        BufferedImageCanvas canvas = new BufferedImageCanvas(1024, 768);
+        Element adapter = new AwtCanvasAdapter("test", canvas, null) {
+            {
+                verbose = true;
+            }
+        };
+        pipeline.addAndLink(bitmap, adapter);
+
+        bitmap.handleData(packet, null);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtClipboardAdapter.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtClipboardAdapter.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtClipboardAdapter.java
new file mode 100755
index 0000000..ccdabf8
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/adapter/AwtClipboardAdapter.java
@@ -0,0 +1,56 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.adapter;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.StringSelection;
+
+import streamer.BaseElement;
+import streamer.ByteBuffer;
+import streamer.Link;
+
+public class AwtClipboardAdapter extends BaseElement {
+
+    public static final String CLIPBOARD_CONTENT = "content";
+
+    public AwtClipboardAdapter(String id) {
+        super(id);
+        declarePads();
+    }
+
+    private void declarePads() {
+        inputPads.put(STDIN, null);
+    }
+
+    @Override
+    public void handleData(ByteBuffer buf, Link link) {
+        if (verbose)
+            System.out.println("[" + this + "] INFO: Data received: " + buf + ".");
+
+        if (buf == null)
+            return;
+
+        String content = (String)buf.getMetadata(CLIPBOARD_CONTENT);
+        StringSelection contents = new StringSelection(content);
+        Toolkit.getDefaultToolkit().getSystemClipboard().setContents(contents, null);
+    }
+
+    public String toString() {
+        return "ClipboardAdapter(" + id + ")";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Any.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Any.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Any.java
new file mode 100755
index 0000000..dd73560
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Any.java
@@ -0,0 +1,78 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import streamer.ByteBuffer;
+
+/**
+ * Any type. Don't forget to set type.
+ */
+public class Any extends Tag {
+
+    /**
+     * Raw bytes of any value.
+     */
+    public ByteBuffer value;
+
+    public Any(String name) {
+        super(name);
+    }
+
+    @Override
+    public boolean isValueSet() {
+        return value != null;
+    }
+
+    @Override
+    public long calculateLengthOfValuePayload() {
+        return value.length;
+    }
+
+    @Override
+    public void writeTagValuePayload(ByteBuffer buf) {
+        buf.writeBytes(value);
+    }
+
+    @Override
+    public void readTagValue(ByteBuffer buf, BerType typeAndFlags) {
+        long length = buf.readBerLength();
+
+        value = buf.readBytes((int)length);
+    }
+
+    @Override
+    public Tag deepCopy(String suffix) {
+        return new Any(name + suffix).copyFrom(this);
+    }
+
+    @Override
+    public Tag copyFrom(Tag tag) {
+        super.copyFrom(tag);
+        this.tagType = tag.tagType;
+        this.value = new ByteBuffer(((Any)tag).value.toByteArray());
+        return this;
+    }
+
+    @Override
+    public boolean isTypeValid(BerType typeAndFlags, boolean explicit) {
+        if (explicit)
+            return typeAndFlags.tagClass == tagClass && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagNumber;
+        else
+            return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Constants.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Constants.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Constants.java
new file mode 100755
index 0000000..6dacd60
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Constants.java
@@ -0,0 +1,83 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+public interface Asn1Constants {
+
+    /**
+     * Universal class type.
+     */
+    public static final int UNIVERSAL_CLASS = 0x00;
+
+    /**
+     * Application class type.
+     */
+    public static final int APPLICATION_CLASS = 0x40;
+
+    public static final int CONTEXT_CLASS = 0x80;
+
+    public static final int PRIVATE_CLASS = 0xC0;
+
+    /**
+     * Constructed type.
+     */
+    public static final int CONSTRUCTED = 0x20;
+
+    /**
+     * Mask to extract class.
+     */
+    public static final int CLASS_MASK = 0xC0;
+
+    /**
+     * Mask to extract type.
+     */
+    public static final int TYPE_MASK = 0x1F;
+
+    public static final int EOF = 0x00;
+    public static final int BOOLEAN = 0x01;
+    /**
+     * Integer primitive.
+     */
+    public static final int INTEGER = 0x02;
+    public static final int BIT_STRING = 0x03;
+    /**
+     * Octet string primitive.
+     */
+    public static final int OCTET_STRING = 0x04;
+    public static final int NULL = 0x05;
+    public static final int OBJECT_ID = 0x06;
+    public static final int REAL = 0x09;
+    public static final int ENUMERATED = 0x0A;
+    /**
+     * Sequence primitive.
+     */
+    public static final int SEQUENCE = 0x10;
+    public static final int SET = 0x11;
+    public static final int NUMERIC_STRING = 0x12;
+    public static final int PRINTABLE_STRING = 0x13;
+    public static final int TELETEX_STRING = 0x14;
+    public static final int VIDEOTEXT_STRING = 0x15;
+    public static final int IA5_STRING = 0x16;
+    public static final int UTCTIME = 0x17;
+    public static final int GENERAL_TIME = 0x18;
+    public static final int GRAPHIC_STRING = 0x19;
+    public static final int VISIBLE_STRING = 0x1A;
+    public static final int GENERAL_STRING = 0x1B;
+
+    public static final int EXTENDED_TYPE = 0x1F;
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Integer.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Integer.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Integer.java
new file mode 100755
index 0000000..fb8715f
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Asn1Integer.java
@@ -0,0 +1,116 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import streamer.ByteBuffer;
+
+/**
+ * Variable length integer.
+ */
+public class Asn1Integer extends Tag {
+
+    public Long value = null;
+
+    public Asn1Integer(String name) {
+        super(name);
+        this.tagType = INTEGER;
+    }
+
+    @Override
+    public void readTagValue(ByteBuffer buf, BerType typeAndFlags) {
+        // Type is already read by parent parser
+
+        long length = buf.readBerLength();
+        if (length > 8)
+            throw new RuntimeException("[" + this + "] ERROR: Integer value is too long: " + length + " bytes. Cannot handle integers more than 8 bytes long. Data: "
+                    + buf + ".");
+
+        value = buf.readSignedVarInt((int)length);
+    }
+
+    @Override
+    public Tag deepCopy(String suffix) {
+        return new Asn1Integer(name + suffix).copyFrom(this);
+    }
+
+    @Override
+    public Tag copyFrom(Tag tag) {
+        super.copyFrom(tag);
+        this.value = ((Asn1Integer)tag).value;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "= " + value;
+    }
+
+    @Override
+    public long calculateLengthOfValuePayload() {
+        if (value <= 0xff)
+            return 1;
+        if (value <= 0xffFF)
+            return 2;
+        if (value <= 0xffFFff)
+            return 3;
+        if (value <= 0xffFFffFFL)
+            return 4;
+        if (value <= 0xffFFffFFffL)
+            return 5;
+        if (value <= 0xffFFffFFffFFL)
+            return 6;
+        if (value <= 0xffFFffFFffFFffL)
+            return 7;
+
+        return 8;
+    }
+
+    @Override
+    public void writeTagValuePayload(ByteBuffer buf) {
+        long value = this.value.longValue();
+
+        if (value < 0xff) {
+            buf.writeByte((int)value);
+        } else if (value <= 0xffFF) {
+            buf.writeShort((int)value);
+        } else if (value <= 0xffFFff) {
+            buf.writeByte((int)(value >> 16));
+            buf.writeShort((int)value);
+        } else if (value <= 0xffFFffFFL) {
+            buf.writeInt((int)value);
+        } else if (value <= 0xffFFffFFffL) {
+            buf.writeByte((int)(value >> 32));
+            buf.writeInt((int)value);
+        } else if (value <= 0xffFFffFFffFFL) {
+            buf.writeShort((int)(value >> 32));
+            buf.writeInt((int)value);
+        } else if (value <= 0xffFFffFFffFFffL) {
+            buf.writeByte((int)(value >> (32 + 16)));
+            buf.writeShort((int)(value >> 32));
+            buf.writeInt((int)value);
+        } else {
+            buf.writeInt((int)(value >> 32));
+            buf.writeInt((int)value);
+        }
+    }
+
+    @Override
+    public boolean isValueSet() {
+        return value != null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BerType.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BerType.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BerType.java
new file mode 100755
index 0000000..8e53f24
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BerType.java
@@ -0,0 +1,40 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+public class BerType {
+
+    public int tagClass;
+    public boolean constructed;
+    public int typeOrTagNumber;
+
+    public BerType() {
+    }
+
+    public BerType(int tagClass, boolean constructed, int typeOrTagNumber) {
+        this.tagClass = tagClass;
+        this.constructed = constructed;
+        this.typeOrTagNumber = typeOrTagNumber;
+    }
+
+    @Override
+    public String toString() {
+        return "BerType [tagClass=" + Tag.tagClassToString(tagClass) + ", constructed=" + constructed + ", type or tag number="
+                + Tag.tagTypeOrNumberToString(tagClass, typeOrTagNumber) + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BitString.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BitString.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BitString.java
new file mode 100755
index 0000000..3aba5d4
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/BitString.java
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import streamer.ByteBuffer;
+
+public class BitString extends Tag {
+
+    /**
+     * Bit string value.
+     */
+    public ByteBuffer value;
+
+    public BitString(String name) {
+        super(name);
+        this.tagType = BIT_STRING;
+    }
+
+    @Override
+    public boolean isValueSet() {
+        return value != null;
+    }
+
+    @Override
+    public long calculateLengthOfValuePayload() {
+        return value.length;
+    }
+
+    @Override
+    public void writeTagValuePayload(ByteBuffer buf) {
+        buf.writeBytes(value);
+    }
+
+    @Override
+    public void readTagValue(ByteBuffer buf, BerType typeAndFlags) {
+        long length = buf.readBerLength();
+
+        value = buf.readBytes((int)length);
+    }
+
+    @Override
+    public Tag deepCopy(String suffix) {
+        return new BitString(name + suffix).copyFrom(this);
+    }
+
+    @Override
+    public Tag copyFrom(Tag tag) {
+        super.copyFrom(tag);
+        this.value = new ByteBuffer(((BitString)tag).value.toByteArray());
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/ObjectID.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/ObjectID.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/ObjectID.java
new file mode 100755
index 0000000..47fdb42
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/ObjectID.java
@@ -0,0 +1,67 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import streamer.ByteBuffer;
+
+public class ObjectID extends Tag {
+
+    /**
+     * Raw bytes of encoded OID.
+     */
+    public ByteBuffer value;
+
+    public ObjectID(String name) {
+        super(name);
+        this.tagType = OBJECT_ID;
+    }
+
+    @Override
+    public boolean isValueSet() {
+        return value != null;
+    }
+
+    @Override
+    public long calculateLengthOfValuePayload() {
+        return value.length;
+    }
+
+    @Override
+    public void writeTagValuePayload(ByteBuffer buf) {
+        buf.writeBytes(value);
+    }
+
+    @Override
+    public void readTagValue(ByteBuffer buf, BerType typeAndFlags) {
+        long length = buf.readBerLength();
+
+        value = buf.readBytes((int)length);
+    }
+
+    @Override
+    public Tag deepCopy(String suffix) {
+        return new ObjectID(name + suffix).copyFrom(this);
+    }
+
+    @Override
+    public Tag copyFrom(Tag tag) {
+        super.copyFrom(tag);
+        this.value = new ByteBuffer(((ObjectID)tag).value.toByteArray());
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/OctetString.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/OctetString.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/OctetString.java
new file mode 100755
index 0000000..edad843
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/OctetString.java
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import streamer.ByteBuffer;
+
+public class OctetString extends Tag {
+
+    public ByteBuffer value = null;
+
+    public OctetString(String name) {
+        super(name);
+        this.tagType = OCTET_STRING;
+    }
+
+    @Override
+    public void readTagValue(ByteBuffer buf, BerType typeAndFlags) {
+        // Type is already read by parent parser
+
+        long length = buf.readBerLength();
+
+        if (length > buf.length)
+            throw new RuntimeException("BER octet string is too long: " + length + " bytes. Data: " + buf + ".");
+
+        value = buf.readBytes((int)length);
+    }
+
+    @Override
+    public Tag deepCopy(String suffix) {
+        return new OctetString(name + suffix).copyFrom(this);
+    }
+
+    @Override
+    public Tag copyFrom(Tag tag) {
+        super.copyFrom(tag);
+        this.value = ((OctetString)tag).value;
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "= " + value;
+    }
+
+    @Override
+    public long calculateLengthOfValuePayload() {
+        if (value != null)
+            return value.length;
+        else
+            return 0;
+    }
+
+    @Override
+    public void writeTagValuePayload(ByteBuffer buf) {
+        if (value != null)
+            buf.writeBytes(value);
+        else
+            return;
+    }
+
+    @Override
+    public boolean isValueSet() {
+        return value != null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Sequence.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Sequence.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Sequence.java
new file mode 100755
index 0000000..1676f69
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Sequence.java
@@ -0,0 +1,143 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import java.util.Arrays;
+
+import streamer.ByteBuffer;
+
+/**
+ * One or more elements of different types.
+ * 
+ * Only prefixed tags are supported.
+ */
+public class Sequence extends Tag {
+
+    public Tag[] tags;
+
+    public Sequence(String name) {
+        super(name);
+        this.tagType = SEQUENCE;
+        // Sequence and SequenceOf are always encoded as constructed
+        this.constructed = true;
+    }
+
+    @Override
+    public long calculateLengthOfValuePayload() {
+        long sum = 0;
+
+        for (Tag tag : tags) {
+            long tagLength = tag.calculateFullLength();
+            sum += tagLength;
+        }
+
+        return sum;
+    }
+
+    @Override
+    public void writeTagValuePayload(ByteBuffer buf) {
+        // Write tags
+        for (Tag tag : tags) {
+            tag.writeTag(buf);
+        }
+    }
+
+    @Override
+    public void readTagValue(ByteBuffer buf, BerType typeAndFlags) {
+        // Type is already read by parent parser
+
+        long length = buf.readBerLength();
+        if (length > buf.remainderLength())
+            throw new RuntimeException("BER sequence is too long: " + length + " bytes, while buffer remainder length is " + buf.remainderLength() + ". Data: " + buf
+                    + ".");
+
+        ByteBuffer value = buf.readBytes((int)length);
+        parseContent(value);
+
+        value.unref();
+    }
+
+    protected void parseContent(ByteBuffer buf) {
+        for (int i = 0; buf.remainderLength() > 0 && i < tags.length; i++) {
+            BerType typeAndFlags = readBerType(buf);
+
+            // If current tag does not match data in buffer
+            if (!tags[i].isTypeValid(typeAndFlags)) {
+
+                // If tag is required, then throw exception
+                if (!tags[i].optional) {
+                    throw new RuntimeException("[" + this + "] ERROR: Required tag is missed: " + tags[i] + ". Unexected tag type: " + typeAndFlags + ". Data: " + buf
+                            + ".");
+                } else {
+                    // One or more tags are omitted, so skip them
+                    for (; i < tags.length; i++) {
+                        if (tags[i].isTypeValid(typeAndFlags)) {
+                            break;
+                        }
+                    }
+
+                    if (i >= tags.length || !tags[i].isTypeValid(typeAndFlags)) {
+                        throw new RuntimeException("[" + this + "] ERROR: No more tags to read or skip, but some data still left in buffer. Unexected tag type: "
+                                + typeAndFlags + ". Data: " + buf + ".");
+                    }
+                }
+            }
+
+            tags[i].readTag(buf, typeAndFlags);
+        }
+
+    }
+
+    @Override
+    public boolean isTypeValid(BerType typeAndFlags, boolean explicit) {
+        if (explicit)
+            return typeAndFlags.tagClass == tagClass && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagNumber;
+        else
+            // Sequences are always encoded as "constructed" in BER.
+            return typeAndFlags.tagClass == UNIVERSAL_CLASS && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == SEQUENCE;
+    }
+
+    @Override
+    public Tag deepCopy(String suffix) {
+        return new Sequence(name + suffix).copyFrom(this);
+    }
+
+    @Override
+    public Tag copyFrom(Tag tag) {
+        super.copyFrom(tag);
+
+        if (this.tags.length != ((Sequence)tag).tags.length)
+            throw new RuntimeException("Incompatible sequences. This: " + this + ", another: " + tag + ".");
+
+        for (int i = 0; i < tags.length; i++) {
+            this.tags[i].copyFrom(((Sequence)tag).tags[i]);
+        }
+
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "{" + Arrays.toString(tags) + " }";
+    }
+
+    @Override
+    public boolean isValueSet() {
+        return tags != null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/SequenceOf.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/SequenceOf.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/SequenceOf.java
new file mode 100755
index 0000000..aa4543c
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/SequenceOf.java
@@ -0,0 +1,81 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import java.util.ArrayList;
+
+import streamer.ByteBuffer;
+
+/**
+ * Zero or more elements of same type (array).
+ */
+public class SequenceOf extends Sequence {
+
+    /**
+     * Type of this array.
+     */
+    public Tag type;
+
+    /* Values are stored in tags[] variable inherited from Sequence. */
+
+    public SequenceOf(String name) {
+        super(name);
+    }
+
+    protected void parseContent(ByteBuffer buf) {
+        ArrayList<Tag> tagList = new ArrayList<Tag>();
+
+        for (int index = 0; buf.remainderLength() > 0; index++) {
+            // End of array is marked with two zero bytes (0x00 0x00)
+            if (buf.peekUnsignedByte(0) == 0x00 && buf.peekUnsignedByte(1) == 0x00) {
+                break;
+            }
+
+            Tag tag = (Tag)type.deepCopy(index);
+
+            tag.readTag(buf);
+            tagList.add(tag);
+        }
+
+        tags = tagList.toArray(new Tag[tagList.size()]);
+    }
+
+    @Override
+    public Tag deepCopy(String suffix) {
+        return new SequenceOf(name + suffix).copyFrom(this);
+    }
+
+    @Override
+    public Tag copyFrom(Tag tag) {
+        super.copyFrom(tag);
+        // We can create shallow copy of type, because it will not be modified
+        this.type = ((SequenceOf)tag).type;
+
+        this.tags = new Tag[((Sequence)tag).tags.length];
+        for (int i = 0; i < tags.length; i++) {
+            this.tags[i] = ((Sequence)tag).tags[i].deepCopy("");
+        }
+
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + ": " + type;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Tag.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Tag.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Tag.java
new file mode 100755
index 0000000..8fb8c82
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/asn1/Tag.java
@@ -0,0 +1,462 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.asn1;
+
+import streamer.ByteBuffer;
+
+public abstract class Tag implements Asn1Constants {
+
+    /**
+     * Name of this tag, for debugging purposes.
+     */
+    public String name = "";
+
+    /**
+     * Is this tag required or optional, for explicit tags only.
+     */
+    public boolean optional = false;
+
+    /**
+     * Tag primitive (e.g. implicit boolean), or constructed (e.g. sequence, or
+     * explicit boolean).
+     */
+    public boolean constructed = false;
+
+    /**
+     * Class of tag, when it is explicit.
+     */
+    public int tagClass = UNIVERSAL_CLASS;
+
+    /**
+     * Tag number (e.g. index in sequence), when tag is explicit.
+     */
+    public int tagNumber = -1;
+
+    /**
+     * Tag type (e.g. INDER), when tag is implicit.
+     */
+    public int tagType = -1;
+
+    /**
+     * If tag is explicit, then it is prefixed with tag number, so it can be
+     * optional or used in unordered set.
+     */
+    public boolean explicit = false;
+
+    public Tag(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Write tag value, with or without prefix.
+     */
+    public void writeTag(ByteBuffer buf) {
+
+        if (!isMustBeWritten())
+            return;
+
+        // Write prefix, when necessary
+        if (explicit) {
+
+            // Write tag prefix, always constructed
+            BerType berTagPrefix = new BerType(tagClass, true, tagNumber);
+            writeBerType(buf, berTagPrefix);
+
+            // Write tag prefix length
+            buf.writeBerLength(calculateLength());
+
+            // Write tag value
+            writeTagValue(buf);
+        } else {
+            // If implicit, just write tag value
+            writeTagValue(buf);
+        }
+    }
+
+    /**
+     * Must return true when value of this tag is set or tag is required, so it
+     * can be written, false otherwise.
+     */
+    public boolean isMustBeWritten() {
+        return !optional || isValueSet();
+    }
+
+    /**
+     * Must return true when value of this tag is set or tag is required, so it
+     * can be written, false otherwise.
+     */
+    public abstract boolean isValueSet();
+
+    /**
+     * Calculate full length of tag, including type (or prefix, when explicit).
+     */
+    public long calculateFullLength() {
+        if (!isMustBeWritten())
+            return 0;
+
+        // Length of value, including type
+        long length = calculateLength();
+
+        if (!explicit) {
+            // Length of tag type and it length
+            length += calculateLengthOfTagTypeOrTagNumber(tagType) + calculateLengthOfLength(length);
+        } else {
+            // Length of tag prefix and it length
+            length += calculateLengthOfTagTypeOrTagNumber(tagNumber) + calculateLengthOfLength(length);
+        }
+
+        return length;
+    }
+
+    /**
+     * Calculate length of tag, including type when explicit, but without length
+     * of prefix (or type, when implicit).
+     */
+    public long calculateLength() {
+        if (!isMustBeWritten())
+            return 0;
+
+        // Length of value
+        long length = calculateLengthOfValuePayload();
+
+        if (explicit) {
+            // Length of tag type and it length
+            length += calculateLengthOfTagTypeOrTagNumber(tagType) + calculateLengthOfLength(length);
+        }
+
+        return length;
+    }
+
+    /**
+     * Calculate length of BER length.
+     */
+    public int calculateLengthOfLength(long length) {
+        if (length < 0)
+            throw new RuntimeException("[" + this + "] ERROR: Length of tag cannot be less than zero: " + length + ".");
+
+        if (length <= 0x7f)
+            return 1;
+        if (length <= 0xff)
+            return 2;
+        if (length <= 0xffFF)
+            return 3;
+        if (length <= 0xffFFff)
+            return 4;
+        if (length <= 0xffFFffFFL)
+            return 5;
+        if (length <= 0xffFFffFFffL)
+            return 6;
+        if (length <= 0xffFFffFFffFFL)
+            return 7;
+        if (length <= 0xffFFffFFffFFffL)
+            return 8;
+
+        return 9;
+    }
+
+    /**
+     * Calculate length of type to tag number. Values less than 31 are encoded
+     * using lower 5 bits of first byte of tag. Values larger than 31 are
+     * indicated by lower 5 bits set to 1 (0x1F, 31), and next bytes are contain
+     * value in network order, where topmost bit of byte (0x80) indicates is value
+     * contains more bytes, i.e. last byte of sequence has this bit set to 0.
+     */
+    public int calculateLengthOfTagTypeOrTagNumber(int tagType) {
+        if (tagType >= EXTENDED_TYPE)
+            throw new RuntimeException("Multibyte tag types are not supported yet.");
+
+        return 1;
+    }
+
+    /**
+     * Calculate length of payload only, without tag prefix, tag type, and
+     * lengths.
+     * 
+     * @return
+     */
+    public abstract long calculateLengthOfValuePayload();
+
+    /**
+     * Write tag value only, without prefix.
+     */
+    public void writeTagValue(ByteBuffer buf) {
+
+        // Write type
+        BerType valueType = new BerType(UNIVERSAL_CLASS, constructed, tagType);
+        writeBerType(buf, valueType);
+
+        // Write length
+        long lengthOfPayload = calculateLengthOfValuePayload();
+        buf.writeBerLength(lengthOfPayload);
+
+        // Store cursor to check is calculated length matches length of actual bytes
+        // written
+        int storedCursor = buf.cursor;
+
+        // Write value
+        writeTagValuePayload(buf);
+
+        // Check is calculated length matches length of actual bytes written, to catch errors early
+        int actualLength = buf.cursor - storedCursor;
+        if (actualLength != lengthOfPayload)
+            throw new RuntimeException("[" + this + "] ERROR: Unexpected length of data in buffer. Expected " + lengthOfPayload + " of bytes of payload, but "
+                    + actualLength + " bytes are written instead. Data: " + buf + ".");
+    }
+
+    /**
+     * Write tag value only, without prefix, tag type, and length.
+     */
+    public abstract void writeTagValuePayload(ByteBuffer buf);
+
+    /**
+     * Read required tag, i.e. we are 100% sure that byte buffer will contain this
+     * tag, or exception will be thrown otherwise.
+     * 
+     * @param buf
+     *          buffer with tag data
+     */
+    public void readTag(ByteBuffer buf) {
+        BerType typeAndFlags = readBerType(buf);
+
+        // * DEBUG */System.out.println("Tag, read " + typeAndFlags);
+
+        if (!isTypeValid(typeAndFlags))
+            throw new RuntimeException("[" + this + "] Unexpected type: " + typeAndFlags + ".");
+
+        readTag(buf, typeAndFlags);
+    }
+
+    /**
+     * Read tag when it type is already read.
+     */
+    public void readTag(ByteBuffer buf, BerType typeAndFlags) {
+
+        if (explicit) {
+            long length = buf.readBerLength();
+
+            if (length > buf.length)
+                throw new RuntimeException("BER value is too long: " + length + " bytes. Data: " + buf + ".");
+
+            ByteBuffer value = buf.readBytes((int)length);
+
+            readTagValue(value);
+
+            value.unref();
+        } else {
+
+            readTagValue(buf, typeAndFlags);
+        }
+    }
+
+    /**
+     * Read tag value only, i.e. it prefix is already read.
+     */
+    public void readTagValue(ByteBuffer value) {
+        BerType typeAndFlags = readBerType(value);
+
+        // * DEBUG */System.out.println("Tag, read value " + typeAndFlags);
+
+        if (!isTypeValid(typeAndFlags, false))
+            throw new RuntimeException("[" + this + "] Unexpected type: " + typeAndFlags + ".");
+
+        readTagValue(value, typeAndFlags);
+    }
+
+    /**
+     * Check are tag type and flags valid for this tag.
+     */
+    public final boolean isTypeValid(BerType typeAndFlags) {
+        return isTypeValid(typeAndFlags, explicit);
+    }
+
+    /**
+     * Check are tag type and flags valid for this tag with or without tag prefix.
+     * 
+     * @param explicit
+     *          if true, then value is wrapped in tag prefix
+     */
+    public boolean isTypeValid(BerType typeAndFlags, boolean explicit) {
+        if (explicit)
+            return typeAndFlags.tagClass == tagClass && typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == tagNumber;
+        else
+            return typeAndFlags.tagClass == UNIVERSAL_CLASS && !typeAndFlags.constructed && typeAndFlags.typeOrTagNumber == this.tagType;
+    }
+
+    @Override
+    public String toString() {
+        return "  \nTag [name="
+                + name
+
+                + ((constructed) ? ", constructed=" + constructed : "")
+
+                + (", tagType=" + tagTypeOrNumberToString(UNIVERSAL_CLASS, tagType))
+
+                + ((explicit) ? ", explicit=" + explicit + ", optional=" + optional + ", tagClass=" + tagClassToString(tagClass) + ", tagNumber="
+                        + tagTypeOrNumberToString(tagClass, tagNumber) : "") + "]";
+    }
+
+    public static final String tagTypeOrNumberToString(int tagClass, int tagTypeOrNumber) {
+        switch (tagClass) {
+        case UNIVERSAL_CLASS:
+            switch (tagTypeOrNumber) {
+            case EOF:
+                return "EOF";
+            case BOOLEAN:
+                return "BOOLEAN";
+            case INTEGER:
+                return "INTEGER";
+            case BIT_STRING:
+                return "BIT_STRING";
+            case OCTET_STRING:
+                return "OCTET_STRING";
+            case NULL:
+                return "NULL";
+            case OBJECT_ID:
+                return "OBJECT_ID";
+            case REAL:
+                return "REAL";
+            case ENUMERATED:
+                return "ENUMERATED";
+            case SEQUENCE:
+                return "SEQUENCE";
+            case SET:
+                return "SET";
+            case NUMERIC_STRING:
+                return "NUMERIC_STRING";
+            case PRINTABLE_STRING:
+                return "PRINTABLE_STRING";
+            case TELETEX_STRING:
+                return "TELETEX_STRING";
+            case VIDEOTEXT_STRING:
+                return "VIDEOTEXT_STRING";
+            case IA5_STRING:
+                return "IA5_STRING";
+            case UTCTIME:
+                return "UTCTIME";
+            case GENERAL_TIME:
+                return "GENERAL_TIME";
+            case GRAPHIC_STRING:
+                return "GRAPHIC_STRING";
+            case VISIBLE_STRING:
+                return "VISIBLE_STRING";
+            case GENERAL_STRING:
+                return "GENERAL_STRING";
+            case EXTENDED_TYPE:
+                return "EXTENDED_TYPE (multibyte)";
+            default:
+                return "UNKNOWN(" + tagTypeOrNumber + ")";
+
+            }
+
+        default:
+            return "[" + tagTypeOrNumber + "]";
+        }
+    }
+
+    public static final String tagClassToString(int tagClass) {
+        switch (tagClass) {
+        case UNIVERSAL_CLASS:
+            return "UNIVERSAL";
+        case CONTEXT_CLASS:
+            return "CONTEXT";
+        case APPLICATION_CLASS:
+            return "APPLICATION";
+        case PRIVATE_CLASS:
+            return "PRIVATE";
+        default:
+            return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Read BER tag type.
+     */
+    public BerType readBerType(ByteBuffer buf) {
+        int typeAndFlags = buf.readUnsignedByte();
+
+        int tagClass = typeAndFlags & CLASS_MASK;
+
+        boolean constructed = (typeAndFlags & CONSTRUCTED) != 0;
+
+        int type = typeAndFlags & TYPE_MASK;
+        if (type == EXTENDED_TYPE)
+            throw new RuntimeException("Extended tag types/numbers (31+) are not supported yet.");
+
+        return new BerType(tagClass, constructed, type);
+    }
+
+    /**
+     * Write BER tag type.
+     */
+    public void writeBerType(ByteBuffer buf, BerType berType) {
+
+        if (berType.typeOrTagNumber >= EXTENDED_TYPE || berType.typeOrTagNumber < 0)
+            throw new RuntimeException("Extended tag types/numbers (31+) are not supported yet: " + berType + ".");
+
+        if ((berType.tagClass & CLASS_MASK) != berType.tagClass)
+            throw new RuntimeException("Value of BER tag class is out of range: " + berType.tagClass + ". Expected values: " + UNIVERSAL_CLASS + ", " + CONTEXT_CLASS
+                    + ", " + APPLICATION_CLASS + ", " + PRIVATE_CLASS + ".");
+
+        int typeAndFlags = berType.tagClass | ((berType.constructed) ? CONSTRUCTED : 0) | berType.typeOrTagNumber;
+
+        buf.writeByte(typeAndFlags);
+    }
+
+    /**
+     * Read tag value only, i.e. it prefix is already read, when value type is
+     * already read.
+     * 
+     * @param buf
+     *          buffer with tag data
+     */
+    public abstract void readTagValue(ByteBuffer buf, BerType typeAndFlags);
+
+    /**
+     * Create deep copy of this tag with given suffix appended to name.
+     * 
+     * @param suffix
+     *          suffix to add to tag name, or empty string
+     * @return deep copy of this tag
+     */
+    public abstract Tag deepCopy(String suffix);
+
+    /**
+     * Create deep copy of this tag for array or set.
+     * 
+     * @param index
+     *          index of element in array or set
+     * @return deep copy of this tag
+     */
+    public Tag deepCopy(int index) {
+        return deepCopy("[" + index + "]");
+    }
+
+    /**
+     * Copy tag values from an other tag, except name.
+     * 
+     * @return this
+     */
+    public Tag copyFrom(Tag tag) {
+        this.constructed = tag.constructed;
+        this.explicit = tag.explicit;
+        this.optional = tag.optional;
+        this.tagClass = tag.tagClass;
+        this.tagNumber = tag.tagNumber;
+        return this;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IncrementalOption.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IncrementalOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IncrementalOption.java
new file mode 100755
index 0000000..eb24d11
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IncrementalOption.java
@@ -0,0 +1,28 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class IncrementalOption extends Option {
+    int value = 0;
+
+    @Override
+    public int parse(int position, String[] args) {
+        value++;
+        return super.parse(position, args);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IntOption.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IntOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IntOption.java
new file mode 100755
index 0000000..87e732a
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/IntOption.java
@@ -0,0 +1,41 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class IntOption extends Option {
+    public int value = 0;
+
+    @Override
+    public int parse(int position, String[] args) {
+        if (position + 1 >= args.length)
+            throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\".");
+
+        value = Integer.parseInt(args[position + 1]);
+        return super.parse(position, args) + 1;
+    }
+
+    @Override
+    public String help() {
+        StringBuilder help = new StringBuilder();
+        help.append(join("|", name, alias, aliases)).append(" VALUE\t").append(description);
+        if (required)
+            help.append(" Required.");
+        else if (value != 0)
+            help.append(" Default value is \"").append("" + value).append("\".");
+        return help.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/NoArgumentForOptionException.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/NoArgumentForOptionException.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/NoArgumentForOptionException.java
new file mode 100755
index 0000000..a074672
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/NoArgumentForOptionException.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class NoArgumentForOptionException extends RuntimeException {
+    public NoArgumentForOptionException(String message) {
+        super(message);
+    }
+
+    private static final long serialVersionUID = 1L;
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/Option.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/Option.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/Option.java
new file mode 100755
index 0000000..e038075
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/Option.java
@@ -0,0 +1,102 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class Option {
+
+    public String name = "";
+    public String alias = null;
+    public String aliases[] = null;
+
+    public String description = "";
+    public boolean used = false;
+    public boolean required = false;
+
+    /**
+     * Parse option value, if any.
+     * 
+     * @param position
+     *          position of this option in list of arguments
+     * @param args
+     *          command line arguments
+     * @return how many arguments are consumed, at least 1
+     */
+    public int parse(int position, String args[]) {
+        used = true;
+        return 1;
+    }
+
+    @Override
+    public String toString() {
+        return help();
+    }
+
+    /**
+     * Return help string for this option. Example:
+     * 
+     * <pre>
+     *   --foo|-f    Foo option.
+     * </pre>
+     */
+    public String help() {
+        return join("|", name, alias, aliases) + "\t" + description + ((required) ? " Required." : "");
+    }
+
+    /**
+     * Return string like "--foo|-f|--another-foo-alias".
+     */
+    protected String join(String delim, String name, String alias, String aliases[]) {
+
+        // Option name is mandatory
+        StringBuilder sb = new StringBuilder(name.length());
+        sb.append(name);
+
+        // Alias is optional
+        if (alias != null && alias.length() > 0) {
+            sb.append(delim).append(alias);
+        }
+
+        // Other aliases are optional too
+        if (aliases != null) {
+            for (String s : aliases) {
+                if (s != null && s.length() > 0) {
+                    sb.append(delim).append(s);
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Return description of options in format suitable for help and usage text.
+     * 
+     * @param header
+     *          header string to print before list of options
+     * @param options
+     *          list of options to print
+     */
+    public static String toHelp(String header, Option[] options) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(header).append(":\n");
+        for (Option option : options) {
+            sb.append("  ").append(option.help()).append('\n');
+        }
+        return sb.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionParser.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionParser.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionParser.java
new file mode 100755
index 0000000..5c45e9e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionParser.java
@@ -0,0 +1,147 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Simple parser of GNU-like options.
+ */
+public class OptionParser {
+
+    public static Option helpOption() {
+        return new Option() {
+            {
+                name = "--help";
+                alias = "-h";
+            }
+        };
+    }
+
+    /**
+     * Parse options, capture values and return rest of arguments.
+     * 
+     * @param args
+     *          command line arguments to parse
+     * @param startFrom
+     *          number of first argument to start parsing from
+     * @param options
+     *          options to fill with values
+     * @return rest of command line after first non-option or "--" separator
+     */
+    public static String[] parseOptions(String args[], int startFrom, Option options[]) {
+        // Convert array of options into map, where key is option name or alias
+        Map<String, Option> optionMap = new HashMap<String, Option>(options.length);
+        for (Option option : options) {
+            optionMap.put(option.name, option);
+
+            if (option.alias != null)
+                optionMap.put(option.alias, option);
+
+            if (option.aliases != null) {
+                for (String alias : option.aliases)
+                    optionMap.put(alias, option);
+            }
+        }
+
+        // Parse arguments
+        int position = startFrom;
+        while (position < args.length) {
+            // Double dash means end of options
+            String optionName = args[position];
+            if (optionName.equals("--")) {
+                position++;
+                break;
+            }
+
+            Option option = optionMap.get(optionName);
+
+            // If option is not found, then this is argument, unless is starts with
+            // dash
+            if (option == null)
+                if (!optionName.startsWith("-"))
+                    break;
+                else
+                    throw new UnknownOptionException("Option \"" + optionName
+                            + "\" is unknown. If this is not an option, then use \"--\" to separate options and arguments. Known options: " + optionMap.keySet().toString());
+
+            position += option.parse(position, args);
+        }
+
+        // Check is required options are used on command line
+        for (Option option : options) {
+            if (option.required && !option.used)
+                throw new OptionRequiredException("Option \"" + option.name + "\" is required.");
+        }
+
+        // Return rest of arguments, which are left after options
+        return (position < args.length) ? Arrays.copyOfRange(args, position, args.length) : new String[] {};
+    }
+
+    /* Example. */
+    public static void main(String args[]) {
+        if (args.length == 0)
+            args = new String[] {"--help", "--foo", "fooval", "--bar", "123", "-v", "--verbose", "-v", "-a", "a1", "-aa", "a2", "-aaa", "a3", "rest", "of",
+                    "arguments"};
+
+        StringOption foo = new StringOption() {
+            {
+                name = "--foo";
+                alias = "-f";
+                value = "fooDefault";
+            }
+        };
+
+        IntOption bar = new IntOption() {
+            {
+                name = "--bar";
+                alias = "-b";
+                value = 123;
+            }
+        };
+
+        IncrementalOption verbose = new IncrementalOption() {
+            {
+                name = "--verbose";
+                alias = "-v";
+            }
+        };
+
+        StringArrayOption array = new StringArrayOption() {
+            {
+                name = "--array";
+                alias = "-a";
+                aliases = new String[] {"-aa", "-aaa"};
+            }
+        };
+
+        String arguments[] = OptionParser.parseOptions(args, 0, new Option[] {helpOption(), foo, bar, verbose, array});
+
+        assertTrue(foo.value.equals("fooval"));
+        assertTrue(bar.value == 123);
+        assertTrue(verbose.value == 3);
+        assertTrue(Arrays.equals(array.value, new String[] {"a1", "a2", "a3"}));
+        assertTrue(Arrays.equals(arguments, new String[] {"rest", "of", "arguments"}));
+    }
+
+    public static void assertTrue(boolean result) {
+        if (!result)
+            throw new AssertionError();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionRequiredException.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionRequiredException.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionRequiredException.java
new file mode 100755
index 0000000..b397627
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/OptionRequiredException.java
@@ -0,0 +1,26 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class OptionRequiredException extends RuntimeException {
+    public OptionRequiredException(String message) {
+        super(message);
+    }
+
+    private static final long serialVersionUID = 1L;
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringArrayOption.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringArrayOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringArrayOption.java
new file mode 100755
index 0000000..b2f3c30
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringArrayOption.java
@@ -0,0 +1,38 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+import java.util.Arrays;
+
+public class StringArrayOption extends Option {
+    public String value[] = null;
+
+    @Override
+    public int parse(int position, String[] args) {
+        if (position + 1 >= args.length)
+            throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\".");
+
+        // Append value to end of array of values
+        if (value == null) {
+            value = new String[] {args[position + 1]};
+        } else {
+            value = Arrays.copyOf(value, value.length + 1);
+            value[value.length - 1] = args[position + 1];
+        }
+        return super.parse(position, args) + 1;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringEnumerationOption.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringEnumerationOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringEnumerationOption.java
new file mode 100755
index 0000000..f59952e
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringEnumerationOption.java
@@ -0,0 +1,72 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class StringEnumerationOption extends Option {
+    public String value = "";
+    public String choices[] = new String[] {};
+
+    @Override
+    public int parse(int position, String[] args) {
+        if (position + 1 >= args.length)
+            throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\".");
+
+        value = args[position + 1];
+
+        for (String s : choices) {
+            if (value.equals(s))
+                return super.parse(position, args) + 1;
+        }
+
+        throw new NoArgumentForOptionException("Unexpected argument for option \"" + args[position] + "\": \"" + value + "\". Expected argument: "
+                + join("|", choices) + ".");
+    }
+
+    @Override
+    public String help() {
+        StringBuilder help = new StringBuilder();
+        help.append(join("|", name, alias, aliases)).append(" ").append(join("|", choices)).append("\t").append(description);
+        if (required)
+            help.append(" Required.");
+        else if (value != null && value.length() > 0)
+            help.append(" Default value is \"").append(value).append("\".");
+        return help.toString();
+    }
+
+    /**
+     * Join strings in array into one large string.
+     */
+    protected String join(String delim, String values[]) {
+        StringBuilder sb = new StringBuilder();
+        if (values != null) {
+            boolean first = true;
+            for (String s : values) {
+                if (s != null && s.length() > 0) {
+                    if (first)
+                        first = false;
+                    else
+                        sb.append(delim);
+
+                    sb.append(s);
+                }
+            }
+        }
+
+        return sb.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringOption.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringOption.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringOption.java
new file mode 100755
index 0000000..ec9c82c
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/StringOption.java
@@ -0,0 +1,41 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class StringOption extends Option {
+    public String value = "";
+
+    @Override
+    public int parse(int position, String[] args) {
+        if (position + 1 >= args.length)
+            throw new NoArgumentForOptionException("Cannot find required argument for option \"" + args[position] + "\".");
+
+        value = args[position + 1];
+        return super.parse(position, args) + 1;
+    }
+
+    @Override
+    public String help() {
+        StringBuilder help = new StringBuilder();
+        help.append(join("|", name, alias, aliases)).append(" VALUE\t").append(description);
+        if (required)
+            help.append(" Required.");
+        else if (value != null && value.length() > 0)
+            help.append(" Default value is \"").append(value).append("\".");
+        return help.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/4f3611f9/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/UnknownOptionException.java
----------------------------------------------------------------------
diff --git a/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/UnknownOptionException.java b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/UnknownOptionException.java
new file mode 100755
index 0000000..58cce73
--- /dev/null
+++ b/services/console-proxy-rdp/rdpconsole/src/main/java/common/opt/UnknownOptionException.java
@@ -0,0 +1,27 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package common.opt;
+
+public class UnknownOptionException extends RuntimeException {
+
+    public UnknownOptionException(String message) {
+        super(message);
+    }
+
+    private static final long serialVersionUID = 1L;
+
+}