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