From 8dc526607c7fa09081a3939f27cddc07d01809fa Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Tue, 04 Feb 2014 16:53:37 +0000
Subject: [PATCH] OPENDJ-1307 Migrate server ASN1 classes to SDK

---
 opendj-core/src/test/java/org/forgerock/opendj/io/ASN1OutputStreamWriterTestCase.java |    2 
 opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java                           |   61 ++++++++++++++++---
 opendj-core/src/main/java/org/forgerock/opendj/io/ASN1InputStreamReader.java          |   12 ++++
 opendj-core/src/main/java/org/forgerock/opendj/io/ASN1Reader.java                     |   25 ++++++++
 opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/ASN1BufferReader.java       |   12 ++++
 opendj-core/src/main/java/org/forgerock/opendj/io/ASN1ByteSequenceReader.java         |   12 ++++
 opendj-core/src/main/java/org/forgerock/opendj/io/ASN1OutputStreamWriter.java         |   30 +++++++++-
 7 files changed, 139 insertions(+), 15 deletions(-)

diff --git a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java
index c316c03..4805a24 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java
@@ -45,6 +45,12 @@
 public final class ASN1 {
 
     /**
+     * Maximum buffer size when reading ASN1. Buffers above this threshold will
+     * be discarded for garbage collection to avoid OutOfMemoryErrors.
+     */
+    private static final int DEFAULT_MAX_BUFFER_SIZE = 32 * 1024;
+
+    /**
      * The byte array containing the pre-encoded ASN.1 encoding for a boolean
      * value of "false".
      */
@@ -62,14 +68,18 @@
     public static final byte UNIVERSAL_BOOLEAN_TYPE = 0x01;
 
     /**
-     * The BER type that is assigned to the universal enumerated type.
-     */
-    public static final byte UNIVERSAL_ENUMERATED_TYPE = 0x0A;
-
-    /**
      * The BER type that is assigned to the universal integer type.
      */
     public static final byte UNIVERSAL_INTEGER_TYPE = 0x02;
+    /**
+     * The BER type that is assigned to the universal bit string type.
+     */
+    public static final byte UNIVERSAL_BIT_STRING_TYPE = 0x03;
+
+    /**
+     * The BER type that is assigned to the universal octet string type.
+     */
+    public static final byte UNIVERSAL_OCTET_STRING_TYPE = 0x04;
 
     /**
      * The BER type that is assigned to the universal null type.
@@ -77,9 +87,9 @@
     public static final byte UNIVERSAL_NULL_TYPE = 0x05;
 
     /**
-     * The BER type that is assigned to the universal octet string type.
+     * The BER type that is assigned to the universal enumerated type.
      */
-    public static final byte UNIVERSAL_OCTET_STRING_TYPE = 0x04;
+    public static final byte UNIVERSAL_ENUMERATED_TYPE = 0x0A;
 
     /**
      * The BER type that is assigned to the universal sequence type.
@@ -139,12 +149,12 @@
      * The bitmask that can be ANDed with the BER type to determine if the
      * element is constructed.
      */
-    static final byte TYPE_MASK_CONSTRUCTED = 0x20;
+    public static final byte TYPE_MASK_CONSTRUCTED = 0x20;
     /**
      * The bitmask that can be ANDed with the BER type to determine if the
      * element is in the context-specific class.
      */
-    static final byte TYPE_MASK_CONTEXT = (byte) 0x80;
+    public static final byte TYPE_MASK_CONTEXT = (byte) 0x80;
     /**
      * The bitmask that can be ANDed with the BER type to determine if the
      * element is a primitive.
@@ -278,7 +288,22 @@
      * @return The new ASN.1 writer.
      */
     public static ASN1Writer getWriter(final ByteStringBuilder builder) {
-        return getWriter(builder.asOutputStream());
+        return getWriter(builder.asOutputStream(), DEFAULT_MAX_BUFFER_SIZE);
+    }
+
+    /**
+     * Returns an ASN.1 writer whose destination is the provided byte string
+     * builder.
+     *
+     * @param builder
+     *            The output stream to use.
+     * @param maxBufferSize
+     *          The threshold capacity beyond which internal cached buffers used
+     *          for encoding and decoding ASN1 will be trimmed after use.
+     * @return The new ASN.1 writer.
+     */
+    public static ASN1Writer getWriter(final ByteStringBuilder builder, final int maxBufferSize) {
+        return getWriter(builder.asOutputStream(), maxBufferSize);
     }
 
     /**
@@ -289,7 +314,21 @@
      * @return The new ASN.1 writer.
      */
     public static ASN1Writer getWriter(final OutputStream stream) {
-        return new ASN1OutputStreamWriter(stream);
+        return getWriter(stream, DEFAULT_MAX_BUFFER_SIZE);
+    }
+
+    /**
+     * Returns an ASN.1 writer whose destination is the provided output stream.
+     *
+     * @param stream
+     *            The output stream to use.
+     * @param maxBufferSize
+     *          The threshold capacity beyond which internal cached buffers used
+     *          for encoding and decoding ASN1 will be trimmed after use.
+     * @return The new ASN.1 writer.
+     */
+    public static ASN1Writer getWriter(final OutputStream stream, final int maxBufferSize) {
+        return new ASN1OutputStreamWriter(stream, maxBufferSize);
     }
 
     // Prevent instantiation.
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1ByteSequenceReader.java b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1ByteSequenceReader.java
index 6e6b8f4..3e6a3f9 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1ByteSequenceReader.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1ByteSequenceReader.java
@@ -174,6 +174,12 @@
         state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public void readEndExplicitTag() throws DecodeException, IOException {
+        readEndSequence();
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -333,6 +339,12 @@
         state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public void readStartExplicitTag() throws DecodeException, IOException {
+        readStartSequence();
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1InputStreamReader.java b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1InputStreamReader.java
index 7096711..77be834 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1InputStreamReader.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1InputStreamReader.java
@@ -206,6 +206,12 @@
         state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public void readEndExplicitTag() throws DecodeException, IOException {
+        readEndSequence();
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -429,6 +435,12 @@
         state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public void readStartExplicitTag() throws DecodeException, IOException {
+        readStartSequence();
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1OutputStreamWriter.java b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1OutputStreamWriter.java
index c09f906..506a7b0 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1OutputStreamWriter.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1OutputStreamWriter.java
@@ -42,21 +42,26 @@
 /**
  * An ASN1Writer implementation that outputs to an outputstream.
  */
-final class ASN1OutputStreamWriter extends AbstractASN1Writer implements ASN1Writer {
+final class ASN1OutputStreamWriter extends AbstractASN1Writer {
     private final OutputStream rootStream;
     private OutputStream out;
     private final ArrayList<ByteStringBuilder> streamStack;
     private int stackDepth;
+    private final int maxBufferSize;
 
     /**
      * Creates a new ASN.1 output stream reader.
      *
      * @param stream
      *            The underlying output stream.
+     * @param maxBufferSize
+     *          The threshold capacity beyond which internal cached buffers used
+     *          for encoding and decoding ASN1 will be trimmed after use.
      */
-    ASN1OutputStreamWriter(final OutputStream stream) {
+    ASN1OutputStreamWriter(final OutputStream stream, final int maxBufferSize) {
         this.out = stream;
         this.rootStream = stream;
+        this.maxBufferSize = maxBufferSize;
         this.streamStack = new ArrayList<ByteStringBuilder>();
         this.stackDepth = -1;
     }
@@ -64,6 +69,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void close() throws IOException {
         while (stackDepth >= 0) {
             writeEndSequence();
@@ -76,6 +82,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public void flush() throws IOException {
         rootStream.flush();
     }
@@ -83,6 +90,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeBoolean(final byte type, final boolean booleanValue) throws IOException {
         out.write(type);
         writeLength(1);
@@ -96,6 +104,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeEndSequence() throws IOException {
         if (stackDepth < 0) {
             final LocalizableMessage message = ERR_ASN1_SEQUENCE_WRITE_NOT_STARTED.get();
@@ -118,13 +127,19 @@
 
         IO_LOG.trace("WRITE ASN.1 END SEQUENCE(length={})", childStream.length());
 
-        childStream.clear();
+        if (childStream.capacity() > maxBufferSize) {
+            // garbage collect excessively large buffers
+            childStream.clear(maxBufferSize);
+        } else {
+            childStream.clear();
+        }
         return this;
     }
 
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeEndSet() throws IOException {
         return writeEndSequence();
     }
@@ -132,6 +147,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeEnumerated(final byte type, final int intValue) throws IOException {
         return writeInteger(type, intValue);
     }
@@ -139,6 +155,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeInteger(final byte type, final int intValue) throws IOException {
         out.write(type);
         if (((intValue < 0) && ((intValue & 0xFFFFFF80) == 0xFFFFFF80))
@@ -173,6 +190,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeInteger(final byte type, final long longValue) throws IOException {
         out.write(type);
         if (((longValue < 0) && ((longValue & 0xFFFFFFFFFFFFFF80L) == 0xFFFFFFFFFFFFFF80L))
@@ -249,6 +267,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeNull(final byte type) throws IOException {
         out.write(type);
         writeLength(0);
@@ -260,6 +279,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeOctetString(final byte type, final byte[] value, final int offset,
             final int length) throws IOException {
         out.write(type);
@@ -273,6 +293,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeOctetString(final byte type, final ByteSequence value)
             throws IOException {
         out.write(type);
@@ -287,6 +308,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeOctetString(final byte type, final String value) throws IOException {
         out.write(type);
 
@@ -307,6 +329,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeStartSequence(final byte type) throws IOException {
         // Write the type in current stream switch to next sub-stream
         out.write(type);
@@ -330,6 +353,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public ASN1Writer writeStartSet(final byte type) throws IOException {
         // From an implementation point of view, a set is equivalent to a
         // sequence.
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1Reader.java b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1Reader.java
index 7a762ac..e1af324 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1Reader.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1Reader.java
@@ -144,6 +144,19 @@
     void readEndSequence() throws DecodeException, IOException;
 
     /**
+     * Finishes reading an explicit tag and discards any unread elements.
+     *
+     * @throws DecodeException
+     *             If an error occurs while advancing to the end of the
+     *             explicit tag.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     * @throws IllegalStateException
+     *             If there is no explicit tag being read.
+     */
+    void readEndExplicitTag() throws DecodeException, IOException;
+
+    /**
      * Finishes reading a set and discards any unread elements.
      *
      * @throws DecodeException
@@ -325,6 +338,18 @@
     void readStartSequence() throws DecodeException, IOException;
 
     /**
+     * Reads the next element as an explicit tag having the Universal Sequence
+     * ASN.1 type tag. All further reads will read the elements in the explicit
+     * tag until {@link #readEndExplicitTag()} is called.
+     *
+     * @throws DecodeException
+     *             If the element cannot be decoded as an explicit tag.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    void readStartExplicitTag() throws DecodeException, IOException;
+
+    /**
      * Reads the next element as a sequence having the provided type tag. All
      * further reads will read the elements in the sequence until
      * {@link #readEndSequence()} is called.
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/io/ASN1OutputStreamWriterTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/io/ASN1OutputStreamWriterTestCase.java
index 9cd9399..df33e57 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/io/ASN1OutputStreamWriterTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/io/ASN1OutputStreamWriterTestCase.java
@@ -34,7 +34,7 @@
  */
 public class ASN1OutputStreamWriterTestCase extends ASN1WriterTestCase {
     private final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-    private final ASN1Writer writer = new ASN1OutputStreamWriter(outStream);
+    private final ASN1Writer writer = new ASN1OutputStreamWriter(outStream, 1);
 
     @Override
     protected byte[] getEncodedBytes() {
diff --git a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/ASN1BufferReader.java b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/ASN1BufferReader.java
index e80fc70..6248d07 100644
--- a/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/ASN1BufferReader.java
+++ b/opendj-grizzly/src/main/java/org/forgerock/opendj/grizzly/ASN1BufferReader.java
@@ -269,6 +269,12 @@
         state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public void readEndExplicitTag() throws DecodeException, IOException {
+        readEndSequence();
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -462,6 +468,12 @@
         state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
+    /** {@inheritDoc} */
+    @Override
+    public void readStartExplicitTag() throws DecodeException, IOException {
+        readStartSequence();
+    }
+
     /**
      * {@inheritDoc}
      */

--
Gitblit v1.10.0