From 23766aa898d550284dea2ed51a7828584c303b6c Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Tue, 26 Nov 2013 15:30:53 +0000
Subject: [PATCH] Minor code cleanup and improvements:

---
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/package-info.java                           |    3 
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java                           |    7 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java                             | 1007 ++--------
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PreReadResponseControl.java      |   21 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestImpl.java       |    5 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/MatchedValuesRequestControl.java |   10 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/AbstractLDAPMessageHandler.java             |  254 ++
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/CompareRequestTestCase.java      |    2 
 opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java               |  124 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java                                   |   12 
 opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java               |    2 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java                      |  594 ++---
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java              |   33 
 opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java               |  124 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java                 |    8 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/AbstractSASLBindRequest.java     |    5 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java                |   33 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java                 |   37 
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/GenericBindRequestTestCase.java  |    8 
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/NameFormTestCase.java              |   93 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java                         |    8 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateTag.java                          |    1 
 opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java                   |    7 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java               |   33 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/AssertionRequestControl.java     |   14 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java                       |   34 
 opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java               |   98 
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/MockConnectionEventListener.java          |    3 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java                 |  244 +
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java                  |   31 
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/BindRequestTestCase.java         |    4 
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSchemaElementTestCase.java |   22 
 opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java    |    3 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java                   |   39 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java                        |   35 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPMessageHandler.java                     |  399 ++++
 opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm                                                |    1 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java                             | 1167 +++++------
 opendj3/pom.xml                                                                                       |    1 
 opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java          |    8 
 /dev/null                                                                                             |   88 
 opendj3/opendj-ldap-toolkit/src/main/assembly/bat/makeldif.bat                                        |   32 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAP.java                                   |  843 ++++++++
 opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPBaseFilter.java                 |    2 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java                                 |   21 
 opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java               |  145 
 opendj3/opendj-ldap-toolkit/src/main/assembly/bin/makeldif                                            |   38 
 opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PostReadResponseControl.java     |   21 
 48 files changed, 3,146 insertions(+), 2,578 deletions(-)

diff --git a/opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPConstants.java b/opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPConstants.java
deleted file mode 100644
index eb2f82b..0000000
--- a/opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPConstants.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2006-2008 Sun Microsystems, Inc.
- */
-package com.forgerock.opendj.ldap;
-
-/**
- * This class defines a number of constants used in the LDAP protocol.
- */
-public final class LDAPConstants {
-
-    /**
-     * The protocol op type for bind requests.
-     */
-    public static final byte OP_TYPE_BIND_REQUEST = 0x60;
-
-    /**
-     * The protocol op type for bind responses.
-     */
-    public static final byte OP_TYPE_BIND_RESPONSE = 0x61;
-
-    /**
-     * The protocol op type for unbind requests.
-     */
-    public static final byte OP_TYPE_UNBIND_REQUEST = 0x42;
-
-    /**
-     * The protocol op type for search requests.
-     */
-    public static final byte OP_TYPE_SEARCH_REQUEST = 0x63;
-
-    /**
-     * The protocol op type for search result entries.
-     */
-    public static final byte OP_TYPE_SEARCH_RESULT_ENTRY = 0x64;
-
-    /**
-     * The protocol op type for search result references.
-     */
-    public static final byte OP_TYPE_SEARCH_RESULT_REFERENCE = 0x73;
-
-    /**
-     * The protocol op type for search result done elements.
-     */
-    public static final byte OP_TYPE_SEARCH_RESULT_DONE = 0x65;
-
-    /**
-     * The protocol op type for modify requests.
-     */
-    public static final byte OP_TYPE_MODIFY_REQUEST = 0x66;
-
-    /**
-     * The protocol op type for modify responses.
-     */
-    public static final byte OP_TYPE_MODIFY_RESPONSE = 0x67;
-
-    /**
-     * The protocol op type for add requests.
-     */
-    public static final byte OP_TYPE_ADD_REQUEST = 0x68;
-
-    /**
-     * The protocol op type for add responses.
-     */
-    public static final byte OP_TYPE_ADD_RESPONSE = 0x69;
-
-    /**
-     * The protocol op type for delete requests.
-     */
-    public static final byte OP_TYPE_DELETE_REQUEST = 0x4A;
-
-    /**
-     * The protocol op type for delete responses.
-     */
-    public static final byte OP_TYPE_DELETE_RESPONSE = 0x6B;
-
-    /**
-     * The protocol op type for modify DN requests.
-     */
-    public static final byte OP_TYPE_MODIFY_DN_REQUEST = 0x6C;
-
-    /**
-     * The protocol op type for modify DN responses.
-     */
-    public static final byte OP_TYPE_MODIFY_DN_RESPONSE = 0x6D;
-
-    /**
-     * The protocol op type for compare requests.
-     */
-    public static final byte OP_TYPE_COMPARE_REQUEST = 0x6E;
-
-    /**
-     * The protocol op type for compare responses.
-     */
-    public static final byte OP_TYPE_COMPARE_RESPONSE = 0x6F;
-
-    /**
-     * The protocol op type for abandon requests.
-     */
-    public static final byte OP_TYPE_ABANDON_REQUEST = 0x50;
-
-    /**
-     * The protocol op type for extended requests.
-     */
-    public static final byte OP_TYPE_EXTENDED_REQUEST = 0x77;
-
-    /**
-     * The protocol op type for extended responses.
-     */
-    public static final byte OP_TYPE_EXTENDED_RESPONSE = 0x78;
-
-    /**
-     * The protocol op type for intermediate responses.
-     */
-    public static final byte OP_TYPE_INTERMEDIATE_RESPONSE = 0x79;
-
-    /**
-     * The BER type to use for encoding the sequence of controls in an LDAP
-     * message.
-     */
-    public static final byte TYPE_CONTROL_SEQUENCE = (byte) 0xA0;
-
-    /**
-     * The BER type to use for encoding the sequence of referral URLs in an
-     * LDAPResult element.
-     */
-    public static final byte TYPE_REFERRAL_SEQUENCE = (byte) 0xA3;
-
-    /**
-     * The BER type to use for the AuthenticationChoice element in a bind
-     * request when simple authentication is to be used.
-     */
-    public static final byte TYPE_AUTHENTICATION_SIMPLE = (byte) 0x80;
-
-    /**
-     * The BER type to use for the AuthenticationChoice element in a bind
-     * request when SASL authentication is to be used.
-     */
-    public static final byte TYPE_AUTHENTICATION_SASL = (byte) 0xA3;
-
-    /**
-     * The BER type to use for the server SASL credentials in a bind response.
-     */
-    public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87;
-
-    /**
-     * The BER type to use for AND filter components.
-     */
-    public static final byte TYPE_FILTER_AND = (byte) 0xA0;
-
-    /**
-     * The BER type to use for OR filter components.
-     */
-    public static final byte TYPE_FILTER_OR = (byte) 0xA1;
-
-    /**
-     * The BER type to use for NOT filter components.
-     */
-    public static final byte TYPE_FILTER_NOT = (byte) 0xA2;
-
-    /**
-     * The BER type to use for equality filter components.
-     */
-    public static final byte TYPE_FILTER_EQUALITY = (byte) 0xA3;
-
-    /**
-     * The BER type to use for substring filter components.
-     */
-    public static final byte TYPE_FILTER_SUBSTRING = (byte) 0xA4;
-
-    /**
-     * The BER type to use for greater than or equal to filter components.
-     */
-    public static final byte TYPE_FILTER_GREATER_OR_EQUAL = (byte) 0xA5;
-
-    /**
-     * The BER type to use for less than or equal to filter components.
-     */
-    public static final byte TYPE_FILTER_LESS_OR_EQUAL = (byte) 0xA6;
-
-    /**
-     * The BER type to use for presence filter components.
-     */
-    public static final byte TYPE_FILTER_PRESENCE = (byte) 0x87;
-
-    /**
-     * The BER type to use for approximate filter components.
-     */
-    public static final byte TYPE_FILTER_APPROXIMATE = (byte) 0xA8;
-
-    /**
-     * The BER type to use for extensible matching filter components.
-     */
-    public static final byte TYPE_FILTER_EXTENSIBLE_MATCH = (byte) 0xA9;
-
-    /**
-     * The BER type to use for the subInitial component of a substring filter.
-     */
-    public static final byte TYPE_SUBINITIAL = (byte) 0x80;
-
-    /**
-     * The BER type to use for the subAny component(s) of a substring filter.
-     */
-    public static final byte TYPE_SUBANY = (byte) 0x81;
-
-    /**
-     * The BER type to use for the subFinal components of a substring filter.
-     */
-    public static final byte TYPE_SUBFINAL = (byte) 0x82;
-
-    /**
-     * The BER type to use for the matching rule OID in a matching rule
-     * assertion.
-     */
-    public static final byte TYPE_MATCHING_RULE_ID = (byte) 0x81;
-
-    /**
-     * The BER type to use for the attribute type in a matching rule assertion.
-     */
-    public static final byte TYPE_MATCHING_RULE_TYPE = (byte) 0x82;
-
-    /**
-     * The BER type to use for the assertion value in a matching rule assertion.
-     */
-    public static final byte TYPE_MATCHING_RULE_VALUE = (byte) 0x83;
-
-    /**
-     * The BER type to use for the DN attributes flag in a matching rule
-     * assertion.
-     */
-    public static final byte TYPE_MATCHING_RULE_DN_ATTRIBUTES = (byte) 0x84;
-
-    /**
-     * The BER type to use for the newSuperior component of a modify DN request.
-     */
-    public static final byte TYPE_MODIFY_DN_NEW_SUPERIOR = (byte) 0x80;
-
-    /**
-     * The BER type to use for the OID of an extended request.
-     */
-    public static final byte TYPE_EXTENDED_REQUEST_OID = (byte) 0x80;
-
-    /**
-     * The BER type to use for the value of an extended request.
-     */
-    public static final byte TYPE_EXTENDED_REQUEST_VALUE = (byte) 0x81;
-
-    /**
-     * The BER type to use for the OID of an extended response.
-     */
-    public static final byte TYPE_EXTENDED_RESPONSE_OID = (byte) 0x8A;
-
-    /**
-     * The BER type to use for the value of an extended response.
-     */
-    public static final byte TYPE_EXTENDED_RESPONSE_VALUE = (byte) 0x8B;
-
-    /**
-     * The BER type to use for the OID of an intermediate response message.
-     */
-    public static final byte TYPE_INTERMEDIATE_RESPONSE_OID = (byte) 0x80;
-
-    /**
-     * The BER type to use for the value of an intermediate response message.
-     */
-    public static final byte TYPE_INTERMEDIATE_RESPONSE_VALUE = (byte) 0x81;
-
-    /**
-     * The OID for the Kerberos V GSSAPI mechanism.
-     */
-    public static final String OID_GSSAPI_KERBEROS_V = "1.2.840.113554.1.2.2";
-
-    /**
-     * The OID for the LDAP notice of disconnection extended operation.
-     */
-    public static final String OID_NOTICE_OF_DISCONNECTION = "1.3.6.1.4.1.1466.20036";
-
-    /**
-     * The ASN.1 element decoding state that indicates that the next byte read
-     * should be the BER type for a new element.
-     */
-    public static final int ELEMENT_READ_STATE_NEED_TYPE = 0;
-
-    /**
-     * The ASN.1 element decoding state that indicates that the next byte read
-     * should be the first byte for the element length.
-     */
-    public static final int ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE = 1;
-
-    /**
-     * The ASN.1 element decoding state that indicates that the next byte read
-     * should be additional bytes of a multi-byte length.
-     */
-    public static final int ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES = 2;
-
-    /**
-     * The ASN.1 element decoding state that indicates that the next byte read
-     * should be applied to the value of the element.
-     */
-    public static final int ELEMENT_READ_STATE_NEED_VALUE_BYTES = 3;
-
-    private LDAPConstants() {
-        // Prevent instantiation.
-    }
-}
diff --git a/opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPUtils.java b/opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPUtils.java
deleted file mode 100644
index a8f5e68..0000000
--- a/opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPUtils.java
+++ /dev/null
@@ -1,622 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2013 ForgeRock AS.
- */
-
-package com.forgerock.opendj.ldap;
-
-import static com.forgerock.opendj.ldap.LDAPConstants.*;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
-import org.forgerock.i18n.LocalizedIllegalArgumentException;
-import org.forgerock.opendj.io.ASN1Reader;
-import org.forgerock.opendj.io.ASN1Writer;
-import org.forgerock.opendj.ldap.Attribute;
-import org.forgerock.opendj.ldap.AttributeDescription;
-import org.forgerock.opendj.ldap.ByteSequence;
-import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.DN;
-import org.forgerock.opendj.ldap.DecodeException;
-import org.forgerock.opendj.ldap.DecodeOptions;
-import org.forgerock.opendj.ldap.Entry;
-import org.forgerock.opendj.ldap.Filter;
-import org.forgerock.opendj.ldap.FilterVisitor;
-import org.forgerock.opendj.ldap.responses.Responses;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
-import org.forgerock.opendj.ldap.schema.Schema;
-
-/**
- * Common LDAP utility methods which may be used when implementing new controls
- * and extension.
- */
-public final class LDAPUtils {
-
-    private static final FilterVisitor<IOException, ASN1Writer> ASN1_ENCODER =
-            new FilterVisitor<IOException, ASN1Writer>() {
-
-                public IOException visitAndFilter(final ASN1Writer writer,
-                        final List<Filter> subFilters) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_AND);
-                        for (final Filter subFilter : subFilters) {
-                            final IOException e = subFilter.accept(this, writer);
-                            if (e != null) {
-                                return e;
-                            }
-                        }
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitApproxMatchFilter(final ASN1Writer writer,
-                        final String attributeDescription, final ByteString assertionValue) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_APPROXIMATE);
-                        writer.writeOctetString(attributeDescription);
-                        writer.writeOctetString(assertionValue);
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitEqualityMatchFilter(final ASN1Writer writer,
-                        final String attributeDescription, final ByteString assertionValue) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_EQUALITY);
-                        writer.writeOctetString(attributeDescription);
-                        writer.writeOctetString(assertionValue);
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitExtensibleMatchFilter(final ASN1Writer writer,
-                        final String matchingRule, final String attributeDescription,
-                        final ByteString assertionValue, final boolean dnAttributes) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH);
-
-                        if (matchingRule != null) {
-                            writer.writeOctetString(TYPE_MATCHING_RULE_ID, matchingRule);
-                        }
-
-                        if (attributeDescription != null) {
-                            writer.writeOctetString(TYPE_MATCHING_RULE_TYPE, attributeDescription);
-                        }
-
-                        writer.writeOctetString(TYPE_MATCHING_RULE_VALUE, assertionValue);
-
-                        if (dnAttributes) {
-                            writer.writeBoolean(TYPE_MATCHING_RULE_DN_ATTRIBUTES, true);
-                        }
-
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitGreaterOrEqualFilter(final ASN1Writer writer,
-                        final String attributeDescription, final ByteString assertionValue) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_GREATER_OR_EQUAL);
-                        writer.writeOctetString(attributeDescription);
-                        writer.writeOctetString(assertionValue);
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitLessOrEqualFilter(final ASN1Writer writer,
-                        final String attributeDescription, final ByteString assertionValue) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_LESS_OR_EQUAL);
-                        writer.writeOctetString(attributeDescription);
-                        writer.writeOctetString(assertionValue);
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitNotFilter(final ASN1Writer writer, final Filter subFilter) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_NOT);
-                        final IOException e = subFilter.accept(this, writer);
-                        if (e != null) {
-                            return e;
-                        }
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitOrFilter(final ASN1Writer writer,
-                        final List<Filter> subFilters) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_OR);
-                        for (final Filter subFilter : subFilters) {
-                            final IOException e = subFilter.accept(this, writer);
-                            if (e != null) {
-                                return e;
-                            }
-                        }
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitPresentFilter(final ASN1Writer writer,
-                        final String attributeDescription) {
-                    try {
-                        writer.writeOctetString(TYPE_FILTER_PRESENCE, attributeDescription);
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitSubstringsFilter(final ASN1Writer writer,
-                        final String attributeDescription, final ByteString initialSubstring,
-                        final List<ByteString> anySubstrings, final ByteString finalSubstring) {
-                    try {
-                        writer.writeStartSequence(TYPE_FILTER_SUBSTRING);
-                        writer.writeOctetString(attributeDescription);
-
-                        writer.writeStartSequence();
-                        if (initialSubstring != null) {
-                            writer.writeOctetString(TYPE_SUBINITIAL, initialSubstring);
-                        }
-
-                        for (final ByteSequence anySubstring : anySubstrings) {
-                            writer.writeOctetString(TYPE_SUBANY, anySubstring);
-                        }
-
-                        if (finalSubstring != null) {
-                            writer.writeOctetString(TYPE_SUBFINAL, finalSubstring);
-                        }
-                        writer.writeEndSequence();
-
-                        writer.writeEndSequence();
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-
-                public IOException visitUnrecognizedFilter(final ASN1Writer writer,
-                        final byte filterTag, final ByteString filterBytes) {
-                    try {
-                        writer.writeOctetString(filterTag, filterBytes);
-                        return null;
-                    } catch (final IOException e) {
-                        return e;
-                    }
-                }
-            };
-
-    /**
-     * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a
-     * {@code Filter}.
-     *
-     * @param reader
-     *            The {@code ASN1Reader} from which the ASN.1 encoded
-     *            {@code Filter} should be read.
-     * @return The decoded {@code Filter}.
-     * @throws IOException
-     *             If an error occurs while reading from {@code reader}.
-     */
-    public static Filter decodeFilter(final ASN1Reader reader) throws IOException {
-        final byte type = reader.peekType();
-
-        switch (type) {
-        case TYPE_FILTER_AND:
-            return decodeAndFilter(reader);
-
-        case TYPE_FILTER_OR:
-            return decodeOrFilter(reader);
-
-        case TYPE_FILTER_NOT:
-            return decodeNotFilter(reader);
-
-        case TYPE_FILTER_EQUALITY:
-            return decodeEqualityMatchFilter(reader);
-
-        case TYPE_FILTER_GREATER_OR_EQUAL:
-            return decodeGreaterOrEqualMatchFilter(reader);
-
-        case TYPE_FILTER_LESS_OR_EQUAL:
-            return decodeLessOrEqualMatchFilter(reader);
-
-        case TYPE_FILTER_APPROXIMATE:
-            return decodeApproxMatchFilter(reader);
-
-        case TYPE_FILTER_SUBSTRING:
-            return decodeSubstringsFilter(reader);
-
-        case TYPE_FILTER_PRESENCE:
-            return Filter.present(reader.readOctetStringAsString(type));
-
-        case TYPE_FILTER_EXTENSIBLE_MATCH:
-            return decodeExtensibleMatchFilter(reader);
-
-        default:
-            return Filter.unrecognized(type, reader.readOctetString(type));
-        }
-    }
-
-    /**
-     * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a
-     * {@code SearchResultEntry}.
-     *
-     * @param reader
-     *            The {@code ASN1Reader} from which the ASN.1 encoded
-     *            {@code SearchResultEntry} should be read.
-     * @param options
-     *            The decode options to use when decoding the entry.
-     * @return The decoded {@code SearchResultEntry}.
-     * @throws IOException
-     *             If an error occurs while reading from {@code reader}.
-     */
-    public static SearchResultEntry decodeSearchResultEntry(final ASN1Reader reader,
-            final DecodeOptions options) throws IOException {
-        Entry entry;
-
-        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
-        try {
-            final String dnString = reader.readOctetStringAsString();
-            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
-            DN dn;
-            try {
-                dn = DN.valueOf(dnString, schema);
-            } catch (final LocalizedIllegalArgumentException e) {
-                throw DecodeException.error(e.getMessageObject());
-            }
-
-            entry = options.getEntryFactory().newEntry(dn);
-            reader.readStartSequence();
-            try {
-                while (reader.hasNextElement()) {
-                    reader.readStartSequence();
-                    try {
-                        final String ads = reader.readOctetStringAsString();
-                        AttributeDescription ad;
-                        try {
-                            ad = AttributeDescription.valueOf(ads, schema);
-                        } catch (final LocalizedIllegalArgumentException e) {
-                            throw DecodeException.error(e.getMessageObject());
-                        }
-
-                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
-
-                        reader.readStartSet();
-                        try {
-                            while (reader.hasNextElement()) {
-                                attribute.add(reader.readOctetString());
-                            }
-                            entry.addAttribute(attribute);
-                        } finally {
-                            reader.readEndSet();
-                        }
-                    } finally {
-                        reader.readEndSequence();
-                    }
-                }
-            } finally {
-                reader.readEndSequence();
-            }
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return Responses.newSearchResultEntry(entry);
-    }
-
-    /**
-     * Writes the ASN.1 encoding of the provided {@code Filter} to the provided
-     * {@code ASN1Writer}.
-     *
-     * @param writer
-     *            The {@code ASN1Writer} to which the ASN.1 encoding of the
-     *            provided {@code Filter} should be written.
-     * @param filter
-     *            The filter to be encoded.
-     * @return The updated {@code ASN1Writer}.
-     * @throws IOException
-     *             If an error occurs while writing to {@code writer}.
-     */
-    public static ASN1Writer encodeFilter(final ASN1Writer writer, final Filter filter)
-            throws IOException {
-        final IOException e = filter.accept(ASN1_ENCODER, writer);
-        if (e != null) {
-            throw e;
-        } else {
-            return writer;
-        }
-    }
-
-    /**
-     * Writes the ASN.1 encoding of the provided {@code SearchResultEntry} to
-     * the provided {@code ASN1Writer}.
-     *
-     * @param writer
-     *            The {@code ASN1Writer} to which the ASN.1 encoding of the
-     *            provided {@code SearchResultEntry} should be written.
-     * @param entry
-     *            The Search Result Entry to be encoded.
-     * @return The updated {@code ASN1Writer}.
-     * @throws IOException
-     *             If an error occurs while writing to {@code writer}.
-     */
-    public static ASN1Writer encodeSearchResultEntry(final ASN1Writer writer,
-            final SearchResultEntry entry) throws IOException {
-        // FIXME: this should include Controls.
-        writer.writeStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
-        writer.writeOctetString(entry.getName().toString());
-
-        writer.writeStartSequence();
-        for (final Attribute attr : entry.getAllAttributes()) {
-            encodeAttribute(writer, attr);
-        }
-        writer.writeEndSequence();
-        writer.writeEndSequence();
-        return writer;
-    }
-
-    private static void encodeAttribute(final ASN1Writer writer, final Attribute attribute)
-            throws IOException {
-        writer.writeStartSequence();
-        writer.writeOctetString(attribute.getAttributeDescriptionAsString());
-
-        writer.writeStartSet();
-        for (final ByteString value : attribute) {
-            writer.writeOctetString(value);
-        }
-        writer.writeEndSequence();
-
-        writer.writeEndSequence();
-    }
-
-    // Decodes an and filter.
-    private static Filter decodeAndFilter(final ASN1Reader reader) throws IOException {
-        Filter filter;
-
-        reader.readStartSequence(TYPE_FILTER_AND);
-        try {
-            if (reader.hasNextElement()) {
-                final List<Filter> subFilters = new LinkedList<Filter>();
-                do {
-                    subFilters.add(decodeFilter(reader));
-                } while (reader.hasNextElement());
-                filter = Filter.and(subFilters);
-            } else {
-                // No sub-filters - this is an RFC 4526 absolute true filter.
-                filter = Filter.alwaysTrue();
-            }
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return filter;
-    }
-
-    // Decodes an approximate match filter.
-    private static Filter decodeApproxMatchFilter(final ASN1Reader reader) throws IOException {
-        String attributeDescription;
-        ByteString assertionValue;
-
-        reader.readStartSequence(TYPE_FILTER_APPROXIMATE);
-        try {
-            attributeDescription = reader.readOctetStringAsString();
-            assertionValue = reader.readOctetString();
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return Filter.approx(attributeDescription, assertionValue);
-    }
-
-    // Decodes an equality match filter.
-    private static Filter decodeEqualityMatchFilter(final ASN1Reader reader) throws IOException {
-        String attributeDescription;
-        ByteString assertionValue;
-
-        reader.readStartSequence(TYPE_FILTER_EQUALITY);
-        try {
-            attributeDescription = reader.readOctetStringAsString();
-            assertionValue = reader.readOctetString();
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return Filter.equality(attributeDescription, assertionValue);
-    }
-
-    // Decodes an extensible match filter.
-    private static Filter decodeExtensibleMatchFilter(final ASN1Reader reader) throws IOException {
-        String matchingRule;
-        String attributeDescription;
-        boolean dnAttributes;
-        ByteString assertionValue;
-
-        reader.readStartSequence(TYPE_FILTER_EXTENSIBLE_MATCH);
-        try {
-            matchingRule = null;
-            if (reader.peekType() == TYPE_MATCHING_RULE_ID) {
-                matchingRule = reader.readOctetStringAsString(TYPE_MATCHING_RULE_ID);
-            }
-            attributeDescription = null;
-            if (reader.peekType() == TYPE_MATCHING_RULE_TYPE) {
-                attributeDescription = reader.readOctetStringAsString(TYPE_MATCHING_RULE_TYPE);
-            }
-            dnAttributes = false;
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_MATCHING_RULE_DN_ATTRIBUTES)) {
-                dnAttributes = reader.readBoolean();
-            }
-            assertionValue = reader.readOctetString(TYPE_MATCHING_RULE_VALUE);
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return Filter.extensible(matchingRule, attributeDescription, assertionValue,
-                dnAttributes);
-    }
-
-    // Decodes a greater than or equal filter.
-    private static Filter decodeGreaterOrEqualMatchFilter(final ASN1Reader reader)
-            throws IOException {
-        String attributeDescription;
-        ByteString assertionValue;
-
-        reader.readStartSequence(TYPE_FILTER_GREATER_OR_EQUAL);
-        try {
-            attributeDescription = reader.readOctetStringAsString();
-            assertionValue = reader.readOctetString();
-        } finally {
-            reader.readEndSequence();
-        }
-        return Filter.greaterOrEqual(attributeDescription, assertionValue);
-    }
-
-    // Decodes a less than or equal filter.
-    private static Filter decodeLessOrEqualMatchFilter(final ASN1Reader reader) throws IOException {
-        String attributeDescription;
-        ByteString assertionValue;
-
-        reader.readStartSequence(TYPE_FILTER_LESS_OR_EQUAL);
-        try {
-            attributeDescription = reader.readOctetStringAsString();
-            assertionValue = reader.readOctetString();
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return Filter.lessOrEqual(attributeDescription, assertionValue);
-    }
-
-    // Decodes a not filter.
-    private static Filter decodeNotFilter(final ASN1Reader reader) throws IOException {
-        Filter subFilter;
-
-        reader.readStartSequence(TYPE_FILTER_NOT);
-        try {
-            subFilter = decodeFilter(reader);
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return Filter.not(subFilter);
-    }
-
-    // Decodes an or filter.
-    private static Filter decodeOrFilter(final ASN1Reader reader) throws IOException {
-        Filter filter;
-
-        reader.readStartSequence(TYPE_FILTER_OR);
-        try {
-            if (reader.hasNextElement()) {
-                final List<Filter> subFilters = new LinkedList<Filter>();
-                do {
-                    subFilters.add(decodeFilter(reader));
-                } while (reader.hasNextElement());
-                filter = Filter.or(subFilters);
-            } else {
-                // No sub-filters - this is an RFC 4526 absolute false filter.
-                filter = Filter.alwaysFalse();
-            }
-        } finally {
-            reader.readEndSequence();
-        }
-
-        return filter;
-    }
-
-    // Decodes a sub-strings filter.
-    private static Filter decodeSubstringsFilter(final ASN1Reader reader) throws IOException {
-        ByteString initialSubstring = null;
-        List<ByteString> anySubstrings = null;
-        ByteString finalSubstring = null;
-        String attributeDescription;
-
-        reader.readStartSequence(TYPE_FILTER_SUBSTRING);
-        try {
-            attributeDescription = reader.readOctetStringAsString();
-            reader.readStartSequence();
-            try {
-                // FIXME: There should be at least one element in this substring
-                // filter sequence.
-                if (reader.peekType() == TYPE_SUBINITIAL) {
-                    initialSubstring = reader.readOctetString(TYPE_SUBINITIAL);
-                }
-                if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY)) {
-                    anySubstrings = new LinkedList<ByteString>();
-                    do {
-                        anySubstrings.add(reader.readOctetString(TYPE_SUBANY));
-                    } while (reader.hasNextElement() && (reader.peekType() == TYPE_SUBANY));
-                }
-                if (reader.hasNextElement() && (reader.peekType() == TYPE_SUBFINAL)) {
-                    finalSubstring = reader.readOctetString(TYPE_SUBFINAL);
-                }
-            } finally {
-                reader.readEndSequence();
-            }
-        } finally {
-            reader.readEndSequence();
-        }
-
-        if (anySubstrings == null) {
-            anySubstrings = Collections.emptyList();
-        }
-
-        return Filter.substrings(attributeDescription, initialSubstring, anySubstrings,
-                finalSubstring);
-    }
-
-    /**
-     * Prevent instantiation.
-     */
-    private LDAPUtils() {
-        // Nothing to do.
-    }
-}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java
index b74cebf..db4b585 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java
@@ -97,27 +97,31 @@
      * The ASN.1 element decoding state that indicates that the next byte read
      * should be additional bytes of a multi-byte length.
      */
-    static final int ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES = 2;
+    public static final int ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES = 2;
 
     /**
      * The ASN.1 element decoding state that indicates that the next byte read
      * should be the first byte for the element length.
      */
-    static final int ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE = 1;
+    public static final int ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE = 1;
+
     /**
      * The ASN.1 element decoding state that indicates that the next byte read
      * should be the BER type for a new element.
      */
-    static final int ELEMENT_READ_STATE_NEED_TYPE = 0;
+    public static final int ELEMENT_READ_STATE_NEED_TYPE = 0;
+
     /**
      * The ASN.1 element decoding state that indicates that the next byte read
      * should be applied to the value of the element.
      */
-    static final int ELEMENT_READ_STATE_NEED_VALUE_BYTES = 3;
+    public static final int ELEMENT_READ_STATE_NEED_VALUE_BYTES = 3;
+
     /**
      * The byte array that will be used for ASN.1 elements with no value.
      */
     static final byte[] NO_VALUE = new byte[0];
+
     /**
      * The bitmask that can be ANDed with the BER type to zero out all bits
      * except those used in the class.
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/AbstractLDAPMessageHandler.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/AbstractLDAPMessageHandler.java
new file mode 100644
index 0000000..ac24335
--- /dev/null
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/AbstractLDAPMessageHandler.java
@@ -0,0 +1,254 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
+ */
+
+package org.forgerock.opendj.io;
+
+import java.io.IOException;
+
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.DecodeException;
+import org.forgerock.opendj.ldap.requests.AbandonRequest;
+import org.forgerock.opendj.ldap.requests.AddRequest;
+import org.forgerock.opendj.ldap.requests.CompareRequest;
+import org.forgerock.opendj.ldap.requests.DeleteRequest;
+import org.forgerock.opendj.ldap.requests.ExtendedRequest;
+import org.forgerock.opendj.ldap.requests.GenericBindRequest;
+import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
+import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.requests.Request;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.opendj.ldap.requests.UnbindRequest;
+import org.forgerock.opendj.ldap.responses.BindResult;
+import org.forgerock.opendj.ldap.responses.CompareResult;
+import org.forgerock.opendj.ldap.responses.ExtendedResult;
+import org.forgerock.opendj.ldap.responses.IntermediateResponse;
+import org.forgerock.opendj.ldap.responses.Response;
+import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.opendj.ldap.responses.SearchResultReference;
+
+/**
+ * This class provides a skeletal implementation of the
+ * {@link LDAPMessageHandler} interface, in order to minimize the effort
+ * required to implement this interface. By default each method throws a fatal
+ * {@link DecodeException}.
+ */
+public abstract class AbstractLDAPMessageHandler implements LDAPMessageHandler {
+    /**
+     * Default constructor.
+     */
+    protected AbstractLDAPMessageHandler() {
+        // No implementation required.
+    }
+
+    @Override
+    public void abandonRequest(final int messageID, final AbandonRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void addRequest(final int messageID, final AddRequest request) throws DecodeException,
+            IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void addResult(final int messageID, final Result result) throws DecodeException,
+            IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public void bindRequest(final int messageID, final int version, final GenericBindRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void bindResult(final int messageID, final BindResult result) throws DecodeException,
+            IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public void compareRequest(final int messageID, final CompareRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void compareResult(final int messageID, final CompareResult result)
+            throws DecodeException, IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public void deleteRequest(final int messageID, final DeleteRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void deleteResult(final int messageID, final Result result) throws DecodeException,
+            IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public <R extends ExtendedResult> void extendedRequest(final int messageID,
+            final ExtendedRequest<R> request) throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void extendedResult(final int messageID, final ExtendedResult result)
+            throws DecodeException, IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public void intermediateResponse(final int messageID, final IntermediateResponse response)
+            throws DecodeException, IOException {
+        throw newUnexpectedResponseException(messageID, response);
+    }
+
+    @Override
+    public void modifyDNRequest(final int messageID, final ModifyDNRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void modifyDNResult(final int messageID, final Result result) throws DecodeException,
+            IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public void modifyRequest(final int messageID, final ModifyRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void modifyResult(final int messageID, final Result result) throws DecodeException,
+            IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public void searchRequest(final int messageID, final SearchRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void searchResult(final int messageID, final Result result) throws DecodeException,
+            IOException {
+        throw newUnexpectedResponseException(messageID, result);
+    }
+
+    @Override
+    public void searchResultEntry(final int messageID, final SearchResultEntry entry)
+            throws DecodeException, IOException {
+        throw newUnexpectedResponseException(messageID, entry);
+    }
+
+    @Override
+    public void searchResultReference(final int messageID, final SearchResultReference reference)
+            throws DecodeException, IOException {
+        throw newUnexpectedResponseException(messageID, reference);
+    }
+
+    @Override
+    public void unbindRequest(final int messageID, final UnbindRequest request)
+            throws DecodeException, IOException {
+        throw newUnexpectedRequestException(messageID, request);
+    }
+
+    @Override
+    public void unrecognizedMessage(final int messageID, final byte messageTag,
+            final ByteString messageBytes) throws DecodeException, IOException {
+        throw newUnsupportedMessageException(messageID, messageTag, messageBytes);
+    }
+
+    /**
+     * Returns a decoding exception suitable for use when an unsupported LDAP
+     * message is received.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param messageTag
+     *            The LDAP message type.
+     * @param messageBytes
+     *            The LDAP message content.
+     * @return A decoding exception suitable for use when an unsupported LDAP
+     *         message is received.
+     */
+    protected DecodeException newUnsupportedMessageException(final int messageID,
+            final byte messageTag, final ByteString messageBytes) {
+        return DecodeException.fatalError(LocalizableMessage.raw(
+                "Unsupported LDAP message: id=%d, tag=%d, content=%s", messageID, messageTag,
+                messageBytes));
+    }
+
+    /**
+     * Returns a decoding exception suitable for use when an unexpected LDAP
+     * request is received.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The LDAP request.
+     * @return A decoding exception suitable for use when an unexpected LDAP
+     *         request is received.
+     */
+    protected DecodeException newUnexpectedRequestException(final int messageID,
+            final Request request) {
+        return DecodeException.fatalError(LocalizableMessage.raw(
+                "Unexpected LDAP request: id=%d, message=%s", messageID, request));
+    }
+
+    /**
+     * Returns a decoding exception suitable for use when an unexpected LDAP
+     * response is received.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param response
+     *            The LDAP response.
+     * @return A decoding exception suitable for use when an unexpected LDAP
+     *         response is received.
+     */
+    protected DecodeException newUnexpectedResponseException(final int messageID,
+            final Response response) {
+        return DecodeException.fatalError(LocalizableMessage.raw(
+                "Unexpected LDAP response: id=%d, message=%s", messageID, response));
+    }
+}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAP.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAP.java
new file mode 100644
index 0000000..452c167
--- /dev/null
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAP.java
@@ -0,0 +1,843 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2013 ForgeRock AS.
+ */
+
+package org.forgerock.opendj.io;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.forgerock.i18n.LocalizedIllegalArgumentException;
+import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.AttributeDescription;
+import org.forgerock.opendj.ldap.ByteSequence;
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.DecodeException;
+import org.forgerock.opendj.ldap.DecodeOptions;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
+import org.forgerock.opendj.ldap.FilterVisitor;
+import org.forgerock.opendj.ldap.schema.Schema;
+
+/**
+ * This class contains various static utility methods encoding and decoding LDAP
+ * protocol elements.
+ *
+ * @see LDAPReader
+ * @see LDAPWriter
+ */
+public final class LDAP {
+    // @Checkstyle:ignore AvoidNestedBlocks
+
+    /**
+     * The OID for the Kerberos V GSSAPI mechanism.
+     */
+    public static final String OID_GSSAPI_KERBEROS_V = "1.2.840.113554.1.2.2";
+
+    /**
+     * The OID for the LDAP notice of disconnection extended operation.
+     */
+    public static final String OID_NOTICE_OF_DISCONNECTION = "1.3.6.1.4.1.1466.20036";
+
+    /**
+     * The protocol op type for abandon requests.
+     */
+    public static final byte OP_TYPE_ABANDON_REQUEST = 0x50;
+
+    /**
+     * The protocol op type for add requests.
+     */
+    public static final byte OP_TYPE_ADD_REQUEST = 0x68;
+
+    /**
+     * The protocol op type for add responses.
+     */
+    public static final byte OP_TYPE_ADD_RESPONSE = 0x69;
+
+    /**
+     * The protocol op type for bind requests.
+     */
+    public static final byte OP_TYPE_BIND_REQUEST = 0x60;
+
+    /**
+     * The protocol op type for bind responses.
+     */
+    public static final byte OP_TYPE_BIND_RESPONSE = 0x61;
+
+    /**
+     * The protocol op type for compare requests.
+     */
+    public static final byte OP_TYPE_COMPARE_REQUEST = 0x6E;
+
+    /**
+     * The protocol op type for compare responses.
+     */
+    public static final byte OP_TYPE_COMPARE_RESPONSE = 0x6F;
+
+    /**
+     * The protocol op type for delete requests.
+     */
+    public static final byte OP_TYPE_DELETE_REQUEST = 0x4A;
+
+    /**
+     * The protocol op type for delete responses.
+     */
+    public static final byte OP_TYPE_DELETE_RESPONSE = 0x6B;
+
+    /**
+     * The protocol op type for extended requests.
+     */
+    public static final byte OP_TYPE_EXTENDED_REQUEST = 0x77;
+
+    /**
+     * The protocol op type for extended responses.
+     */
+    public static final byte OP_TYPE_EXTENDED_RESPONSE = 0x78;
+
+    /**
+     * The protocol op type for intermediate responses.
+     */
+    public static final byte OP_TYPE_INTERMEDIATE_RESPONSE = 0x79;
+
+    /**
+     * The protocol op type for modify DN requests.
+     */
+    public static final byte OP_TYPE_MODIFY_DN_REQUEST = 0x6C;
+
+    /**
+     * The protocol op type for modify DN responses.
+     */
+    public static final byte OP_TYPE_MODIFY_DN_RESPONSE = 0x6D;
+
+    /**
+     * The protocol op type for modify requests.
+     */
+    public static final byte OP_TYPE_MODIFY_REQUEST = 0x66;
+
+    /**
+     * The protocol op type for modify responses.
+     */
+    public static final byte OP_TYPE_MODIFY_RESPONSE = 0x67;
+    /**
+     * The protocol op type for search requests.
+     */
+    public static final byte OP_TYPE_SEARCH_REQUEST = 0x63;
+    /**
+     * The protocol op type for search result done elements.
+     */
+    public static final byte OP_TYPE_SEARCH_RESULT_DONE = 0x65;
+    /**
+     * The protocol op type for search result entries.
+     */
+    public static final byte OP_TYPE_SEARCH_RESULT_ENTRY = 0x64;
+    /**
+     * The protocol op type for search result references.
+     */
+    public static final byte OP_TYPE_SEARCH_RESULT_REFERENCE = 0x73;
+    /**
+     * The protocol op type for unbind requests.
+     */
+    public static final byte OP_TYPE_UNBIND_REQUEST = 0x42;
+    /**
+     * The BER type to use for the AuthenticationChoice element in a bind
+     * request when SASL authentication is to be used.
+     */
+    public static final byte TYPE_AUTHENTICATION_SASL = (byte) 0xA3;
+    /**
+     * The BER type to use for the AuthenticationChoice element in a bind
+     * request when simple authentication is to be used.
+     */
+    public static final byte TYPE_AUTHENTICATION_SIMPLE = (byte) 0x80;
+    /**
+     * The BER type to use for encoding the sequence of controls in an LDAP
+     * message.
+     */
+    public static final byte TYPE_CONTROL_SEQUENCE = (byte) 0xA0;
+    /**
+     * The BER type to use for the OID of an extended request.
+     */
+    public static final byte TYPE_EXTENDED_REQUEST_OID = (byte) 0x80;
+    /**
+     * The BER type to use for the value of an extended request.
+     */
+    public static final byte TYPE_EXTENDED_REQUEST_VALUE = (byte) 0x81;
+    /**
+     * The BER type to use for the OID of an extended response.
+     */
+    public static final byte TYPE_EXTENDED_RESPONSE_OID = (byte) 0x8A;
+    /**
+     * The BER type to use for the value of an extended response.
+     */
+    public static final byte TYPE_EXTENDED_RESPONSE_VALUE = (byte) 0x8B;
+    /**
+     * The BER type to use for AND filter components.
+     */
+    public static final byte TYPE_FILTER_AND = (byte) 0xA0;
+    /**
+     * The BER type to use for approximate filter components.
+     */
+    public static final byte TYPE_FILTER_APPROXIMATE = (byte) 0xA8;
+    /**
+     * The BER type to use for equality filter components.
+     */
+    public static final byte TYPE_FILTER_EQUALITY = (byte) 0xA3;
+    /**
+     * The BER type to use for extensible matching filter components.
+     */
+    public static final byte TYPE_FILTER_EXTENSIBLE_MATCH = (byte) 0xA9;
+    /**
+     * The BER type to use for greater than or equal to filter components.
+     */
+    public static final byte TYPE_FILTER_GREATER_OR_EQUAL = (byte) 0xA5;
+    /**
+     * The BER type to use for less than or equal to filter components.
+     */
+    public static final byte TYPE_FILTER_LESS_OR_EQUAL = (byte) 0xA6;
+    /**
+     * The BER type to use for NOT filter components.
+     */
+    public static final byte TYPE_FILTER_NOT = (byte) 0xA2;
+    /**
+     * The BER type to use for OR filter components.
+     */
+    public static final byte TYPE_FILTER_OR = (byte) 0xA1;
+    /**
+     * The BER type to use for presence filter components.
+     */
+    public static final byte TYPE_FILTER_PRESENCE = (byte) 0x87;
+    /**
+     * The BER type to use for substring filter components.
+     */
+    public static final byte TYPE_FILTER_SUBSTRING = (byte) 0xA4;
+    /**
+     * The BER type to use for the OID of an intermediate response message.
+     */
+    public static final byte TYPE_INTERMEDIATE_RESPONSE_OID = (byte) 0x80;
+    /**
+     * The BER type to use for the value of an intermediate response message.
+     */
+    public static final byte TYPE_INTERMEDIATE_RESPONSE_VALUE = (byte) 0x81;
+    /**
+     * The BER type to use for the DN attributes flag in a matching rule
+     * assertion.
+     */
+    public static final byte TYPE_MATCHING_RULE_DN_ATTRIBUTES = (byte) 0x84;
+    /**
+     * The BER type to use for the matching rule OID in a matching rule
+     * assertion.
+     */
+    public static final byte TYPE_MATCHING_RULE_ID = (byte) 0x81;
+    /**
+     * The BER type to use for the attribute type in a matching rule assertion.
+     */
+    public static final byte TYPE_MATCHING_RULE_TYPE = (byte) 0x82;
+    /**
+     * The BER type to use for the assertion value in a matching rule assertion.
+     */
+    public static final byte TYPE_MATCHING_RULE_VALUE = (byte) 0x83;
+    /**
+     * The BER type to use for the newSuperior component of a modify DN request.
+     */
+    public static final byte TYPE_MODIFY_DN_NEW_SUPERIOR = (byte) 0x80;
+    /**
+     * The BER type to use for encoding the sequence of referral URLs in an
+     * LDAPResult element.
+     */
+    public static final byte TYPE_REFERRAL_SEQUENCE = (byte) 0xA3;
+    /**
+     * The BER type to use for the server SASL credentials in a bind response.
+     */
+    public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87;
+    /**
+     * The BER type to use for the subAny component(s) of a substring filter.
+     */
+    public static final byte TYPE_SUBANY = (byte) 0x81;
+    /**
+     * The BER type to use for the subFinal components of a substring filter.
+     */
+    public static final byte TYPE_SUBFINAL = (byte) 0x82;
+    /**
+     * The BER type to use for the subInitial component of a substring filter.
+     */
+    public static final byte TYPE_SUBINITIAL = (byte) 0x80;
+    private static final FilterVisitor<IOException, ASN1Writer> ASN1_ENCODER =
+            new FilterVisitor<IOException, ASN1Writer>() {
+
+                @Override
+                public IOException visitAndFilter(final ASN1Writer writer,
+                        final List<Filter> subFilters) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_AND);
+                        for (final Filter subFilter : subFilters) {
+                            final IOException e = subFilter.accept(this, writer);
+                            if (e != null) {
+                                return e;
+                            }
+                        }
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitApproxMatchFilter(final ASN1Writer writer,
+                        final String attributeDescription, final ByteString assertionValue) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_APPROXIMATE);
+                        writer.writeOctetString(attributeDescription);
+                        writer.writeOctetString(assertionValue);
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitEqualityMatchFilter(final ASN1Writer writer,
+                        final String attributeDescription, final ByteString assertionValue) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_EQUALITY);
+                        writer.writeOctetString(attributeDescription);
+                        writer.writeOctetString(assertionValue);
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitExtensibleMatchFilter(final ASN1Writer writer,
+                        final String matchingRule, final String attributeDescription,
+                        final ByteString assertionValue, final boolean dnAttributes) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_EXTENSIBLE_MATCH);
+
+                        if (matchingRule != null) {
+                            writer.writeOctetString(LDAP.TYPE_MATCHING_RULE_ID, matchingRule);
+                        }
+
+                        if (attributeDescription != null) {
+                            writer.writeOctetString(LDAP.TYPE_MATCHING_RULE_TYPE,
+                                    attributeDescription);
+                        }
+
+                        writer.writeOctetString(LDAP.TYPE_MATCHING_RULE_VALUE, assertionValue);
+
+                        if (dnAttributes) {
+                            writer.writeBoolean(LDAP.TYPE_MATCHING_RULE_DN_ATTRIBUTES, true);
+                        }
+
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitGreaterOrEqualFilter(final ASN1Writer writer,
+                        final String attributeDescription, final ByteString assertionValue) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_GREATER_OR_EQUAL);
+                        writer.writeOctetString(attributeDescription);
+                        writer.writeOctetString(assertionValue);
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitLessOrEqualFilter(final ASN1Writer writer,
+                        final String attributeDescription, final ByteString assertionValue) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_LESS_OR_EQUAL);
+                        writer.writeOctetString(attributeDescription);
+                        writer.writeOctetString(assertionValue);
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitNotFilter(final ASN1Writer writer, final Filter subFilter) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_NOT);
+                        final IOException e = subFilter.accept(this, writer);
+                        if (e != null) {
+                            return e;
+                        }
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitOrFilter(final ASN1Writer writer,
+                        final List<Filter> subFilters) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_OR);
+                        for (final Filter subFilter : subFilters) {
+                            final IOException e = subFilter.accept(this, writer);
+                            if (e != null) {
+                                return e;
+                            }
+                        }
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitPresentFilter(final ASN1Writer writer,
+                        final String attributeDescription) {
+                    try {
+                        writer.writeOctetString(LDAP.TYPE_FILTER_PRESENCE, attributeDescription);
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitSubstringsFilter(final ASN1Writer writer,
+                        final String attributeDescription, final ByteString initialSubstring,
+                        final List<ByteString> anySubstrings, final ByteString finalSubstring) {
+                    try {
+                        writer.writeStartSequence(LDAP.TYPE_FILTER_SUBSTRING);
+                        writer.writeOctetString(attributeDescription);
+
+                        writer.writeStartSequence();
+                        if (initialSubstring != null) {
+                            writer.writeOctetString(LDAP.TYPE_SUBINITIAL, initialSubstring);
+                        }
+
+                        for (final ByteSequence anySubstring : anySubstrings) {
+                            writer.writeOctetString(LDAP.TYPE_SUBANY, anySubstring);
+                        }
+
+                        if (finalSubstring != null) {
+                            writer.writeOctetString(LDAP.TYPE_SUBFINAL, finalSubstring);
+                        }
+                        writer.writeEndSequence();
+
+                        writer.writeEndSequence();
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+
+                @Override
+                public IOException visitUnrecognizedFilter(final ASN1Writer writer,
+                        final byte filterTag, final ByteString filterBytes) {
+                    try {
+                        writer.writeOctetString(filterTag, filterBytes);
+                        return null;
+                    } catch (final IOException e) {
+                        return e;
+                    }
+                }
+            };
+
+    /**
+     * Creates a new LDAP reader which will read LDAP messages from an ASN.1
+     * reader using the provided decoding options.
+     *
+     * @param <R>
+     *            The type of ASN.1 reader used for decoding elements.
+     * @param asn1Reader
+     *            The ASN.1 reader from which LDAP messages will be read.
+     * @param options
+     *            LDAP message decoding options.
+     * @return A new LDAP reader which will read LDAP messages from an ASN.1
+     *         reader using the provided decoding options.
+     */
+    public static <R extends ASN1Reader> LDAPReader<R> getReader(final R asn1Reader,
+            final DecodeOptions options) {
+        return new LDAPReader<R>(asn1Reader, options);
+    }
+
+    /**
+     * Creates a new LDAP writer which will write LDAP messages to the provided
+     * ASN.1 writer.
+     *
+     * @param <W>
+     *            The type of ASN.1 writer used for encoding elements.
+     * @param asn1Writer
+     *            The ASN.1 writer to which LDAP messages will be written.
+     * @return A new LDAP writer which will write LDAP messages to the provided
+     *         ASN.1 writer.
+     */
+    public static <W extends ASN1Writer> LDAPWriter<W> getWriter(final W asn1Writer) {
+        return new LDAPWriter<W>(asn1Writer);
+    }
+
+    /**
+     * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a
+     * {@code Filter}.
+     *
+     * @param reader
+     *            The {@code ASN1Reader} from which the ASN.1 encoded
+     *            {@code Filter} should be read.
+     * @return The decoded {@code Filter}.
+     * @throws IOException
+     *             If an error occurs while reading from {@code reader}.
+     */
+    public static Filter readFilter(final ASN1Reader reader) throws IOException {
+        final byte type = reader.peekType();
+        switch (type) {
+        case LDAP.TYPE_FILTER_AND:
+            return readAndFilter(reader);
+        case LDAP.TYPE_FILTER_OR:
+            return readOrFilter(reader);
+        case LDAP.TYPE_FILTER_NOT:
+            return readNotFilter(reader);
+        case LDAP.TYPE_FILTER_EQUALITY:
+            return readEqualityMatchFilter(reader);
+        case LDAP.TYPE_FILTER_GREATER_OR_EQUAL:
+            return readGreaterOrEqualMatchFilter(reader);
+        case LDAP.TYPE_FILTER_LESS_OR_EQUAL:
+            return readLessOrEqualMatchFilter(reader);
+        case LDAP.TYPE_FILTER_APPROXIMATE:
+            return readApproxMatchFilter(reader);
+        case LDAP.TYPE_FILTER_SUBSTRING:
+            return readSubstringsFilter(reader);
+        case LDAP.TYPE_FILTER_PRESENCE:
+            return Filter.present(reader.readOctetStringAsString(type));
+        case LDAP.TYPE_FILTER_EXTENSIBLE_MATCH:
+            return readExtensibleMatchFilter(reader);
+        default:
+            return Filter.unrecognized(type, reader.readOctetString(type));
+        }
+    }
+
+    /**
+     * Reads the next ASN.1 element from the provided {@code ASN1Reader} as a an
+     * {@code Entry}.
+     *
+     * @param reader
+     *            The {@code ASN1Reader} from which the ASN.1 encoded
+     *            {@code Entry} should be read.
+     * @param options
+     *            The decode options to use when decoding the entry.
+     * @return The decoded {@code Entry}.
+     * @throws IOException
+     *             If an error occurs while reading from {@code reader}.
+     */
+    public static Entry readEntry(final ASN1Reader reader, final DecodeOptions options)
+            throws IOException {
+        return readEntry(reader, OP_TYPE_SEARCH_RESULT_ENTRY, options);
+    }
+
+    /**
+     * Writes a {@code Filter} to the provided {@code ASN1Writer}.
+     *
+     * @param writer
+     *            The {@code ASN1Writer} to which the ASN.1 encoded
+     *            {@code Filter} should be written.
+     * @param filter
+     *            The filter.
+     * @throws IOException
+     *             If an error occurs while writing to {@code writer}.
+     */
+    public static void writeFilter(final ASN1Writer writer, final Filter filter) throws IOException {
+        final IOException e = filter.accept(ASN1_ENCODER, writer);
+        if (e != null) {
+            throw e;
+        }
+    }
+
+    /**
+     * Writes an {@code Entry} to the provided {@code ASN1Writer}.
+     *
+     * @param writer
+     *            The {@code ASN1Writer} to which the ASN.1 encoded
+     *            {@code Entry} should be written.
+     * @param entry
+     *            The entry.
+     * @throws IOException
+     *             If an error occurs while writing to {@code writer}.
+     */
+    public static void writeEntry(final ASN1Writer writer, final Entry entry) throws IOException {
+        writeEntry(writer, OP_TYPE_SEARCH_RESULT_ENTRY, entry);
+    }
+
+    static AttributeDescription readAttributeDescription(final String attributeDescription,
+            final Schema schema) throws DecodeException {
+        try {
+            return AttributeDescription.valueOf(attributeDescription, schema);
+        } catch (final LocalizedIllegalArgumentException e) {
+            throw DecodeException.error(e.getMessageObject());
+        }
+    }
+
+    static DN readDN(final String dn, final Schema schema) throws DecodeException {
+        try {
+            return DN.valueOf(dn, schema);
+        } catch (final LocalizedIllegalArgumentException e) {
+            throw DecodeException.error(e.getMessageObject());
+        }
+    }
+
+    static Entry readEntry(final ASN1Reader reader, final byte tagType, final DecodeOptions options)
+            throws DecodeException, IOException {
+        reader.readStartSequence(tagType);
+        final Entry entry;
+        try {
+            final String dnString = reader.readOctetStringAsString();
+            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
+            final DN dn = readDN(dnString, schema);
+            entry = options.getEntryFactory().newEntry(dn);
+            reader.readStartSequence();
+            try {
+                while (reader.hasNextElement()) {
+                    reader.readStartSequence();
+                    try {
+                        final String ads = reader.readOctetStringAsString();
+                        final AttributeDescription ad = readAttributeDescription(ads, schema);
+                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
+                        reader.readStartSet();
+                        try {
+                            while (reader.hasNextElement()) {
+                                attribute.add(reader.readOctetString());
+                            }
+                            entry.addAttribute(attribute);
+                        } finally {
+                            reader.readEndSet();
+                        }
+                    } finally {
+                        reader.readEndSequence();
+                    }
+                }
+            } finally {
+                reader.readEndSequence();
+            }
+        } finally {
+            reader.readEndSequence();
+        }
+        return entry;
+    }
+
+    static void writeAttribute(final ASN1Writer writer, final Attribute attribute)
+            throws IOException {
+        writer.writeStartSequence();
+        {
+            writer.writeOctetString(attribute.getAttributeDescriptionAsString());
+            writer.writeStartSet();
+            {
+                for (final ByteString value : attribute) {
+                    writer.writeOctetString(value);
+                }
+            }
+            writer.writeEndSet();
+        }
+        writer.writeEndSequence();
+    }
+
+    static void writeEntry(final ASN1Writer writer, final byte typeTag, final Entry entry)
+            throws IOException {
+        writer.writeStartSequence(typeTag);
+        {
+            writer.writeOctetString(entry.getName().toString());
+            writer.writeStartSequence();
+            {
+                for (final Attribute attr : entry.getAllAttributes()) {
+                    writeAttribute(writer, attr);
+                }
+            }
+            writer.writeEndSequence();
+        }
+        writer.writeEndSequence();
+    }
+
+    private static Filter readAndFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_AND);
+        try {
+            if (reader.hasNextElement()) {
+                final List<Filter> subFilters = new LinkedList<Filter>();
+                do {
+                    subFilters.add(readFilter(reader));
+                } while (reader.hasNextElement());
+                return Filter.and(subFilters);
+            } else {
+                // No sub-filters - this is an RFC 4526 absolute true filter.
+                return Filter.alwaysTrue();
+            }
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    private static Filter readApproxMatchFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_APPROXIMATE);
+        try {
+            final String attributeDescription = reader.readOctetStringAsString();
+            final ByteString assertionValue = reader.readOctetString();
+            return Filter.approx(attributeDescription, assertionValue);
+        } finally {
+            reader.readEndSequence();
+        }
+
+    }
+
+    private static Filter readEqualityMatchFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_EQUALITY);
+        try {
+            final String attributeDescription = reader.readOctetStringAsString();
+            final ByteString assertionValue = reader.readOctetString();
+            return Filter.equality(attributeDescription, assertionValue);
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    private static Filter readExtensibleMatchFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_EXTENSIBLE_MATCH);
+        try {
+            String matchingRule = null;
+            if (reader.peekType() == LDAP.TYPE_MATCHING_RULE_ID) {
+                matchingRule = reader.readOctetStringAsString(LDAP.TYPE_MATCHING_RULE_ID);
+            }
+            String attributeDescription = null;
+            if (reader.peekType() == LDAP.TYPE_MATCHING_RULE_TYPE) {
+                attributeDescription = reader.readOctetStringAsString(LDAP.TYPE_MATCHING_RULE_TYPE);
+            }
+            boolean dnAttributes = false;
+            if (reader.hasNextElement()
+                    && (reader.peekType() == LDAP.TYPE_MATCHING_RULE_DN_ATTRIBUTES)) {
+                dnAttributes = reader.readBoolean();
+            }
+            final ByteString assertionValue = reader.readOctetString(LDAP.TYPE_MATCHING_RULE_VALUE);
+            return Filter.extensible(matchingRule, attributeDescription, assertionValue,
+                    dnAttributes);
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    private static Filter readGreaterOrEqualMatchFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_GREATER_OR_EQUAL);
+        try {
+            final String attributeDescription = reader.readOctetStringAsString();
+            final ByteString assertionValue = reader.readOctetString();
+            return Filter.greaterOrEqual(attributeDescription, assertionValue);
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    private static Filter readLessOrEqualMatchFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_LESS_OR_EQUAL);
+        try {
+            final String attributeDescription = reader.readOctetStringAsString();
+            final ByteString assertionValue = reader.readOctetString();
+            return Filter.lessOrEqual(attributeDescription, assertionValue);
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    private static Filter readNotFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_NOT);
+        try {
+            return Filter.not(readFilter(reader));
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    private static Filter readOrFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_OR);
+        try {
+            if (reader.hasNextElement()) {
+                final List<Filter> subFilters = new LinkedList<Filter>();
+                do {
+                    subFilters.add(readFilter(reader));
+                } while (reader.hasNextElement());
+                return Filter.or(subFilters);
+            } else {
+                // No sub-filters - this is an RFC 4526 absolute false filter.
+                return Filter.alwaysFalse();
+            }
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    private static Filter readSubstringsFilter(final ASN1Reader reader) throws IOException {
+        reader.readStartSequence(LDAP.TYPE_FILTER_SUBSTRING);
+        try {
+            final String attributeDescription = reader.readOctetStringAsString();
+            reader.readStartSequence();
+            try {
+                // FIXME: There should be at least one element in this substring
+                // filter sequence.
+                ByteString initialSubstring = null;
+                if (reader.peekType() == LDAP.TYPE_SUBINITIAL) {
+                    initialSubstring = reader.readOctetString(LDAP.TYPE_SUBINITIAL);
+                }
+                final List<ByteString> anySubstrings;
+                if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_SUBANY)) {
+                    anySubstrings = new LinkedList<ByteString>();
+                    do {
+                        anySubstrings.add(reader.readOctetString(LDAP.TYPE_SUBANY));
+                    } while (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_SUBANY));
+                } else {
+                    anySubstrings = Collections.emptyList();
+                }
+                ByteString finalSubstring = null;
+                if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_SUBFINAL)) {
+                    finalSubstring = reader.readOctetString(LDAP.TYPE_SUBFINAL);
+                }
+                return Filter.substrings(attributeDescription, initialSubstring, anySubstrings,
+                        finalSubstring);
+            } finally {
+                reader.readEndSequence();
+            }
+        } finally {
+            reader.readEndSequence();
+        }
+    }
+
+    // Prevent instantiation.
+    private LDAP() {
+        // Nothing to do.
+    }
+}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPMessageHandler.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPMessageHandler.java
new file mode 100644
index 0000000..89cc38c
--- /dev/null
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPMessageHandler.java
@@ -0,0 +1,399 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
+ */
+
+package org.forgerock.opendj.io;
+
+import java.io.IOException;
+
+import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.DecodeException;
+import org.forgerock.opendj.ldap.requests.AbandonRequest;
+import org.forgerock.opendj.ldap.requests.AddRequest;
+import org.forgerock.opendj.ldap.requests.CompareRequest;
+import org.forgerock.opendj.ldap.requests.DeleteRequest;
+import org.forgerock.opendj.ldap.requests.ExtendedRequest;
+import org.forgerock.opendj.ldap.requests.GenericBindRequest;
+import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
+import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
+import org.forgerock.opendj.ldap.requests.UnbindRequest;
+import org.forgerock.opendj.ldap.responses.BindResult;
+import org.forgerock.opendj.ldap.responses.CompareResult;
+import org.forgerock.opendj.ldap.responses.ExtendedResult;
+import org.forgerock.opendj.ldap.responses.IntermediateResponse;
+import org.forgerock.opendj.ldap.responses.Result;
+import org.forgerock.opendj.ldap.responses.SearchResultEntry;
+import org.forgerock.opendj.ldap.responses.SearchResultReference;
+
+/**
+ * An interface for handling LDAP messages decoded using an {@link LDAPReader}.
+ */
+public interface LDAPMessageHandler {
+
+    /**
+     * Handles an LDAP abandon request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded abandon request.
+     * @throws DecodeException
+     *             If this handler does not support abandon requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void abandonRequest(int messageID, AbandonRequest request) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP add request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded add request.
+     * @throws DecodeException
+     *             If this handler does not support add requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void addRequest(int messageID, AddRequest request) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP add result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded add result.
+     * @throws DecodeException
+     *             If this handler does not support add results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void addResult(int messageID, Result result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP bind request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param version
+     *            The requested LDAP protocol version.
+     * @param request
+     *            The decoded bind request.
+     * @throws DecodeException
+     *             If this handler does not support bind requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void bindRequest(int messageID, int version, GenericBindRequest request)
+            throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP bind result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded bind result.
+     * @throws DecodeException
+     *             If this handler does not support bind results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void bindResult(int messageID, BindResult result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP compare request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded compare request.
+     * @throws DecodeException
+     *             If this handler does not support compare requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void compareRequest(int messageID, CompareRequest request) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP compare result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded compare result.
+     * @throws DecodeException
+     *             If this handler does not support compare results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void compareResult(int messageID, CompareResult result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP delete request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded delete request.
+     * @throws DecodeException
+     *             If this handler does not support delete requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void deleteRequest(int messageID, DeleteRequest request) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP delete result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded delete result.
+     * @throws DecodeException
+     *             If this handler does not support delete results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void deleteResult(int messageID, Result result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP extended request message.
+     *
+     * @param <R>
+     *            type of extended result
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded extended request.
+     * @throws DecodeException
+     *             If this handler does not support extended requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    <R extends ExtendedResult> void extendedRequest(int messageID, ExtendedRequest<R> request)
+            throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP extended result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded extended result.
+     * @throws DecodeException
+     *             If this handler does not support extended results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void extendedResult(int messageID, ExtendedResult result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP intermediate response message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param response
+     *            The decoded intermediate response.
+     * @throws DecodeException
+     *             If this handler does not support intermediate responses.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void intermediateResponse(int messageID, IntermediateResponse response) throws DecodeException,
+            IOException;
+
+    /**
+     * Handles an LDAP modify DN request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded modify DN request.
+     * @throws DecodeException
+     *             If this handler does not support modify DN requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void modifyDNRequest(int messageID, ModifyDNRequest request) throws DecodeException,
+            IOException;
+
+    /**
+     * Handles an LDAP modify DN result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded modify DN result.
+     * @throws DecodeException
+     *             If this handler does not support modify DN results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void modifyDNResult(int messageID, Result result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP modify request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded modify request.
+     * @throws DecodeException
+     *             If this handler does not support modify requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void modifyRequest(int messageID, ModifyRequest request) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP modify result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded modify result.
+     * @throws DecodeException
+     *             If this handler does not support modify results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void modifyResult(int messageID, Result result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP search request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded search request.
+     * @throws DecodeException
+     *             If this handler does not support search requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void searchRequest(int messageID, SearchRequest request) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP search result message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The decoded search result.
+     * @throws DecodeException
+     *             If this handler does not support search results.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void searchResult(int messageID, Result result) throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP search result entry message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param entry
+     *            The decoded search result entry.
+     * @throws DecodeException
+     *             If this handler does not support search result entries.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void searchResultEntry(int messageID, SearchResultEntry entry) throws DecodeException,
+            IOException;
+
+    /**
+     * Handles an LDAP search result reference message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param reference
+     *            The decoded search result reference.
+     * @throws DecodeException
+     *             If this handler does not support search result references.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             response.
+     */
+    void searchResultReference(int messageID, SearchResultReference reference)
+            throws DecodeException, IOException;
+
+    /**
+     * Handles an LDAP unbind request message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The decoded unbind request.
+     * @throws DecodeException
+     *             If this handler does not support unbind requests.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             request.
+     */
+    void unbindRequest(int messageID, UnbindRequest request) throws DecodeException, IOException;
+
+    /**
+     * Handles an unrecognized LDAP message.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param messageTag
+     *            The LDAP message type.
+     * @param messageBytes
+     *            The contents of the LDAP message.
+     * @throws DecodeException
+     *             If this handler does not support the message type.
+     * @throws IOException
+     *             If an unexpected IO error occurred while processing the
+     *             message.
+     */
+    void unrecognizedMessage(int messageID, byte messageTag, ByteString messageBytes)
+            throws DecodeException, IOException;
+}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java
index 9f86b0c..a14e14e 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java
@@ -27,9 +27,11 @@
 
 package org.forgerock.opendj.io;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static com.forgerock.opendj.ldap.LDAPConstants.*;
-import static com.forgerock.opendj.util.StaticUtils.*;
+import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAP_MODIFICATION_DECODE_INVALID_MOD_TYPE;
+import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_DEREF;
+import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_SCOPE;
+import static com.forgerock.opendj.util.StaticUtils.IO_LOG;
+import static com.forgerock.opendj.util.StaticUtils.byteToHex;
 
 import java.io.IOException;
 
@@ -72,55 +74,58 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldap.schema.Schema;
-import org.forgerock.opendj.ldap.spi.LDAPMessageHandler;
-
-import com.forgerock.opendj.ldap.LDAPUtils;
 
 /**
- * Responsible for reading LDAP messages.
+ * Reads LDAP messages from an underlying ASN.1 reader.
  *
  * @param <R>
- *            type of ASN1 reader used to decode elements
+ *            The type of ASN.1 reader used for decoding elements.
  */
 public final class LDAPReader<R extends ASN1Reader> {
-
+    private final DecodeOptions options;
     private final R reader;
 
-    private final DecodeOptions options;
-
-    /**
-     * Creates a reader with the provided ASN1 reader and decoding options.
-     *
-     * @param asn1Reader
-     *            reader capable of decoding ASN1 elements
-     * @param options
-     *            allow to control how responses and requests are decoded
-     */
-    public LDAPReader(final R asn1Reader, final DecodeOptions options) {
+    LDAPReader(final R asn1Reader, final DecodeOptions options) {
         this.reader = asn1Reader;
         this.options = options;
     }
 
     /**
-     * Returns the ASN1 reader used to decode elements.
+     * Returns the ASN.1 reader from which LDAP messages will be read.
      *
-     * @return ASN1 reader
+     * @return The ASN.1 reader from which LDAP messages will be read.
      */
     public R getASN1Reader() {
         return reader;
     }
 
     /**
-     * Read a LDAP message by decoding the elements from the provided ASN.1 asn1Reader.
+     * Returns {@code true} if the next LDAP message can be read without
+     * blocking.
+     *
+     * @return {@code true} if the next LDAP message can be read without
+     *         blocking or {@code false} otherwise.
+     * @throws DecodeException
+     *             If the available data was not a valid LDAP message.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public boolean hasMessageAvailable() throws DecodeException, IOException {
+        return reader.elementAvailable();
+    }
+
+    /**
+     * Reads the next LDAP message from the underlying ASN.1 reader.
      *
      * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle a decoded
+     *            The message handler which will handle the decoded LDAP
      *            message.
+     * @throws DecodeException
+     *             If the available data was not a valid LDAP message.
      * @throws IOException
-     *             If an error occurred while reading bytes to decode.
+     *             If an unexpected IO error occurred.
      */
-    public void readMessage(final LDAPMessageHandler handler)
-            throws IOException {
+    public void readMessage(final LDAPMessageHandler handler) throws DecodeException, IOException {
         reader.readStartSequence();
         try {
             final int messageID = (int) reader.readInteger();
@@ -130,124 +135,28 @@
         }
     }
 
-    /**
-     * Indicates whether or not the next message can be read without blocking.
-     *
-     * @return {@code true} if a complete element is available or {@code false}
-     *         otherwise.
-     * @throws DecodeException
-     *             If the available data was not valid ASN.1.
-     * @throws IOException
-     *             If an unexpected IO error occurred.
-     */
-    public boolean hasMessageAvailable() throws DecodeException, IOException {
-        return reader.elementAvailable();
-    }
-
-    /**
-     * Decodes the elements from the provided ASN.1 read as an LDAP abandon
-     * request protocol op.
-     *
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readAbandonRequest(final int messageID, final LDAPMessageHandler handler) throws IOException {
-        final int msgToAbandon = (int) reader.readInteger(OP_TYPE_ABANDON_REQUEST);
+    private void readAbandonRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        final int msgToAbandon = (int) reader.readInteger(LDAP.OP_TYPE_ABANDON_REQUEST);
         final AbandonRequest message = Requests.newAbandonRequest(msgToAbandon);
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP ABANDON REQUEST(messageID={}, request={})", messageID, message);
-
         handler.abandonRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP add
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readAddRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        Entry entry;
-
-        reader.readStartSequence(OP_TYPE_ADD_REQUEST);
-        try {
-            final String dnString = reader.readOctetStringAsString();
-            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
-            final DN dn = readDN(dnString, schema);
-            entry = options.getEntryFactory().newEntry(dn);
-
-            reader.readStartSequence();
-            try {
-                while (reader.hasNextElement()) {
-                    reader.readStartSequence();
-                    try {
-                        final String ads = reader.readOctetStringAsString();
-                        final AttributeDescription ad = readAttributeDescription(ads, schema);
-                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
-
-                        reader.readStartSet();
-                        try {
-                            while (reader.hasNextElement()) {
-                                attribute.add(reader.readOctetString());
-                            }
-                            entry.addAttribute(attribute);
-                        } finally {
-                            reader.readEndSet();
-                        }
-                    } finally {
-                        reader.readEndSequence();
-                    }
-                }
-            } finally {
-                reader.readEndSequence();
-            }
-        } finally {
-            reader.readEndSequence();
-        }
-
+    private void readAddRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        final Entry entry = LDAP.readEntry(reader, LDAP.OP_TYPE_ADD_REQUEST, options);
         final AddRequest message = Requests.newAddRequest(entry);
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP ADD REQUEST(messageID={}, request={})", messageID, message);
-
         handler.addRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an add response
-     * protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readAddResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        Result message;
-
-        reader.readStartSequence(OP_TYPE_ADD_RESPONSE);
+    private void readAddResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_ADD_RESPONSE);
+        final Result message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -259,53 +168,24 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP ADD RESULT(messageID={}, result={})", messageID, message);
-
         handler.addResult(messageID, message);
     }
 
-    private AttributeDescription readAttributeDescription(final String attributeDescription,
-            final Schema schema) throws DecodeException {
-        try {
-            return AttributeDescription.valueOf(attributeDescription, schema);
-        } catch (final LocalizedIllegalArgumentException e) {
-            throw DecodeException.error(e.getMessageObject());
-        }
-    }
-
-    /**
-     * Decodes the elements from the provided ASN.1 read as an LDAP bind request
-     * protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readBindRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        reader.readStartSequence(OP_TYPE_BIND_REQUEST);
+    private void readBindRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_BIND_REQUEST);
         try {
             final int protocolVersion = (int) reader.readInteger();
             final String authName = reader.readOctetStringAsString();
             final byte authType = reader.peekType();
             final byte[] authBytes = reader.readOctetString(authType).toByteArray();
-
             final GenericBindRequest request =
                     Requests.newGenericBindRequest(authName, authType, authBytes);
-
             readControls(request);
-
-            IO_LOG.trace("DECODE LDAP BIND REQUEST(messageID={}, auth=0x{}, request={})", messageID,
-                    byteToHex(request.getAuthenticationType()), request);
+            IO_LOG.trace("DECODE LDAP BIND REQUEST(messageID={}, auth=0x{}, request={})",
+                    messageID, byteToHex(request.getAuthenticationType()), request);
 
             handler.bindRequest(messageID, protocolVersion, request);
         } finally {
@@ -313,25 +193,10 @@
         }
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a bind response
-     * protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readBindResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        BindResult message;
-
-        reader.readStartSequence(OP_TYPE_BIND_RESPONSE);
+    private void readBindResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_BIND_RESPONSE);
+        final BindResult message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -340,49 +205,30 @@
                     Responses.newBindResult(resultCode).setMatchedDN(matchedDN)
                             .setDiagnosticMessage(diagnosticMessage);
             readResponseReferrals(message);
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_SERVER_SASL_CREDENTIALS)) {
+            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_SERVER_SASL_CREDENTIALS)) {
                 message.setServerSASLCredentials(reader
-                        .readOctetString(TYPE_SERVER_SASL_CREDENTIALS));
+                        .readOctetString(LDAP.TYPE_SERVER_SASL_CREDENTIALS));
             }
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP BIND RESULT(messageID={}, result={})", messageID, message);
-
         handler.bindResult(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP compare
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readCompareRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        CompareRequest message;
-
-        reader.readStartSequence(OP_TYPE_COMPARE_REQUEST);
+    private void readCompareRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_COMPARE_REQUEST);
+        final CompareRequest message;
         try {
             final String dnString = reader.readOctetStringAsString();
             final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
-            final DN dn = readDN(dnString, schema);
-
+            final DN dn = LDAP.readDN(dnString, schema);
             reader.readStartSequence();
             try {
                 final String ads = reader.readOctetStringAsString();
-                final AttributeDescription ad = readAttributeDescription(ads, schema);
+                final AttributeDescription ad = LDAP.readAttributeDescription(ads, schema);
                 final ByteString assertionValue = reader.readOctetString();
                 message = Requests.newCompareRequest(dn, ad, assertionValue);
             } finally {
@@ -391,33 +237,15 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP COMPARE REQUEST(messageID={}, request={})", messageID, message);
-
         handler.compareRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a compare response
-     * protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readCompareResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        CompareResult message;
-
-        reader.readStartSequence(OP_TYPE_COMPARE_RESPONSE);
+    private void readCompareResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_COMPARE_RESPONSE);
+        final CompareResult message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -429,98 +257,39 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP COMPARE RESULT(messageID={}, result={})", messageID, message);
-
         handler.compareResult(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP control.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param request
-     *            The decoded request to decode controls for.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readControl(final Request request) throws IOException {
-        String oid;
-        boolean isCritical;
-        ByteString value;
-
+    private Control readControl() throws IOException {
         reader.readStartSequence();
         try {
-            oid = reader.readOctetStringAsString();
-            isCritical = false;
-            value = null;
+            final String oid = reader.readOctetStringAsString();
+            final boolean isCritical;
             if (reader.hasNextElement() && (reader.peekType() == ASN1.UNIVERSAL_BOOLEAN_TYPE)) {
                 isCritical = reader.readBoolean();
+            } else {
+                isCritical = false;
             }
+            final ByteString value;
             if (reader.hasNextElement() && (reader.peekType() == ASN1.UNIVERSAL_OCTET_STRING_TYPE)) {
                 value = reader.readOctetString();
+            } else {
+                value = null;
             }
+            return GenericControl.newControl(oid, isCritical, value);
         } finally {
             reader.readEndSequence();
         }
-
-        final Control c = GenericControl.newControl(oid, isCritical, value);
-        request.addControl(c);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP control.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param response
-     *            The decoded message to decode controls for.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readControl(final Response response) throws IOException {
-        String oid;
-        boolean isCritical;
-        ByteString value;
-
-        reader.readStartSequence();
-        try {
-            oid = reader.readOctetStringAsString();
-            isCritical = false;
-            value = null;
-            if (reader.hasNextElement() && (reader.peekType() == ASN1.UNIVERSAL_BOOLEAN_TYPE)) {
-                isCritical = reader.readBoolean();
-            }
-            if (reader.hasNextElement() && (reader.peekType() == ASN1.UNIVERSAL_OCTET_STRING_TYPE)) {
-                value = reader.readOctetString();
-            }
-        } finally {
-            reader.readEndSequence();
-        }
-
-        final Control c = GenericControl.newControl(oid, isCritical, value);
-        response.addControl(c);
-    }
-
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a set of controls.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param request
-     *            The decoded message to decode controls for.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
     private void readControls(final Request request) throws IOException {
-        if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE)) {
-            reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
+        if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_CONTROL_SEQUENCE)) {
+            reader.readStartSequence(LDAP.TYPE_CONTROL_SEQUENCE);
             try {
                 while (reader.hasNextElement()) {
-                    readControl(request);
+                    request.addControl(readControl());
                 }
             } finally {
                 reader.readEndSequence();
@@ -528,77 +297,34 @@
         }
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a set of controls.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param response
-     *            The decoded message to decode controls for.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readControls(final Response response)
+    private void readControls(final Response response) throws IOException {
+        if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_CONTROL_SEQUENCE)) {
+            reader.readStartSequence(LDAP.TYPE_CONTROL_SEQUENCE);
+            try {
+                while (reader.hasNextElement()) {
+                    response.addControl(readControl());
+                }
+            } finally {
+                reader.readEndSequence();
+            }
+        }
+    }
+
+    private void readDeleteRequest(final int messageID, final LDAPMessageHandler handler)
             throws IOException {
-        if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE)) {
-            reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
-            try {
-                while (reader.hasNextElement()) {
-                    readControl(response);
-                }
-            } finally {
-                reader.readEndSequence();
-            }
-        }
-    }
-
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP delete
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readDeleteRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        final String dnString = reader.readOctetStringAsString(OP_TYPE_DELETE_REQUEST);
+        final String dnString = reader.readOctetStringAsString(LDAP.OP_TYPE_DELETE_REQUEST);
         final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
-        final DN dn = readDN(dnString, schema);
+        final DN dn = LDAP.readDN(dnString, schema);
         final DeleteRequest message = Requests.newDeleteRequest(dn);
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP DELETE REQUEST(messageID={}, request={})", messageID, message);
-
         handler.deleteRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a delete response
-     * protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readDeleteResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        Result message;
-
-        reader.readStartSequence(OP_TYPE_DELETE_RESPONSE);
+    private void readDeleteResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_DELETE_RESPONSE);
+        final Result message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -610,81 +336,35 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP DELETE RESULT(messageID={}, result={})", messageID, message);
-
         handler.deleteResult(messageID, message);
     }
 
-    private DN readDN(final String dn, final Schema schema) throws DecodeException {
+    private void readExtendedRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_EXTENDED_REQUEST);
+        final GenericExtendedRequest message;
         try {
-            return DN.valueOf(dn, schema);
-        } catch (final LocalizedIllegalArgumentException e) {
-            throw DecodeException.error(e.getMessageObject());
-        }
-    }
-
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP extended
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readExtendedRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        String oid;
-        ByteString value;
-
-        reader.readStartSequence(OP_TYPE_EXTENDED_REQUEST);
-        try {
-            oid = reader.readOctetStringAsString(TYPE_EXTENDED_REQUEST_OID);
-            value = null;
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_EXTENDED_REQUEST_VALUE)) {
-                value = reader.readOctetString(TYPE_EXTENDED_REQUEST_VALUE);
+            final String oid = reader.readOctetStringAsString(LDAP.TYPE_EXTENDED_REQUEST_OID);
+            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_EXTENDED_REQUEST_VALUE)) {
+                final ByteString value = reader.readOctetString(LDAP.TYPE_EXTENDED_REQUEST_VALUE);
+                message = Requests.newGenericExtendedRequest(oid, value);
+            } else {
+                message = Requests.newGenericExtendedRequest(oid, null);
             }
         } finally {
             reader.readEndSequence();
         }
-
-        final GenericExtendedRequest message = Requests.newGenericExtendedRequest(oid, value);
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP EXTENDED REQUEST(messageID={}, request={})", messageID, message);
-
         handler.extendedRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a extended
-     * response protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readExtendedResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-
-        GenericExtendedResult message;
-
-        reader.readStartSequence(OP_TYPE_EXTENDED_RESPONSE);
+    private void readExtendedResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_EXTENDED_RESPONSE);
+        final GenericExtendedResult message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -693,129 +373,73 @@
                     Responses.newGenericExtendedResult(resultCode).setMatchedDN(matchedDN)
                             .setDiagnosticMessage(diagnosticMessage);
             readResponseReferrals(message);
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_EXTENDED_RESPONSE_OID)) {
-                message.setOID(reader.readOctetStringAsString(TYPE_EXTENDED_RESPONSE_OID));
+            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_EXTENDED_RESPONSE_OID)) {
+                message.setOID(reader.readOctetStringAsString(LDAP.TYPE_EXTENDED_RESPONSE_OID));
             }
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_EXTENDED_RESPONSE_VALUE)) {
-                message.setValue(reader.readOctetString(TYPE_EXTENDED_RESPONSE_VALUE));
+            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_EXTENDED_RESPONSE_VALUE)) {
+                message.setValue(reader.readOctetString(LDAP.TYPE_EXTENDED_RESPONSE_VALUE));
             }
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP EXTENDED RESULT(messageID={}, result={})", messageID, message);
-
         handler.extendedResult(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP
-     * intermediate response protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readIntermediateResponse(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        GenericIntermediateResponse message;
-
-        reader.readStartSequence(OP_TYPE_INTERMEDIATE_RESPONSE);
+    private void readIntermediateResponse(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_INTERMEDIATE_RESPONSE);
+        final GenericIntermediateResponse message;
         try {
             message = Responses.newGenericIntermediateResponse();
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_INTERMEDIATE_RESPONSE_OID)) {
-                message.setOID(reader.readOctetStringAsString(TYPE_INTERMEDIATE_RESPONSE_OID));
+            if (reader.hasNextElement()
+                    && (reader.peekType() == LDAP.TYPE_INTERMEDIATE_RESPONSE_OID)) {
+                message.setOID(reader.readOctetStringAsString(LDAP.TYPE_INTERMEDIATE_RESPONSE_OID));
             }
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_INTERMEDIATE_RESPONSE_VALUE)) {
-                message.setValue(reader.readOctetString(TYPE_INTERMEDIATE_RESPONSE_VALUE));
+            if (reader.hasNextElement()
+                    && (reader.peekType() == LDAP.TYPE_INTERMEDIATE_RESPONSE_VALUE)) {
+                message.setValue(reader.readOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_VALUE));
             }
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
-        IO_LOG.trace("DECODE LDAP INTERMEDIATE RESPONSE(messageID={}, response={})",
-                messageID, message);
-
+        IO_LOG.trace("DECODE LDAP INTERMEDIATE RESPONSE(messageID={}, response={})", messageID,
+                message);
         handler.intermediateResponse(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a modify DN
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readModifyDNRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        ModifyDNRequest message;
-
-        reader.readStartSequence(OP_TYPE_MODIFY_DN_REQUEST);
+    private void readModifyDNRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_DN_REQUEST);
+        final ModifyDNRequest message;
         try {
             final String dnString = reader.readOctetStringAsString();
             final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
-            final DN dn = readDN(dnString, schema);
-
+            final DN dn = LDAP.readDN(dnString, schema);
             final String newRDNString = reader.readOctetStringAsString();
             final RDN newRDN = readRDN(newRDNString, schema);
-
             message = Requests.newModifyDNRequest(dn, newRDN);
-
             message.setDeleteOldRDN(reader.readBoolean());
-
-            if (reader.hasNextElement() && (reader.peekType() == TYPE_MODIFY_DN_NEW_SUPERIOR)) {
+            if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_MODIFY_DN_NEW_SUPERIOR)) {
                 final String newSuperiorString =
-                        reader.readOctetStringAsString(TYPE_MODIFY_DN_NEW_SUPERIOR);
-                final DN newSuperior = readDN(newSuperiorString, schema);
+                        reader.readOctetStringAsString(LDAP.TYPE_MODIFY_DN_NEW_SUPERIOR);
+                final DN newSuperior = LDAP.readDN(newSuperiorString, schema);
                 message.setNewSuperior(newSuperior);
             }
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP MODIFY DN REQUEST(messageID={}, request={})", messageID, message);
-
         handler.modifyDNRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a modify DN
-     * response protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readModifyDNResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        Result message;
-
-        reader.readStartSequence(OP_TYPE_MODIFY_DN_RESPONSE);
+    private void readModifyDNResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_DN_RESPONSE);
+        final Result message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -827,39 +451,20 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE MODIFY DN RESULT(messageID={}, result={})", messageID, message);
-
         handler.modifyDNResult(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP modify
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readModifyRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        ModifyRequest message;
-
-        reader.readStartSequence(OP_TYPE_MODIFY_REQUEST);
+    private void readModifyRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_REQUEST);
+        final ModifyRequest message;
         try {
             final String dnString = reader.readOctetStringAsString();
             final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
-            final DN dn = readDN(dnString, schema);
+            final DN dn = LDAP.readDN(dnString, schema);
             message = Requests.newModifyRequest(dn);
-
             reader.readStartSequence();
             try {
                 while (reader.hasNextElement()) {
@@ -875,10 +480,10 @@
                         reader.readStartSequence();
                         try {
                             final String ads = reader.readOctetStringAsString();
-                            final AttributeDescription ad = readAttributeDescription(ads, schema);
+                            final AttributeDescription ad =
+                                    LDAP.readAttributeDescription(ads, schema);
                             final Attribute attribute =
                                     options.getAttributeFactory().newAttribute(ad);
-
                             reader.readStartSet();
                             try {
                                 while (reader.hasNextElement()) {
@@ -901,33 +506,15 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP MODIFY REQUEST(messageID={}, request={})", messageID, message);
-
         handler.modifyRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a modify response
-     * protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readModifyResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        Result message;
-
-        reader.readStartSequence(OP_TYPE_MODIFY_RESPONSE);
+    private void readModifyResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_MODIFY_RESPONSE);
+        final Result message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -939,143 +526,76 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP MODIFY RESULT(messageID={}, result={})", messageID, message);
-
         handler.modifyResult(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP protocol
-     * op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readProtocolOp(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
+    private void readProtocolOp(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
         final byte type = reader.peekType();
-
         switch (type) {
-        case OP_TYPE_UNBIND_REQUEST: // 0x42
+        case LDAP.OP_TYPE_UNBIND_REQUEST: // 0x42
             readUnbindRequest(messageID, handler);
             break;
-        case 0x43: // 0x43
-        case 0x44: // 0x44
-        case 0x45: // 0x45
-        case 0x46: // 0x46
-        case 0x47: // 0x47
-        case 0x48: // 0x48
-        case 0x49: // 0x49
-            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
-            break;
-        case OP_TYPE_DELETE_REQUEST: // 0x4A
+        case LDAP.OP_TYPE_DELETE_REQUEST: // 0x4A
             readDeleteRequest(messageID, handler);
             break;
-        case 0x4B: // 0x4B
-        case 0x4C: // 0x4C
-        case 0x4D: // 0x4D
-        case 0x4E: // 0x4E
-        case 0x4F: // 0x4F
-            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
-            break;
-        case OP_TYPE_ABANDON_REQUEST: // 0x50
+        case LDAP.OP_TYPE_ABANDON_REQUEST: // 0x50
             readAbandonRequest(messageID, handler);
             break;
-        case 0x51: // 0x51
-        case 0x52: // 0x52
-        case 0x53: // 0x53
-        case 0x54: // 0x54
-        case 0x55: // 0x55
-        case 0x56: // 0x56
-        case 0x57: // 0x57
-        case 0x58: // 0x58
-        case 0x59: // 0x59
-        case 0x5A: // 0x5A
-        case 0x5B: // 0x5B
-        case 0x5C: // 0x5C
-        case 0x5D: // 0x5D
-        case 0x5E: // 0x5E
-        case 0x5F: // 0x5F
-            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
-            break;
-        case OP_TYPE_BIND_REQUEST: // 0x60
+        case LDAP.OP_TYPE_BIND_REQUEST: // 0x60
             readBindRequest(messageID, handler);
             break;
-        case OP_TYPE_BIND_RESPONSE: // 0x61
+        case LDAP.OP_TYPE_BIND_RESPONSE: // 0x61
             readBindResult(messageID, handler);
             break;
-        case 0x62: // 0x62
-            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
-            break;
-        case OP_TYPE_SEARCH_REQUEST: // 0x63
+        case LDAP.OP_TYPE_SEARCH_REQUEST: // 0x63
             readSearchRequest(messageID, handler);
             break;
-        case OP_TYPE_SEARCH_RESULT_ENTRY: // 0x64
+        case LDAP.OP_TYPE_SEARCH_RESULT_ENTRY: // 0x64
             readSearchResultEntry(messageID, handler);
             break;
-        case OP_TYPE_SEARCH_RESULT_DONE: // 0x65
+        case LDAP.OP_TYPE_SEARCH_RESULT_DONE: // 0x65
             readSearchResult(messageID, handler);
             break;
-        case OP_TYPE_MODIFY_REQUEST: // 0x66
+        case LDAP.OP_TYPE_MODIFY_REQUEST: // 0x66
             readModifyRequest(messageID, handler);
             break;
-        case OP_TYPE_MODIFY_RESPONSE: // 0x67
+        case LDAP.OP_TYPE_MODIFY_RESPONSE: // 0x67
             readModifyResult(messageID, handler);
             break;
-        case OP_TYPE_ADD_REQUEST: // 0x68
+        case LDAP.OP_TYPE_ADD_REQUEST: // 0x68
             readAddRequest(messageID, handler);
             break;
-        case OP_TYPE_ADD_RESPONSE: // 0x69
+        case LDAP.OP_TYPE_ADD_RESPONSE: // 0x69
             readAddResult(messageID, handler);
             break;
-        case 0x6A: // 0x6A
-            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
-            break;
-        case OP_TYPE_DELETE_RESPONSE: // 0x6B
+        case LDAP.OP_TYPE_DELETE_RESPONSE: // 0x6B
             readDeleteResult(messageID, handler);
             break;
-        case OP_TYPE_MODIFY_DN_REQUEST: // 0x6C
+        case LDAP.OP_TYPE_MODIFY_DN_REQUEST: // 0x6C
             readModifyDNRequest(messageID, handler);
             break;
-        case OP_TYPE_MODIFY_DN_RESPONSE: // 0x6D
+        case LDAP.OP_TYPE_MODIFY_DN_RESPONSE: // 0x6D
             readModifyDNResult(messageID, handler);
             break;
-        case OP_TYPE_COMPARE_REQUEST: // 0x6E
+        case LDAP.OP_TYPE_COMPARE_REQUEST: // 0x6E
             readCompareRequest(messageID, handler);
             break;
-        case OP_TYPE_COMPARE_RESPONSE: // 0x6F
+        case LDAP.OP_TYPE_COMPARE_RESPONSE: // 0x6F
             readCompareResult(messageID, handler);
             break;
-        case 0x70: // 0x70
-        case 0x71: // 0x71
-        case 0x72: // 0x72
-            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
-            break;
-        case OP_TYPE_SEARCH_RESULT_REFERENCE: // 0x73
+        case LDAP.OP_TYPE_SEARCH_RESULT_REFERENCE: // 0x73
             readSearchResultReference(messageID, handler);
             break;
-        case 0x74: // 0x74
-        case 0x75: // 0x75
-        case 0x76: // 0x76
-            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
-            break;
-        case OP_TYPE_EXTENDED_REQUEST: // 0x77
+        case LDAP.OP_TYPE_EXTENDED_REQUEST: // 0x77
             readExtendedRequest(messageID, handler);
             break;
-        case OP_TYPE_EXTENDED_RESPONSE: // 0x78
+        case LDAP.OP_TYPE_EXTENDED_RESPONSE: // 0x78
             readExtendedResult(messageID, handler);
             break;
-        case OP_TYPE_INTERMEDIATE_RESPONSE: // 0x79
+        case LDAP.OP_TYPE_INTERMEDIATE_RESPONSE: // 0x79
             readIntermediateResponse(messageID, handler);
             break;
         default:
@@ -1092,10 +612,9 @@
         }
     }
 
-    private void readResponseReferrals(final Result message)
-            throws IOException {
-        if (reader.hasNextElement() && (reader.peekType() == TYPE_REFERRAL_SEQUENCE)) {
-            reader.readStartSequence(TYPE_REFERRAL_SEQUENCE);
+    private void readResponseReferrals(final Result message) throws IOException {
+        if (reader.hasNextElement() && (reader.peekType() == LDAP.TYPE_REFERRAL_SEQUENCE)) {
+            reader.readStartSequence(LDAP.TYPE_REFERRAL_SEQUENCE);
             try {
                 // Should have at least 1.
                 do {
@@ -1107,37 +626,20 @@
         }
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP search
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readSearchRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        SearchRequest message;
-
-        reader.readStartSequence(OP_TYPE_SEARCH_REQUEST);
+    private void readSearchRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_SEARCH_REQUEST);
+        final SearchRequest message;
         try {
             final String baseDNString = reader.readOctetStringAsString();
             final Schema schema = options.getSchemaResolver().resolveSchema(baseDNString);
-            final DN baseDN = readDN(baseDNString, schema);
-
+            final DN baseDN = LDAP.readDN(baseDNString, schema);
             final int scopeIntValue = reader.readEnumerated();
             final SearchScope scope = SearchScope.valueOf(scopeIntValue);
             if (scope == null) {
                 throw DecodeException.error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_SCOPE
                         .get(scopeIntValue));
             }
-
             final int dereferencePolicyIntValue = reader.readEnumerated();
             final DereferenceAliasesPolicy dereferencePolicy =
                     DereferenceAliasesPolicy.valueOf(dereferencePolicyIntValue);
@@ -1145,12 +647,10 @@
                 throw DecodeException.error(ERR_LDAP_SEARCH_REQUEST_DECODE_INVALID_DEREF
                         .get(dereferencePolicyIntValue));
             }
-
             final int sizeLimit = (int) reader.readInteger();
             final int timeLimit = (int) reader.readInteger();
             final boolean typesOnly = reader.readBoolean();
-            final Filter filter = LDAPUtils.decodeFilter(reader);
-
+            final Filter filter = LDAP.readFilter(reader);
             message = Requests.newSearchRequest(baseDN, scope, filter);
             message.setDereferenceAliasesPolicy(dereferencePolicy);
             try {
@@ -1160,7 +660,6 @@
                 throw DecodeException.error(e.getMessageObject());
             }
             message.setTypesOnly(typesOnly);
-
             reader.readStartSequence();
             try {
                 while (reader.hasNextElement()) {
@@ -1172,34 +671,15 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP SEARCH REQUEST(messageID={}, request={})", messageID, message);
-
         handler.searchRequest(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a search result
-     * done protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readSearchResult(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-
-        Result message;
-
-        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_DONE);
+    private void readSearchResult(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_SEARCH_RESULT_DONE);
+        final Result message;
         try {
             final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
             final String matchedDN = reader.readOctetStringAsString();
@@ -1211,96 +691,24 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP SEARCH RESULT(messageID={}, result={})", messageID, message);
-
         handler.searchResult(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as an LDAP search
-     * result entry protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readSearchResultEntry(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        Entry entry;
-
-        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
-        try {
-            final String dnString = reader.readOctetStringAsString();
-            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
-            final DN dn = readDN(dnString, schema);
-            entry = options.getEntryFactory().newEntry(dn);
-
-            reader.readStartSequence();
-            try {
-                while (reader.hasNextElement()) {
-                    reader.readStartSequence();
-                    try {
-                        final String ads = reader.readOctetStringAsString();
-                        final AttributeDescription ad = readAttributeDescription(ads, schema);
-                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
-
-                        reader.readStartSet();
-                        try {
-                            while (reader.hasNextElement()) {
-                                attribute.add(reader.readOctetString());
-                            }
-                            entry.addAttribute(attribute);
-                        } finally {
-                            reader.readEndSet();
-                        }
-                    } finally {
-                        reader.readEndSequence();
-                    }
-                }
-            } finally {
-                reader.readEndSequence();
-            }
-        } finally {
-            reader.readEndSequence();
-        }
-
+    private void readSearchResultEntry(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        final Entry entry = LDAP.readEntry(reader, LDAP.OP_TYPE_SEARCH_RESULT_ENTRY, options);
         final SearchResultEntry message = Responses.newSearchResultEntry(entry);
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP SEARCH RESULT ENTRY(messageID={}, entry={})", messageID, message);
-
         handler.searchResultEntry(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 reader as a search result
-     * reference protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readSearchResultReference(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        SearchResultReference message;
-
-        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_REFERENCE);
+    private void readSearchResultReference(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readStartSequence(LDAP.OP_TYPE_SEARCH_RESULT_REFERENCE);
+        final SearchResultReference message;
         try {
             message = Responses.newSearchResultReference(reader.readOctetStringAsString());
             while (reader.hasNextElement()) {
@@ -1309,39 +717,18 @@
         } finally {
             reader.readEndSequence();
         }
-
         readControls(message);
-
-        IO_LOG.trace("DECODE LDAP SEARCH RESULT REFERENCE(messageID={}, result={})",
-                messageID, message);
-
+        IO_LOG.trace("DECODE LDAP SEARCH RESULT REFERENCE(messageID={}, result={})", messageID,
+                message);
         handler.searchResultReference(messageID, message);
     }
 
-    /**
-     * Decodes the elements from the provided ASN.1 read as an LDAP unbind
-     * request protocol op.
-     *
-     * @param reader
-     *            The ASN.1 reader
-     * @param messageID
-     *            The decoded message ID for this message.
-     * @param handler
-     *            The <code>LDAPMessageHandler</code> that will handle this
-     *            decoded message.
-     * @throws IOException
-     *             If an error occurred while reading bytes to decode.
-     */
-    private void readUnbindRequest(final int messageID,
-            final LDAPMessageHandler handler) throws IOException {
-        UnbindRequest message;
-        reader.readNull(OP_TYPE_UNBIND_REQUEST);
-        message = Requests.newUnbindRequest();
-
+    private void readUnbindRequest(final int messageID, final LDAPMessageHandler handler)
+            throws IOException {
+        reader.readNull(LDAP.OP_TYPE_UNBIND_REQUEST);
+        final UnbindRequest message = Requests.newUnbindRequest();
         readControls(message);
-
         IO_LOG.trace("DECODE LDAP UNBIND REQUEST(messageID={}, request={})", messageID, message);
-
         handler.unbindRequest(messageID, message);
     }
 }
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java
index 1aea357..c63465f 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java
@@ -27,14 +27,12 @@
 
 package org.forgerock.opendj.io;
 
-import static com.forgerock.opendj.ldap.LDAPConstants.*;
 import static com.forgerock.opendj.util.StaticUtils.IO_LOG;
 import static com.forgerock.opendj.util.StaticUtils.byteToHex;
 
 import java.io.IOException;
 import java.util.List;
 
-import org.forgerock.opendj.ldap.Attribute;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.Modification;
@@ -47,143 +45,638 @@
 import org.forgerock.opendj.ldap.requests.GenericBindRequest;
 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
-import org.forgerock.opendj.ldap.requests.Request;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
 import org.forgerock.opendj.ldap.requests.UnbindRequest;
 import org.forgerock.opendj.ldap.responses.BindResult;
 import org.forgerock.opendj.ldap.responses.CompareResult;
 import org.forgerock.opendj.ldap.responses.ExtendedResult;
 import org.forgerock.opendj.ldap.responses.IntermediateResponse;
-import org.forgerock.opendj.ldap.responses.Response;
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 
-import com.forgerock.opendj.ldap.LDAPUtils;
 import com.forgerock.opendj.util.StaticUtils;
 
 /**
- * Responsible for writing LDAP messages.
+ * Writes LDAP messages to an underlying ASN.1 writer.
+ * <p>
+ * Methods for creating {@link LDAPWriter}s are provided in the {@link LDAP}
+ * class.
  *
  * @param <W>
- *            type of ASN1 writer used to encode elements
+ *            The type of ASN.1 writer used for encoding elements.
  */
 public final class LDAPWriter<W extends ASN1Writer> {
+    // @Checkstyle:ignore AvoidNestedBlocks
 
     private final W writer;
 
-    /**
-     * Creates a writer based on the provided ASN1 writer.
-     *
-     * @param asn1Writer
-     *            writer to encode ASN.1 elements
-     */
-    public LDAPWriter(W asn1Writer) {
+    LDAPWriter(final W asn1Writer) {
         this.writer = asn1Writer;
     }
 
     /**
-     * Returns the ASN1 writer used to encode elements.
+     * Returns the ASN.1 writer to which LDAP messages will be written.
      *
-     * @return the ASN1 writer
+     * @return The ASN.1 writer to which LDAP messages will be written.
      */
     public W getASN1Writer() {
         return writer;
     }
 
     /**
-     * Write the provided control.
+     * Writes the provided abandon request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeAbandonRequest(final int messageID, final AbandonRequest request)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP ABANDON REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeInteger(LDAP.OP_TYPE_ABANDON_REQUEST, request.getRequestID());
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes the provided add request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeAddRequest(final int messageID, final AddRequest request) throws IOException {
+        IO_LOG.trace("ENCODE LDAP ADD REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            LDAP.writeEntry(writer, LDAP.OP_TYPE_ADD_REQUEST, request);
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes the provided add result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeAddResult(final int messageID, final Result result) throws IOException {
+        IO_LOG.trace("ENCODE LDAP ADD RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_ADD_RESPONSE, result);
+            writeResultFooter(writer);
+        }
+        writeMessageFooter(result.getControls());
+    }
+
+    /**
+     * Writes the provided bind request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param version
+     *            The requested LDAP protocol version.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeBindRequest(final int messageID, final int version,
+            final GenericBindRequest request) throws IOException {
+        IO_LOG.trace("ENCODE LDAP BIND REQUEST(messageID={}, auth=0x{}, request={})", messageID,
+                byteToHex(request.getAuthenticationType()), request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_BIND_REQUEST);
+            {
+                writer.writeInteger(version);
+                writer.writeOctetString(request.getName());
+                writer.writeOctetString(request.getAuthenticationType(), request
+                        .getAuthenticationValue());
+            }
+            writer.writeEndSequence();
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes the provided bind result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeBindResult(final int messageID, final BindResult result) throws IOException {
+        IO_LOG.trace("ENCODE LDAP BIND RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_BIND_RESPONSE, result);
+            {
+                final ByteString saslCredentials = result.getServerSASLCredentials();
+                if (saslCredentials != null && saslCredentials.length() > 0) {
+                    writer.writeOctetString(LDAP.TYPE_SERVER_SASL_CREDENTIALS, result
+                            .getServerSASLCredentials());
+                }
+            }
+            writeResultFooter(writer);
+        }
+        writeMessageFooter(result.getControls());
+    }
+
+    /**
+     * Writes the provided compare request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeCompareRequest(final int messageID, final CompareRequest request)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP COMPARE REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_COMPARE_REQUEST);
+            {
+                writer.writeOctetString(request.getName().toString());
+                writer.writeStartSequence();
+                {
+                    writer.writeOctetString(request.getAttributeDescription().toString());
+                    writer.writeOctetString(request.getAssertionValue());
+                }
+                writer.writeEndSequence();
+            }
+            writer.writeEndSequence();
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes the provided compare result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeCompareResult(final int messageID, final CompareResult result)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP COMPARE RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_COMPARE_RESPONSE, result);
+            writeResultFooter(writer);
+        }
+        writeMessageFooter(result.getControls());
+    }
+
+    /**
+     * Writes the provided control.
      *
      * @param control
-     *            the control
+     *            The control.
      * @throws IOException
-     *             if an error occurs during write operation
+     *             If an unexpected IO error occurred.
      */
-    public void writeControl(final Control control)
-            throws IOException {
+    public void writeControl(final Control control) throws IOException {
         writer.writeStartSequence();
-        writer.writeOctetString(control.getOID());
-        if (control.isCritical()) {
-            writer.writeBoolean(control.isCritical());
-        }
-        if (control.getValue() != null) {
-            writer.writeOctetString(control.getValue());
+        {
+            writer.writeOctetString(control.getOID());
+            if (control.isCritical()) {
+                writer.writeBoolean(control.isCritical());
+            }
+            if (control.getValue() != null) {
+                writer.writeOctetString(control.getValue());
+            }
         }
         writer.writeEndSequence();
     }
 
     /**
-     * Write the provided entry.
+     * Writes the provided delete request.
      *
-     * @param searchResultEntry
-     *            entry
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
      * @throws IOException
-     *             if an error occurs during write operation
+     *             If an unexpected IO error occurred.
      */
-    public void writeEntry(final SearchResultEntry searchResultEntry) throws IOException {
-        writer.writeStartSequence(OP_TYPE_SEARCH_RESULT_ENTRY);
-        writer.writeOctetString(searchResultEntry.getName().toString());
-
-        writer.writeStartSequence();
-        for (final Attribute attr : searchResultEntry.getAllAttributes()) {
-            writeAttribute(attr);
+    public void writeDeleteRequest(final int messageID, final DeleteRequest request)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP DELETE REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeOctetString(LDAP.OP_TYPE_DELETE_REQUEST, request.getName().toString());
         }
-        writer.writeEndSequence();
-        writer.writeEndSequence();
+        writeMessageFooter(request.getControls());
     }
 
-    private void writeAttribute(final Attribute attribute)
-            throws IOException {
-        writer.writeStartSequence();
-        writer.writeOctetString(attribute.getAttributeDescriptionAsString());
-
-        writer.writeStartSet();
-        for (final ByteString value : attribute) {
-            writer.writeOctetString(value);
+    /**
+     * Writes the provided delete result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeDeleteResult(final int messageID, final Result result) throws IOException {
+        IO_LOG.trace("ENCODE LDAP DELETE RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_DELETE_RESPONSE, result);
+            writeResultFooter(writer);
         }
-        writer.writeEndSequence();
-
-        writer.writeEndSequence();
+        writeMessageFooter(result.getControls());
     }
 
-    private void writeChange(final Modification change)
+    /**
+     * Writes the provided extended request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeExtendedRequest(final int messageID, final ExtendedRequest<?> request)
             throws IOException {
-        writer.writeStartSequence();
-        writer.writeEnumerated(change.getModificationType().intValue());
-        writeAttribute(change.getAttribute());
-        writer.writeEndSequence();
-    }
-
-    private void writeMessageFooter(final Request request)
-            throws IOException {
-        final List<Control> controls = request.getControls();
-        if (!controls.isEmpty()) {
-            writer.writeStartSequence(TYPE_CONTROL_SEQUENCE);
-            for (final Control control : controls) {
-                writeControl(control);
+        IO_LOG.trace("ENCODE LDAP EXTENDED REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_EXTENDED_REQUEST);
+            {
+                writer.writeOctetString(LDAP.TYPE_EXTENDED_REQUEST_OID, request.getOID());
+                final ByteString requestValue = request.getValue();
+                if (requestValue != null) {
+                    writer.writeOctetString(LDAP.TYPE_EXTENDED_REQUEST_VALUE, requestValue);
+                }
             }
             writer.writeEndSequence();
         }
-
-        writer.writeEndSequence();
+        writeMessageFooter(request.getControls());
     }
 
-    private void writeMessageFooter(final Response response)
+    /**
+     * Writes the provided extended result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeExtendedResult(final int messageID, final ExtendedResult result)
             throws IOException {
-        final List<Control> controls = response.getControls();
-        if (!controls.isEmpty()) {
-            writer.writeStartSequence(TYPE_CONTROL_SEQUENCE);
-            for (final Control control : controls) {
-                writeControl(control);
+        IO_LOG.trace("ENCODE LDAP EXTENDED RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_EXTENDED_RESPONSE, result);
+            {
+                final String responseName = result.getOID();
+                if (responseName != null) {
+                    writer.writeOctetString(LDAP.TYPE_EXTENDED_RESPONSE_OID, responseName);
+                }
+                final ByteString responseValue = result.getValue();
+                if (responseValue != null) {
+                    writer.writeOctetString(LDAP.TYPE_EXTENDED_RESPONSE_VALUE, responseValue);
+                }
+            }
+            writeResultFooter(writer);
+        }
+        writeMessageFooter(result.getControls());
+    }
+
+    /**
+     * Writes the provided intermediate response.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param response
+     *            The response.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeIntermediateResponse(final int messageID, final IntermediateResponse response)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP INTERMEDIATE RESPONSE(messageID={}, response={})", messageID,
+                response);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_INTERMEDIATE_RESPONSE);
+            {
+                final String responseName = response.getOID();
+                if (responseName != null) {
+                    writer.writeOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_OID, response.getOID());
+                }
+                final ByteString responseValue = response.getValue();
+                if (responseValue != null) {
+                    writer.writeOctetString(LDAP.TYPE_INTERMEDIATE_RESPONSE_VALUE, response
+                            .getValue());
+                }
             }
             writer.writeEndSequence();
         }
+        writeMessageFooter(response.getControls());
+    }
 
+    /**
+     * Writes the provided modify DN request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeModifyDNRequest(final int messageID, final ModifyDNRequest request)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP MODIFY DN REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_MODIFY_DN_REQUEST);
+            {
+                writer.writeOctetString(request.getName().toString());
+                writer.writeOctetString(request.getNewRDN().toString());
+                writer.writeBoolean(request.isDeleteOldRDN());
+                final DN newSuperior = request.getNewSuperior();
+                if (newSuperior != null) {
+                    writer.writeOctetString(LDAP.TYPE_MODIFY_DN_NEW_SUPERIOR, newSuperior
+                            .toString());
+                }
+            }
+            writer.writeEndSequence();
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes the provided modify DN result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeModifyDNResult(final int messageID, final Result result) throws IOException {
+        IO_LOG.trace("ENCODE LDAP MODIFY DN RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_MODIFY_DN_RESPONSE, result);
+            writeResultFooter(writer);
+        }
+        writeMessageFooter(result.getControls());
+    }
+
+    /**
+     * Writes the provided modify request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeModifyRequest(final int messageID, final ModifyRequest request)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP MODIFY REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_MODIFY_REQUEST);
+            {
+                writer.writeOctetString(request.getName().toString());
+                writer.writeStartSequence();
+                {
+                    for (final Modification change : request.getModifications()) {
+                        writeChange(change);
+                    }
+                }
+                writer.writeEndSequence();
+            }
+            writer.writeEndSequence();
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes the provided extended result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeModifyResult(final int messageID, final Result result) throws IOException {
+        IO_LOG.trace("ENCODE LDAP MODIFY RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_MODIFY_RESPONSE, result);
+            writeResultFooter(writer);
+        }
+        writeMessageFooter(result.getControls());
+    }
+
+    /**
+     * Writes the provided search request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeSearchRequest(final int messageID, final SearchRequest request)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP SEARCH REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_SEARCH_REQUEST);
+            {
+                writer.writeOctetString(request.getName().toString());
+                writer.writeEnumerated(request.getScope().intValue());
+                writer.writeEnumerated(request.getDereferenceAliasesPolicy().intValue());
+                writer.writeInteger(request.getSizeLimit());
+                writer.writeInteger(request.getTimeLimit());
+                writer.writeBoolean(request.isTypesOnly());
+                LDAP.writeFilter(writer, request.getFilter());
+                writer.writeStartSequence();
+                {
+                    for (final String attribute : request.getAttributes()) {
+                        writer.writeOctetString(attribute);
+                    }
+                }
+                writer.writeEndSequence();
+            }
+            writer.writeEndSequence();
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes the provided search result.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param result
+     *            The result.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeSearchResult(final int messageID, final Result result) throws IOException {
+        IO_LOG.trace("ENCODE LDAP SEARCH RESULT(messageID={}, result={})", messageID, result);
+        writeMessageHeader(messageID);
+        {
+            writeResultHeader(LDAP.OP_TYPE_SEARCH_RESULT_DONE, result);
+            writeResultFooter(writer);
+        }
+        writeMessageFooter(result.getControls());
+    }
+
+    /**
+     * Writes the provided search result entry.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param entry
+     *            The entry.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeSearchResultEntry(final int messageID, final SearchResultEntry entry)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP SEARCH RESULT ENTRY(messageID={}, entry={})", messageID, entry);
+        writeMessageHeader(messageID);
+        {
+            LDAP.writeEntry(writer, LDAP.OP_TYPE_SEARCH_RESULT_ENTRY, entry);
+        }
+        writeMessageFooter(entry.getControls());
+    }
+
+    /**
+     * Writes the provided search result reference.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param reference
+     *            The reference.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeSearchResultReference(final int messageID,
+            final SearchResultReference reference) throws IOException {
+        IO_LOG.trace("ENCODE LDAP SEARCH RESULT REFERENCE(messageID={}, reference={})", messageID,
+                reference);
+        writeMessageHeader(messageID);
+        {
+            writer.writeStartSequence(LDAP.OP_TYPE_SEARCH_RESULT_REFERENCE);
+            {
+                for (final String url : reference.getURIs()) {
+                    writer.writeOctetString(url);
+                }
+            }
+            writer.writeEndSequence();
+        }
+        writeMessageFooter(reference.getControls());
+    }
+
+    /**
+     * Writes the provided unbind request.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param request
+     *            The request.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeUnbindRequest(final int messageID, final UnbindRequest request)
+            throws IOException {
+        IO_LOG.trace("ENCODE LDAP UNBIND REQUEST(messageID={}, request={})", messageID, request);
+        writeMessageHeader(messageID);
+        {
+            writer.writeNull(LDAP.OP_TYPE_UNBIND_REQUEST);
+        }
+        writeMessageFooter(request.getControls());
+    }
+
+    /**
+     * Writes a message with the provided id, tag and content bytes.
+     *
+     * @param messageID
+     *            The LDAP message ID.
+     * @param messageTag
+     *            The LDAP message type.
+     * @param messageBytes
+     *            The contents of the LDAP message.
+     * @throws IOException
+     *             If an unexpected IO error occurred.
+     */
+    public void writeUnrecognizedMessage(final int messageID, final byte messageTag,
+            final ByteString messageBytes) throws IOException {
+        IO_LOG.trace("ENCODE LDAP UNKNOWN MESSAGE(messageID={}, messageTag={}, messageBytes={})",
+                messageID, StaticUtils.byteToHex(messageTag), messageBytes);
+        writeMessageHeader(messageID);
+        {
+            writer.writeOctetString(messageTag, messageBytes);
+        }
         writer.writeEndSequence();
     }
 
-    private void writeMessageHeader(final int messageID)
-            throws IOException {
+    private void writeChange(final Modification change) throws IOException {
+        writer.writeStartSequence();
+        {
+            writer.writeEnumerated(change.getModificationType().intValue());
+            LDAP.writeAttribute(writer, change.getAttribute());
+        }
+        writer.writeEndSequence();
+    }
+
+    private void writeMessageFooter(final List<Control> controls) throws IOException {
+        if (!controls.isEmpty()) {
+            writer.writeStartSequence(LDAP.TYPE_CONTROL_SEQUENCE);
+            {
+                for (final Control control : controls) {
+                    writeControl(control);
+                }
+            }
+            writer.writeEndSequence();
+        }
+        writer.writeEndSequence();
+    }
+
+    private void writeMessageHeader(final int messageID) throws IOException {
         writer.writeStartSequence();
         writer.writeInteger(messageID);
     }
@@ -192,532 +685,20 @@
         writer.writeEndSequence();
     }
 
-    private void writeResultHeader(final byte typeTag,
-            final Result rawMessage) throws IOException {
+    private void writeResultHeader(final byte typeTag, final Result rawMessage) throws IOException {
         writer.writeStartSequence(typeTag);
         writer.writeEnumerated(rawMessage.getResultCode().intValue());
         writer.writeOctetString(rawMessage.getMatchedDN());
         writer.writeOctetString(rawMessage.getDiagnosticMessage());
-
         final List<String> referralURIs = rawMessage.getReferralURIs();
         if (!referralURIs.isEmpty()) {
-            writer.writeStartSequence(TYPE_REFERRAL_SEQUENCE);
-            for (final String s : referralURIs) {
-                writer.writeOctetString(s);
+            writer.writeStartSequence(LDAP.TYPE_REFERRAL_SEQUENCE);
+            {
+                for (final String s : referralURIs) {
+                    writer.writeOctetString(s);
+                }
             }
             writer.writeEndSequence();
         }
     }
-
-    /**
-     * Write the provided abandon request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeAbandonRequest(final int messageID, final AbandonRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP ABANDON REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeInteger(OP_TYPE_ABANDON_REQUEST, request.getRequestID());
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided add request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeAddRequest(final int messageID, final AddRequest request)
-            throws IOException {
-        IO_LOG.trace("ENCODE LDAP ADD REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_ADD_REQUEST);
-        writer.writeOctetString(request.getName().toString());
-
-        // Write the attributes
-        writer.writeStartSequence();
-        for (final Attribute attr : request.getAllAttributes()) {
-            writeAttribute(attr);
-        }
-        writer.writeEndSequence();
-
-        writer.writeEndSequence();
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided add result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeAddResult(final int messageID, final Result result)
-            throws IOException {
-        IO_LOG.trace("ENCODE LDAP ADD RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_ADD_RESPONSE, result);
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided bind request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param version
-     *            version of the protocol
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeBindRequest(final int messageID, final int version,
-            final GenericBindRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP BIND REQUEST(messageID={}, auth=0x{}, request={})",
-                messageID, byteToHex(request.getAuthenticationType()), request);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_BIND_REQUEST);
-
-        writer.writeInteger(version);
-        writer.writeOctetString(request.getName());
-        writer.writeOctetString(request.getAuthenticationType(), request.getAuthenticationValue());
-
-        writer.writeEndSequence();
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided bind result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeBindResult(final int messageID, final BindResult result)
-            throws IOException {
-        IO_LOG.trace("ENCODE LDAP BIND RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_BIND_RESPONSE, result);
-
-        final ByteString saslCredentials = result.getServerSASLCredentials();
-        if (saslCredentials != null && saslCredentials.length() > 0) {
-            writer.writeOctetString(TYPE_SERVER_SASL_CREDENTIALS, result.getServerSASLCredentials());
-        }
-
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided compare request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeCompareRequest(final int messageID,
-            final CompareRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP COMPARE REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_COMPARE_REQUEST);
-        writer.writeOctetString(request.getName().toString());
-
-        writer.writeStartSequence();
-        writer.writeOctetString(request.getAttributeDescription().toString());
-        writer.writeOctetString(request.getAssertionValue());
-        writer.writeEndSequence();
-
-        writer.writeEndSequence();
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided compare result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeCompareResult(final int messageID,
-            final CompareResult result) throws IOException {
-        IO_LOG.trace("ENCODE LDAP COMPARE RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_COMPARE_RESPONSE, result);
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided delete request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeDeleteRequest(final int messageID,
-            final DeleteRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP DELETE REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeOctetString(OP_TYPE_DELETE_REQUEST, request.getName().toString());
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided delete result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeDeleteResult(final int messageID, final Result result)
-            throws IOException {
-        IO_LOG.trace("ENCODE LDAP DELETE RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_DELETE_RESPONSE, result);
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided extended request.
-     *
-     * @param <R>
-     *            type of extended result returned by the request
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public <R extends ExtendedResult> void writeExtendedRequest(final int messageID,
-            final ExtendedRequest<R> request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP EXTENDED REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_EXTENDED_REQUEST);
-        writer.writeOctetString(TYPE_EXTENDED_REQUEST_OID, request.getOID());
-
-        final ByteString requestValue = request.getValue();
-        if (requestValue != null) {
-            writer.writeOctetString(TYPE_EXTENDED_REQUEST_VALUE, requestValue);
-        }
-
-        writer.writeEndSequence();
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided extended result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeExtendedResult(final int messageID,
-            final ExtendedResult result) throws IOException {
-        IO_LOG.trace("ENCODE LDAP EXTENDED RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_EXTENDED_RESPONSE, result);
-
-        final String responseName = result.getOID();
-        final ByteString responseValue = result.getValue();
-
-        if (responseName != null) {
-            writer.writeOctetString(TYPE_EXTENDED_RESPONSE_OID, responseName);
-        }
-
-        if (responseValue != null) {
-            writer.writeOctetString(TYPE_EXTENDED_RESPONSE_VALUE, responseValue);
-        }
-
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided intermediate response.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param response
-     *            the response
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeIntermediateResponse(final int messageID,
-            final IntermediateResponse response) throws IOException {
-        IO_LOG.trace("ENCODE LDAP INTERMEDIATE RESPONSE(messageID={}, response={})", messageID, response);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_INTERMEDIATE_RESPONSE);
-
-        final String responseName = response.getOID();
-        final ByteString responseValue = response.getValue();
-
-        if (responseName != null) {
-            writer.writeOctetString(TYPE_INTERMEDIATE_RESPONSE_OID, response.getOID());
-        }
-
-        if (responseValue != null) {
-            writer.writeOctetString(TYPE_INTERMEDIATE_RESPONSE_VALUE, response.getValue());
-        }
-
-        writer.writeEndSequence();
-        writeMessageFooter(response);
-    }
-
-    /**
-     * Write the provided modify DN request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeModifyDNRequest(final int messageID,
-            final ModifyDNRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP MODIFY DN REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_MODIFY_DN_REQUEST);
-        writer.writeOctetString(request.getName().toString());
-        writer.writeOctetString(request.getNewRDN().toString());
-        writer.writeBoolean(request.isDeleteOldRDN());
-
-        final DN newSuperior = request.getNewSuperior();
-        if (newSuperior != null) {
-            writer.writeOctetString(TYPE_MODIFY_DN_NEW_SUPERIOR, newSuperior.toString());
-        }
-
-        writer.writeEndSequence();
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided modify DN result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeModifyDNResult(final int messageID, final Result result)
-            throws IOException {
-        IO_LOG.trace("ENCODE LDAP MODIFY DN RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_MODIFY_DN_RESPONSE, result);
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided modify request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeModifyRequest(final int messageID,
-            final ModifyRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP MODIFY REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_MODIFY_REQUEST);
-        writer.writeOctetString(request.getName().toString());
-
-        writer.writeStartSequence();
-        for (final Modification change : request.getModifications()) {
-            writeChange(change);
-        }
-        writer.writeEndSequence();
-
-        writer.writeEndSequence();
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided extended result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeModifyResult(final int messageID, final Result result)
-            throws IOException {
-        IO_LOG.trace("ENCODE LDAP MODIFY RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_MODIFY_RESPONSE, result);
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided search request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeSearchRequest(final int messageID,
-            final SearchRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP SEARCH REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_SEARCH_REQUEST);
-        writer.writeOctetString(request.getName().toString());
-        writer.writeEnumerated(request.getScope().intValue());
-        writer.writeEnumerated(request.getDereferenceAliasesPolicy().intValue());
-        writer.writeInteger(request.getSizeLimit());
-        writer.writeInteger(request.getTimeLimit());
-        writer.writeBoolean(request.isTypesOnly());
-        LDAPUtils.encodeFilter(writer, request.getFilter());
-
-        writer.writeStartSequence();
-        for (final String attribute : request.getAttributes()) {
-            writer.writeOctetString(attribute);
-        }
-        writer.writeEndSequence();
-
-        writer.writeEndSequence();
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write the provided search result.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param result
-     *            the result
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeSearchResult(final int messageID, final Result result)
-            throws IOException {
-        IO_LOG.trace("ENCODE LDAP SEARCH RESULT(messageID={}, result={})", messageID, result);
-        writeMessageHeader(messageID);
-        writeResultHeader(OP_TYPE_SEARCH_RESULT_DONE, result);
-        writeResultFooter(writer);
-        writeMessageFooter(result);
-    }
-
-    /**
-     * Write the provided search result entry.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param entry
-     *            the entry
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeSearchResultEntry(final int messageID,
-            final SearchResultEntry entry) throws IOException {
-        IO_LOG.trace("ENCODE LDAP SEARCH RESULT ENTRY(messageID={}, entry={})", messageID, entry);
-        writeMessageHeader(messageID);
-        writeEntry(entry);
-        writeMessageFooter(entry);
-    }
-
-    /**
-     * Write the provided search result reference.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param reference
-     *            the reference
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeSearchResultReference(final int messageID,
-            final SearchResultReference reference) throws IOException {
-        IO_LOG.trace("ENCODE LDAP SEARCH RESULT REFERENCE(messageID={}, reference={})", messageID, reference);
-        writeMessageHeader(messageID);
-        writer.writeStartSequence(OP_TYPE_SEARCH_RESULT_REFERENCE);
-        for (final String url : reference.getURIs()) {
-            writer.writeOctetString(url);
-        }
-        writer.writeEndSequence();
-        writeMessageFooter(reference);
-    }
-
-    /**
-     * Write the provided unbind request.
-     *
-     * @param messageID
-     *            identifier of the request message
-     * @param request
-     *            the request
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeUnbindRequest(final int messageID,
-            final UnbindRequest request) throws IOException {
-        IO_LOG.trace("ENCODE LDAP UNBIND REQUEST(messageID={}, request={})", messageID, request);
-        writeMessageHeader(messageID);
-        writer.writeNull(OP_TYPE_UNBIND_REQUEST);
-        writeMessageFooter(request);
-    }
-
-    /**
-     * Write a message with the provided id, tag and content bytes.
-     *
-     * @param messageID
-     *            identifier of the result message
-     * @param messageTag
-     *            tag identifying the type of message
-     * @param messageBytes
-     *            content of message
-     * @throws IOException
-     *             if an error occurs during write operation
-     */
-    public void writeUnrecognizedMessage(final int messageID,
-            final byte messageTag, final ByteString messageBytes) throws IOException {
-        IO_LOG.trace("ENCODE LDAP UNKNOWN MESSAGE(messageID={}, messageTag={}, messageBytes={})",
-                messageID, StaticUtils.byteToHex(messageTag), messageBytes);
-        writeMessageHeader(messageID);
-        writer.writeOctetString(messageTag, messageBytes);
-        writer.writeEndSequence();
-    }
 }
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/package-info.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/package-info.java
index ea6e4c4..ad6a681 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/package-info.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/package-info.java
@@ -27,7 +27,8 @@
 /**
  * Classes and interfaces providing I/O functionality.
  * <p>
- * It includes facilities for encoding and decoding ASN.1 data streams.
+ * It includes facilities for encoding and decoding ASN.1 data streams, as
+ * well as LDAP protocol messages.
  * <p>
  * Note that this particular implementation is limited to the subset of elements
  * that are typically used by LDAP clients. As such, it does not include all
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/AssertionRequestControl.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/AssertionRequestControl.java
index 00fadea..a88e22c 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/AssertionRequestControl.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/AssertionRequestControl.java
@@ -27,10 +27,10 @@
 
 package org.forgerock.opendj.ldap.controls;
 
-import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
 import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAPASSERT_CONTROL_BAD_OID;
 import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAPASSERT_INVALID_CONTROL_VALUE;
 import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAPASSERT_NO_CONTROL_VALUE;
+import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
 
 import java.io.IOException;
 
@@ -38,13 +38,13 @@
 import org.forgerock.opendj.io.ASN1;
 import org.forgerock.opendj.io.ASN1Reader;
 import org.forgerock.opendj.io.ASN1Writer;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ByteStringBuilder;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
 import org.forgerock.opendj.ldap.Filter;
 
-import com.forgerock.opendj.ldap.LDAPUtils;
 import com.forgerock.opendj.util.Validator;
 
 /**
@@ -109,16 +109,14 @@
                         throw DecodeException.error(message);
                     }
 
-                    final ASN1Reader reader = ASN1.getReader(control.getValue());
-                    Filter filter;
                     try {
-                        filter = LDAPUtils.decodeFilter(reader);
+                        final ASN1Reader reader = ASN1.getReader(control.getValue());
+                        final Filter filter = LDAP.readFilter(reader);
+                        return new AssertionRequestControl(control.isCritical(), filter);
                     } catch (final IOException e) {
                         throw DecodeException.error(ERR_LDAPASSERT_INVALID_CONTROL_VALUE
                                 .get(getExceptionMessage(e)), e);
                     }
-
-                    return new AssertionRequestControl(control.isCritical(), filter);
                 }
 
                 public String getOID() {
@@ -179,7 +177,7 @@
         final ByteStringBuilder buffer = new ByteStringBuilder();
         final ASN1Writer writer = ASN1.getWriter(buffer);
         try {
-            LDAPUtils.encodeFilter(writer, filter);
+            LDAP.writeFilter(writer, filter);
             return buffer.toByteString();
         } catch (final IOException ioe) {
             // This should never happen unless there is a bug somewhere.
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/MatchedValuesRequestControl.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/MatchedValuesRequestControl.java
index 784f71a..970d6b8 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/MatchedValuesRequestControl.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/MatchedValuesRequestControl.java
@@ -27,8 +27,8 @@
 
 package org.forgerock.opendj.ldap.controls;
 
-import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
 import static com.forgerock.opendj.ldap.CoreMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -42,6 +42,7 @@
 import org.forgerock.opendj.io.ASN1;
 import org.forgerock.opendj.io.ASN1Reader;
 import org.forgerock.opendj.io.ASN1Writer;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.ldap.AbstractFilterVisitor;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ByteStringBuilder;
@@ -49,7 +50,6 @@
 import org.forgerock.opendj.ldap.DecodeOptions;
 import org.forgerock.opendj.ldap.Filter;
 
-import com.forgerock.opendj.ldap.LDAPUtils;
 import com.forgerock.opendj.util.StaticUtils;
 import com.forgerock.opendj.util.Validator;
 
@@ -191,14 +191,12 @@
 
                         final LinkedList<Filter> filters = new LinkedList<Filter>();
                         do {
-                            final Filter filter = LDAPUtils.decodeFilter(reader);
-
+                            final Filter filter = LDAP.readFilter(reader);
                             try {
                                 validateFilter(filter);
                             } catch (final LocalizedIllegalArgumentException e) {
                                 throw DecodeException.error(e.getMessageObject());
                             }
-
                             filters.add(filter);
                         } while (reader.hasNextElement());
 
@@ -340,7 +338,7 @@
         try {
             writer.writeStartSequence();
             for (final Filter f : filters) {
-                LDAPUtils.encodeFilter(writer, f);
+                LDAP.writeFilter(writer, f);
             }
             writer.writeEndSequence();
             return buffer.toByteString();
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PostReadResponseControl.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PostReadResponseControl.java
index 8b99e12..05efac4 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PostReadResponseControl.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PostReadResponseControl.java
@@ -27,26 +27,21 @@
 
 package org.forgerock.opendj.ldap.controls;
 
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_POSTREADRESP_CANNOT_DECODE_VALUE;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_POSTREADRESP_NO_CONTROL_VALUE;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_POSTREAD_CONTROL_BAD_OID;
+import static com.forgerock.opendj.ldap.CoreMessages.*;
 
 import java.io.IOException;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.opendj.io.ASN1;
 import org.forgerock.opendj.io.ASN1Reader;
-import org.forgerock.opendj.io.ASN1Writer;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ByteStringBuilder;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
 import org.forgerock.opendj.ldap.Entries;
 import org.forgerock.opendj.ldap.Entry;
-import org.forgerock.opendj.ldap.responses.Responses;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 
-import com.forgerock.opendj.ldap.LDAPUtils;
 import com.forgerock.opendj.util.StaticUtils;
 import com.forgerock.opendj.util.Validator;
 
@@ -116,12 +111,11 @@
                     }
 
                     final ASN1Reader reader = ASN1.getReader(control.getValue());
-                    SearchResultEntry searchEntry;
+                    final Entry entry;
                     try {
-                        searchEntry = LDAPUtils.decodeSearchResultEntry(reader, options);
+                        entry = LDAP.readEntry(reader, options);
                     } catch (final IOException le) {
                         StaticUtils.CONTROLS_LOG.debug("Unable to read result entry ", le);
-
                         final LocalizableMessage message =
                                 ERR_POSTREADRESP_CANNOT_DECODE_VALUE.get(le.getMessage());
                         throw DecodeException.error(message, le);
@@ -134,7 +128,7 @@
                      * controls?
                      */
                     return new PostReadResponseControl(control.isCritical(), Entries
-                            .unmodifiableEntry(searchEntry));
+                            .unmodifiableEntry(entry));
                 }
 
                 public String getOID() {
@@ -195,10 +189,9 @@
      * {@inheritDoc}
      */
     public ByteString getValue() {
-        final ByteStringBuilder buffer = new ByteStringBuilder();
-        final ASN1Writer writer = ASN1.getWriter(buffer);
         try {
-            LDAPUtils.encodeSearchResultEntry(writer, Responses.newSearchResultEntry(entry));
+            final ByteStringBuilder buffer = new ByteStringBuilder();
+            LDAP.writeEntry(ASN1.getWriter(buffer), entry);
             return buffer.toByteString();
         } catch (final IOException ioe) {
             // This should never happen unless there is a bug somewhere.
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PreReadResponseControl.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PreReadResponseControl.java
index 74629fe..f4e6694 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PreReadResponseControl.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PreReadResponseControl.java
@@ -27,26 +27,21 @@
 
 package org.forgerock.opendj.ldap.controls;
 
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_PREREADRESP_CANNOT_DECODE_VALUE;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_PREREADRESP_NO_CONTROL_VALUE;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_PREREAD_CONTROL_BAD_OID;
+import static com.forgerock.opendj.ldap.CoreMessages.*;
 
 import java.io.IOException;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.opendj.io.ASN1;
 import org.forgerock.opendj.io.ASN1Reader;
-import org.forgerock.opendj.io.ASN1Writer;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.ByteStringBuilder;
 import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.DecodeOptions;
 import org.forgerock.opendj.ldap.Entries;
 import org.forgerock.opendj.ldap.Entry;
-import org.forgerock.opendj.ldap.responses.Responses;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 
-import com.forgerock.opendj.ldap.LDAPUtils;
 import com.forgerock.opendj.util.StaticUtils;
 import com.forgerock.opendj.util.Validator;
 
@@ -115,12 +110,11 @@
                     }
 
                     final ASN1Reader reader = ASN1.getReader(control.getValue());
-                    SearchResultEntry searchEntry;
+                    final Entry entry;
                     try {
-                        searchEntry = LDAPUtils.decodeSearchResultEntry(reader, options);
+                        entry = LDAP.readEntry(reader, options);
                     } catch (final IOException le) {
                         StaticUtils.CONTROLS_LOG.debug("Unable to read result entry", le);
-
                         final LocalizableMessage message =
                                 ERR_PREREADRESP_CANNOT_DECODE_VALUE.get(le.getMessage());
                         throw DecodeException.error(message, le);
@@ -133,7 +127,7 @@
                      * controls?
                      */
                     return new PreReadResponseControl(control.isCritical(), Entries
-                            .unmodifiableEntry(searchEntry));
+                            .unmodifiableEntry(entry));
                 }
 
                 public String getOID() {
@@ -194,10 +188,9 @@
      * {@inheritDoc}
      */
     public ByteString getValue() {
-        final ByteStringBuilder buffer = new ByteStringBuilder();
-        final ASN1Writer writer = ASN1.getWriter(buffer);
         try {
-            LDAPUtils.encodeSearchResultEntry(writer, Responses.newSearchResultEntry(entry));
+            final ByteStringBuilder buffer = new ByteStringBuilder();
+            LDAP.writeEntry(ASN1.getWriter(buffer), entry);
             return buffer.toByteString();
         } catch (final IOException ioe) {
             // This should never happen unless there is a bug somewhere.
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/AbstractSASLBindRequest.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/AbstractSASLBindRequest.java
index b04c2ba..4e6bfe1 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/AbstractSASLBindRequest.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/AbstractSASLBindRequest.java
@@ -22,11 +22,12 @@
  *
  *
  *      Copyright 2010 Sun Microsystems, Inc.
+ *      Portions copyright 2013 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.requests;
 
-import static com.forgerock.opendj.ldap.LDAPConstants.TYPE_AUTHENTICATION_SASL;
+import org.forgerock.opendj.io.LDAP;
 
 /**
  * An abstract SASL Bind request which can be used as the basis for implementing
@@ -48,7 +49,7 @@
 
     @Override
     public final byte getAuthenticationType() {
-        return TYPE_AUTHENTICATION_SASL;
+        return LDAP.TYPE_AUTHENTICATION_SASL;
     }
 
     @Override
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestImpl.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestImpl.java
index 055c759..6f30a2f 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestImpl.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestImpl.java
@@ -27,8 +27,7 @@
 
 package org.forgerock.opendj.ldap.requests;
 
-import static com.forgerock.opendj.ldap.LDAPConstants.TYPE_AUTHENTICATION_SIMPLE;
-
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.ldap.ErrorResultException;
 
 import com.forgerock.opendj.util.StaticUtils;
@@ -60,7 +59,7 @@
 
     @Override
     public byte getAuthenticationType() {
-        return TYPE_AUTHENTICATION_SIMPLE;
+        return LDAP.TYPE_AUTHENTICATION_SIMPLE;
     }
 
     @Override
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java
index bac6a05..a7812d0 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java
@@ -58,9 +58,6 @@
     // The attribute usage for this attribute type.
     private final AttributeUsage attributeUsage;
 
-    // The definition string used to create this objectclass.
-    private final String definition;
-
     // The equality matching rule for this attribute type.
     private final String equalityMatchingRuleOID;
 
@@ -137,7 +134,7 @@
             final boolean collective, final boolean noUserModification,
             final AttributeUsage attributeUsage, final Map<String, List<String>> extraProperties,
             final String definition) {
-        super(description, extraProperties);
+        super(description, extraProperties, definition);
 
         Validator.ensureNotNull(oid, names, description, attributeUsage);
         Validator.ensureTrue(superiorType != null || syntax != null,
@@ -157,13 +154,6 @@
         this.isCollective = collective;
         this.isNoUserModification = noUserModification;
         this.attributeUsage = attributeUsage;
-
-        if (definition != null) {
-            this.definition = definition;
-        } else {
-            this.definition = buildDefinition();
-        }
-
         this.isObjectClassType = oid.equals("2.5.4.0");
         this.isPlaceHolder = false;
         this.normalizedName = StaticUtils.toLowerCase(getNameOrOID());
@@ -179,8 +169,6 @@
      *            The name of the place-holder attribute type.
      */
     AttributeType(final String name) {
-        super("", Collections.<String, List<String>> emptyMap());
-
         final StringBuilder builder = new StringBuilder(name.length() + 4);
         StaticUtils.toLowerCase(name, builder);
         builder.append("-oid");
@@ -201,7 +189,6 @@
         this.isCollective = false;
         this.isNoUserModification = false;
         this.attributeUsage = AttributeUsage.USER_APPLICATIONS;
-        this.definition = buildDefinition();
         this.isObjectClassType = false;
         this.isPlaceHolder = true;
         this.normalizedName = StaticUtils.toLowerCase(getNameOrOID());
@@ -564,23 +551,11 @@
         }
     }
 
-    /**
-     * Returns the string representation of this schema definition in the form
-     * specified in RFC 2252.
-     *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
     AttributeType duplicate() {
-        return new AttributeType(oid, names, description, isObsolete, superiorTypeOID,
+        return new AttributeType(oid, names, getDescription(), isObsolete, superiorTypeOID,
                 equalityMatchingRuleOID, orderingMatchingRuleOID, substringMatchingRuleOID,
                 approximateMatchingRuleOID, syntaxOID, isSingleValue, isCollective,
-                isNoUserModification, attributeUsage, extraProperties, definition);
+                isNoUserModification, attributeUsage, getExtraProperties(), toString());
     }
 
     @Override
@@ -608,11 +583,7 @@
             }
         }
 
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
 
         if (isObsolete) {
             buffer.append(" OBSOLETE");
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java
index 69bdfee..e5ab9cc 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java
@@ -70,9 +70,6 @@
     // The set of required attribute types for this DIT content rule.
     private final Set<String> requiredAttributeOIDs;
 
-    // The definition string used to create this objectclass.
-    private final String definition;
-
     private ObjectClass structuralClass;
     private Set<ObjectClass> auxiliaryClasses = Collections.emptySet();
     private Set<AttributeType> optionalAttributes = Collections.emptySet();
@@ -84,7 +81,7 @@
             final Set<String> optionalAttributeOIDs, final Set<String> prohibitedAttributeOIDs,
             final Set<String> requiredAttributeOIDs,
             final Map<String, List<String>> extraProperties, final String definition) {
-        super(description, extraProperties);
+        super(description, extraProperties, definition);
 
         Validator.ensureNotNull(structuralClassOID, names);
         Validator.ensureNotNull(auxiliaryClassOIDs, optionalAttributeOIDs, prohibitedAttributeOIDs,
@@ -96,12 +93,6 @@
         this.optionalAttributeOIDs = optionalAttributeOIDs;
         this.prohibitedAttributeOIDs = prohibitedAttributeOIDs;
         this.requiredAttributeOIDs = requiredAttributeOIDs;
-
-        if (definition != null) {
-            this.definition = definition;
-        } else {
-            this.definition = buildDefinition();
-        }
     }
 
     /**
@@ -304,22 +295,10 @@
         return isRequired(attributeType) || isOptional(attributeType);
     }
 
-    /**
-     * Returns the string representation of this schema definition in the form
-     * specified in RFC 2252.
-     *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
     DITContentRule duplicate() {
-        return new DITContentRule(structuralClassOID, names, description, isObsolete,
+        return new DITContentRule(structuralClassOID, names, getDescription(), isObsolete,
                 auxiliaryClassOIDs, optionalAttributeOIDs, prohibitedAttributeOIDs,
-                requiredAttributeOIDs, extraProperties, definition);
+                requiredAttributeOIDs, getExtraProperties(), toString());
     }
 
     @Override
@@ -347,11 +326,7 @@
             }
         }
 
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
 
         if (isObsolete) {
             buffer.append(" OBSOLETE");
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java
index 1f06531..a3a5746 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java
@@ -61,9 +61,6 @@
     // The set of superior DIT structure rules.
     private final Set<Integer> superiorRuleIDs;
 
-    // The definition string used to create this objectclass.
-    private final String definition;
-
     private NameForm nameForm;
     private Set<DITStructureRule> superiorRules = Collections.emptySet();
 
@@ -76,7 +73,7 @@
     DITStructureRule(final Integer ruleID, final List<String> names, final String description,
             final boolean obsolete, final String nameFormOID, final Set<Integer> superiorRuleIDs,
             final Map<String, List<String>> extraProperties, final String definition) {
-        super(description, extraProperties);
+        super(description, extraProperties, definition);
 
         Validator.ensureNotNull(ruleID, nameFormOID, superiorRuleIDs);
         this.ruleID = ruleID;
@@ -84,12 +81,6 @@
         this.isObsolete = obsolete;
         this.nameFormOID = nameFormOID;
         this.superiorRuleIDs = superiorRuleIDs;
-
-        if (definition != null) {
-            this.definition = definition;
-        } else {
-            this.definition = buildDefinition();
-        }
     }
 
     /**
@@ -205,21 +196,9 @@
         return isObsolete;
     }
 
-    /**
-     * Retrieves the string representation of this schema definition in the form
-     * specified in RFC 2252.
-     *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
     DITStructureRule duplicate() {
-        return new DITStructureRule(ruleID, names, description, isObsolete, nameFormOID,
-                superiorRuleIDs, extraProperties, definition);
+        return new DITStructureRule(ruleID, names, getDescription(), isObsolete, nameFormOID,
+                superiorRuleIDs, getExtraProperties(), toString());
     }
 
     @Override
@@ -247,11 +226,7 @@
             }
         }
 
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
 
         if (isObsolete) {
             buffer.append(" OBSOLETE");
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java
index e526ec4..5894451 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java
@@ -62,7 +62,6 @@
     private final List<String> names;
     private final boolean isObsolete;
     private final String syntaxOID;
-    private final String definition;
     private MatchingRuleImpl impl;
     private Syntax syntax;
     private Schema schema;
@@ -71,7 +70,7 @@
             final boolean obsolete, final String syntax,
             final Map<String, List<String>> extraProperties, final String definition,
             final MatchingRuleImpl implementation) {
-        super(description, extraProperties);
+        super(description, extraProperties, definition);
 
         Validator.ensureNotNull(oid, names, description, syntax);
         Validator.ensureNotNull(extraProperties);
@@ -79,12 +78,6 @@
         this.names = names;
         this.isObsolete = obsolete;
         this.syntaxOID = syntax;
-
-        if (definition != null) {
-            this.definition = definition;
-        } else {
-            this.definition = buildDefinition();
-        }
         this.impl = implementation;
     }
 
@@ -303,21 +296,9 @@
         return impl.normalizeAttributeValue(schema, value);
     }
 
-    /**
-     * Returns the string representation of this schema definition in the form
-     * specified in RFC 2252.
-     *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
     MatchingRule duplicate() {
-        return new MatchingRule(oid, names, description, isObsolete, syntaxOID, extraProperties,
-                definition, impl);
+        return new MatchingRule(oid, names, getDescription(), isObsolete, syntaxOID,
+                getExtraProperties(), toString(), impl);
     }
 
     @Override
@@ -345,11 +326,7 @@
             }
         }
 
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
 
         if (isObsolete) {
             buffer.append(" OBSOLETE");
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java
index 8faa1ca..f667534 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java
@@ -60,28 +60,19 @@
     // associated.
     private final Set<String> attributeOIDs;
 
-    // The definition string used to create this objectclass.
-    private final String definition;
-
     private MatchingRule matchingRule;
     private Set<AttributeType> attributes = Collections.emptySet();
 
     MatchingRuleUse(final String oid, final List<String> names, final String description,
             final boolean obsolete, final Set<String> attributeOIDs,
             final Map<String, List<String>> extraProperties, final String definition) {
-        super(description, extraProperties);
+        super(description, extraProperties, definition);
 
         Validator.ensureNotNull(oid, names, attributeOIDs);
         this.oid = oid;
         this.names = names;
         this.isObsolete = obsolete;
         this.attributeOIDs = attributeOIDs;
-
-        if (definition != null) {
-            this.definition = definition;
-        } else {
-            this.definition = buildDefinition();
-        }
     }
 
     /**
@@ -224,21 +215,9 @@
         return isObsolete;
     }
 
-    /**
-     * Returns the string representation of this schema definition in the form
-     * specified in RFC 2252.
-     *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
     MatchingRuleUse duplicate() {
-        return new MatchingRuleUse(oid, names, description, isObsolete, attributeOIDs,
-                extraProperties, definition);
+        return new MatchingRuleUse(oid, names, getDescription(), isObsolete, attributeOIDs,
+                getExtraProperties(), toString());
     }
 
     @Override
@@ -266,11 +245,7 @@
             }
         }
 
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
 
         if (isObsolete) {
             buffer.append(" OBSOLETE");
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java
index 69da108..59a54c4 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java
@@ -27,19 +27,13 @@
 
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_NAME_FORM_STRUCTURAL_CLASS_NOT_STRUCTURAL1;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_OPTIONAL_ATTR1;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_REQUIRED_ATTR1;
-import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_NAME_FORM_UNKNOWN_STRUCTURAL_CLASS1;
+import static com.forgerock.opendj.ldap.CoreMessages.*;
 
-
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -53,84 +47,62 @@
  * RDN of an entry with a given structural objectclass.
  */
 public final class NameForm extends SchemaElement {
-
-    // The OID that may be used to reference this definition.
-    private final String oid;
-
-    // The set of user defined names for this definition.
-    private final List<String> names;
-
-    // Indicates whether this definition is declared "obsolete".
-    private final boolean isObsolete;
-
-    // The reference to the structural objectclass for this name form.
-    private final String structuralClassOID;
-
-    // The set of optional attribute types for this name form.
-    private final Set<String> optionalAttributeOIDs;
-
-    // The set of required attribute types for this name form.
-    private final Set<String> requiredAttributeOIDs;
-
-    // The definition string used to create this objectclass.
-    private final String definition;
-
-    private ObjectClass structuralClass;
-    private Set<AttributeType> optionalAttributes = Collections.emptySet();
-    private Set<AttributeType> requiredAttributes = Collections.emptySet();
-
     /**
-     * The name form builder.
+     * A fluent API for incrementally constructing name forms.
      */
     public static class Builder extends SchemaElementBuilder<Builder> {
-
-        // Required attributes
-        private String oid;
-        private String structuralObjectClassOID;
-        private Set<String> requiredAttribute = new LinkedHashSet<String>();
-
-        // Optional attributes - initialized to default values.
-        private List<String> names = new LinkedList<String>();
-        private Set<String> optionalAttributes = new LinkedHashSet<String>();
-        private String definition;
         private boolean isObsolete = false;
+        private final List<String> names = new LinkedList<String>();
+        private String oid;
+        private final Set<String> optionalAttributes = new LinkedHashSet<String>();
+        private final Set<String> requiredAttributes = new LinkedHashSet<String>();
+        private String structuralObjectClassOID;
 
-        /**
-         * Sets the OID of the name form definition.
-         * <p>
-         * RFC 4512 : numericoid ; object identifier.
-         *
-         * @param oid
-         *            Like 1.3.6.1.4.1.1466.115.121.1.35.
-         * @return This name form builder.
-         */
-        public Builder oid(final String oid) {
-            this.oid = oid;
-            return this;
+        Builder(final NameForm nf, final SchemaBuilder builder) {
+            super(builder, nf);
+            this.oid = nf.oid;
+            this.structuralObjectClassOID = nf.structuralClassOID;
+            this.isObsolete = nf.isObsolete;
+            this.names.addAll(nf.names);
+            this.requiredAttributes.addAll(nf.requiredAttributeOIDs);
+            this.optionalAttributes.addAll(nf.optionalAttributeOIDs);
+        }
+
+        Builder(final String oid, final SchemaBuilder builder) {
+            super(builder);
+            this.oid(oid);
         }
 
         /**
-         * Sets the structural object class OID.
-         * <p>
-         * e.g : OC person.
+         * Adds this name form to the schema overwriting any existing name form
+         * with the same numeric OID.
          *
-         * @param oid
-         *            = SP "OC" SP oid (RFC 4512).
-         * @return This name form builder.
+         * @return The parent schema builder.
          */
-        public Builder structuralObjectClassOID(final String oid) {
-            this.structuralObjectClassOID = oid;
-            return this;
+        public SchemaBuilder addToSchema() {
+            return this.getSchemaBuilder().addNameForm(new NameForm(this), true);
         }
 
         /**
-         * Sets the user defined names for this definition.
-         * <p>
-         * RFC 4512 : [ SP "NAME" SP qdescrs ] ; short names (descriptors).
+         * Adds this name form to the schema, throwing an
+         * {@code  ConflictingSchemaElementException} if there is an existing
+         * name form with the same numeric OID.
+         *
+         * @return The parent schema builder.
+         * @throws ConflictingSchemaElementException
+         *             If there is an existing name form with the same numeric
+         *             OID.
+         */
+        public SchemaBuilder addToSchemaNoOverwrite() {
+            return this.getSchemaBuilder().addNameForm(new NameForm(this), false);
+        }
+
+        /**
+         * Adds the provided user friendly names.
          *
          * @param names
-         *            Contains a collection of strings.
-         * @return This name form builder.
+         *            The user friendly names.
+         * @return This builder.
          */
         public Builder names(final Collection<String> names) {
             this.names.addAll(names);
@@ -138,147 +110,23 @@
         }
 
         /**
-         * Sets the user defined names for this definition.
-         * <p>
-         * RFC 4512 : [ SP "NAME" SP qdescrs ] ; short names (descriptors).
+         * Adds the provided user friendly names.
          *
          * @param names
-         *            Contains a series of strings.
-         * @return This name form builder.
+         *            The user friendly names.
+         * @return This builder.
          */
         public Builder names(final String... names) {
             return names(Arrays.asList(names));
         }
 
         /**
-         * Erases all the names.
-         *
-         * @return This name form builder.
-         */
-        public Builder removeAllNames() {
-            this.names.clear();
-            return this;
-        }
-
-        /**
-         * Removes the defined name.
-         *
-         * @param name
-         *            The name to remove.
-         * @return This name form builder.
-         */
-        public Builder removeName(String name) {
-            names.remove(name);
-            return this;
-        }
-
-        /**
-         * Specifies which attributes are required by this name form.
-         * <p>
-         * RFC 4512 : SP "MUST" SP oids ; attribute types.
-         *
-         * @param oids
-         *            The OIDs of the required attributes.
-         * @return This name form builder.
-         */
-        public Builder requiredAttributes(final String... oids) {
-            return requiredAttributes(Arrays.asList(oids));
-        }
-
-        /**
-         * Specifies which attributes are required by this name form.
-         * <p>
-         * RFC 4512 : SP "MUST" SP oids ; attribute types.
-         *
-         * @param oids
-         *            The OIDs of the required attributes.
-         * @return This name form builder.
-         */
-        public Builder requiredAttributes(final Collection<String> oids) {
-            this.requiredAttribute.addAll(oids);
-            return this;
-        }
-
-        /**
-         * Removes the specified required attribute.
-         *
-         * @param oid
-         *            The OID of the required attributes.
-         * @return This name form builder.
-         */
-        public Builder removeRequiredAttribute(final String oid) {
-            this.requiredAttribute.remove(oid);
-            return this;
-        }
-
-        /**
-         * Removes all the required attributes.
-         *
-         * @return This name form builder.
-         */
-        public Builder removeAllRequiredAttributes() {
-            this.requiredAttribute.clear();
-            return this;
-        }
-
-        /**
-         * Sets the optional attribute OIDs.
-         * <p>
-         * RFC 4512 : [ SP "MAY" SP oids ] ; attribute types.
-         *
-         * @param oids
-         *            The OIDs of the optional attributes.
-         * @return This name form builder.
-         */
-        public Builder optionalAttributes(final String... oids) {
-            return optionalAttributes(Arrays.asList(oids));
-        }
-
-        /**
-         * Sets the optional attributes.
-         * <p>
-         * RFC 4512 : [ SP "MAY" SP oids ] ; attribute types.
-         *
-         * @param oids
-         *            The OIDs of the optional attributes.
-         * @return This name form builder.
-         */
-        public Builder optionalAttributes(final Collection<String> oids) {
-            this.optionalAttributes.addAll(oids);
-            return this;
-        }
-
-        /**
-         * Removes the specified attributes.
-         *
-         * @param oid
-         *            The OID of the optional attributes.
-         * @return This name form builder.
-         */
-        public Builder removeOptionalAttribute(final String oid) {
-            this.optionalAttributes.remove(oid);
-            return this;
-        }
-
-        /**
-         * Removes all the optional attributes.
-         *
-         * @return This name form builder.
-         */
-        public Builder removeAllOptionalAttributes() {
-            this.optionalAttributes.clear();
-            return this;
-        }
-
-        /**
-         * {@code true} if the object class definition is obsolete, otherwise
-         * {@code false}.
-         * <p>
-         * RFC 4512 : [ SP "OBSOLETE" ] ; not active.
+         * Specifies whether or not this schema element is obsolete.
          *
          * @param isObsolete
-         *            default is {@code false}.
-         * @return This name form builder.
+         *            {@code true} if this schema element is obsolete (default
+         *            is {@code false}).
+         * @return This builder.
          */
         public Builder obsolete(final boolean isObsolete) {
             this.isObsolete = isObsolete;
@@ -286,90 +134,171 @@
         }
 
         /**
-         * Sets the definition string used to create this object class.
+         * Sets the numeric OID which uniquely identifies this name form.
          *
-         * @param definition
-         *            The definition to set.
-         * @return This name form builder.
+         * @param oid
+         *            The numeric OID.
+         * @return This builder.
          */
-        Builder definition(final String definition) {
-            this.definition = definition;
+        public Builder oid(final String oid) {
+            this.oid = oid;
             return this;
         }
 
         /**
-         * Returns the builder.
+         * Adds the provided optional attributes.
          *
-         * @return This name form builder.
+         * @param nameOrOIDs
+         *            The list of optional attributes.
+         * @return This builder.
          */
+        public Builder optionalAttributes(final Collection<String> nameOrOIDs) {
+            this.optionalAttributes.addAll(nameOrOIDs);
+            return this;
+        }
+
+        /**
+         * Adds the provided optional attributes.
+         *
+         * @param nameOrOIDs
+         *            The list of optional attributes.
+         * @return This builder.
+         */
+        public Builder optionalAttributes(final String... nameOrOIDs) {
+            return optionalAttributes(Arrays.asList(nameOrOIDs));
+        }
+
+        /**
+         * Removes all user friendly names.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllNames() {
+            this.names.clear();
+            return this;
+        }
+
+        /**
+         * Removes all optional attributes.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllOptionalAttributes() {
+            this.optionalAttributes.clear();
+            return this;
+        }
+
+        /**
+         * Removes all required attributes.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllRequiredAttributes() {
+            this.requiredAttributes.clear();
+            return this;
+        }
+
+        /**
+         * Removes the provided user friendly name.
+         *
+         * @param name
+         *            The user friendly name to be removed.
+         * @return This builder.
+         */
+        public Builder removeName(final String name) {
+            names.remove(name);
+            return this;
+        }
+
+        /**
+         * Removes the specified optional attribute.
+         *
+         * @param nameOrOID
+         *            The optional attribute to be removed.
+         * @return This builder.
+         */
+        public Builder removeOptionalAttribute(final String nameOrOID) {
+            this.optionalAttributes.remove(nameOrOID);
+            return this;
+        }
+
+        /**
+         * Removes the specified required attribute.
+         *
+         * @param nameOrOID
+         *            The required attribute to be removed.
+         * @return This builder.
+         */
+        public Builder removeRequiredAttribute(final String nameOrOID) {
+            this.requiredAttributes.remove(nameOrOID);
+            return this;
+        }
+
+        /**
+         * Adds the provided required attributes.
+         *
+         * @param nameOrOIDs
+         *            The list of required attributes.
+         * @return This builder.
+         */
+        public Builder requiredAttributes(final Collection<String> nameOrOIDs) {
+            this.requiredAttributes.addAll(nameOrOIDs);
+            return this;
+        }
+
+        /**
+         * Adds the provided required attributes.
+         *
+         * @param nameOrOIDs
+         *            The list of required attributes.
+         * @return This builder.
+         */
+        public Builder requiredAttributes(final String... nameOrOIDs) {
+            return requiredAttributes(Arrays.asList(nameOrOIDs));
+        }
+
+        /**
+         * Sets the structural object class.
+         *
+         * @param nameOrOID
+         *            The structural object class.
+         * @return This builder.
+         */
+        public Builder structuralObjectClassOID(final String nameOrOID) {
+            this.structuralObjectClassOID = nameOrOID;
+            return this;
+        }
+
         @Override
         Builder getThis() {
             return this;
         }
-
-        /**
-         * Creates a new name form builder implementation.
-         *
-         * @param oid
-         *            The OID of the name form definition.
-         * @param builder
-         *            The schema builder linked.
-         */
-        Builder(final String oid, final SchemaBuilder builder) {
-            this.oid(oid);
-            this.schemaBuilder(builder);
-        }
-
-        /**
-         * Duplicates an existing name form builder.
-         *
-         * @param nf
-         *            The name form to duplicate.
-         * @param builder
-         *            The schema builder where to adds this new name form
-         * @throws ConflictingSchemaElementException
-         *             If {@code overwrite} was {@code false} and a conflicting
-         *             schema element was found.
-         */
-        Builder(final NameForm nf, final SchemaBuilder builder) {
-            this.oid = nf.oid;
-            this.definition = nf.buildDefinition();
-            this.description(nf.description);
-            this.structuralObjectClassOID = nf.structuralClassOID;
-            this.isObsolete = nf.isObsolete;
-            this.names = new ArrayList<String>(nf.names);
-            this.extraProperties(new LinkedHashMap<String, List<String>>(nf.extraProperties));
-            this.requiredAttribute = new LinkedHashSet<String>(nf.requiredAttributeOIDs);
-            this.optionalAttributes = new LinkedHashSet<String>(nf.optionalAttributeOIDs);
-            this.schemaBuilder(builder);
-        }
-
-        /**
-         * Adds the name form to the builder overwriting any existing name form
-         * with the same OID.
-         *
-         * @return A schema builder.
-         */
-        public SchemaBuilder addToSchema() {
-            return this.getSchemaBuilder().addNameForm(new NameForm(this), true);
-        }
-
-        /**
-         * Adds the name form to the builder throwing an
-         * ConflictingSchemaElementException if there is an existing name form
-         * with the same OID.
-         *
-         * @return A schema builder.
-         * @throws ConflictingSchemaElementException
-         *             If there is an existing name form with the same OID.
-         */
-        public SchemaBuilder addNoOverwriteToSchema() {
-            return this.getSchemaBuilder().addNameForm(new NameForm(this), false);
-        }
     }
 
+    // Indicates whether this definition is declared "obsolete".
+    private final boolean isObsolete;
+
+    // The set of user defined names for this definition.
+    private final List<String> names;
+
+    // The OID that may be used to reference this definition.
+    private final String oid;
+
+    // The set of optional attribute types for this name form.
+    private final Set<String> optionalAttributeOIDs;
+    private Set<AttributeType> optionalAttributes = Collections.emptySet();
+
+    // The set of required attribute types for this name form.
+    private final Set<String> requiredAttributeOIDs;
+    private Set<AttributeType> requiredAttributes = Collections.emptySet();
+
+    // The reference to the structural objectclass for this name form.
+    private ObjectClass structuralClass;
+    private final String structuralClassOID;
+
     private NameForm(final Builder builder) {
-        super(builder.description, builder.extraProperties);
+        super(builder);
+
         // Checks for required attributes.
         if (builder.oid == null || builder.oid.isEmpty()) {
             throw new IllegalArgumentException("An OID must be specified.");
@@ -377,19 +306,16 @@
         if (builder.structuralObjectClassOID == null || builder.structuralObjectClassOID.isEmpty()) {
             throw new IllegalArgumentException("A structural class OID must be specified.");
         }
-        if (builder.requiredAttribute == null || builder.requiredAttribute.isEmpty()) {
+        if (builder.requiredAttributes == null || builder.requiredAttributes.isEmpty()) {
             throw new IllegalArgumentException("Required attribute must be specified.");
         }
 
         oid = builder.oid;
         structuralClassOID = builder.structuralObjectClassOID;
         names = SchemaUtils.unmodifiableCopyOfList(builder.names);
-        requiredAttributeOIDs = SchemaUtils.unmodifiableCopyOfSet(builder.requiredAttribute);
+        requiredAttributeOIDs = SchemaUtils.unmodifiableCopyOfSet(builder.requiredAttributes);
         optionalAttributeOIDs = SchemaUtils.unmodifiableCopyOfSet(builder.optionalAttributes);
         isObsolete = builder.isObsolete;
-
-        definition = buildDefinition();
-
     }
 
     /**
@@ -414,11 +340,11 @@
     }
 
     /**
-     * Returns the name or OID for this schema definition. If it has one or more
+     * Returns the name or numeric OID of this name form. If it has one or more
      * names, then the primary name will be returned. If it does not have any
-     * names, then the OID will be returned.
+     * names, then the numeric OID will be returned.
      *
-     * @return The name or OID for this schema definition.
+     * @return The name or numeric OID of this name form.
      */
     public String getNameOrOID() {
         if (names.isEmpty()) {
@@ -428,31 +354,30 @@
     }
 
     /**
-     * Returns an unmodifiable list containing the user-defined names that may
-     * be used to reference this schema definition.
+     * Returns an unmodifiable list containing the user-friendly names that may
+     * be used to reference this name form.
      *
-     * @return Returns an unmodifiable list containing the user-defined names
-     *         that may be used to reference this schema definition.
+     * @return An unmodifiable list containing the user-friendly names that may
+     *         be used to reference this name form.
      */
     public List<String> getNames() {
         return names;
     }
 
     /**
-     * Returns the OID for this schema definition.
+     * Returns the numeric OID of this name form.
      *
-     * @return The OID for this schema definition.
+     * @return The numeric OID of this name form.
      */
     public String getOID() {
-
         return oid;
     }
 
     /**
-     * Returns an unmodifiable set containing the optional attributes for this
+     * Returns an unmodifiable set containing the optional attributes of this
      * name form.
      *
-     * @return An unmodifiable set containing the optional attributes for this
+     * @return An unmodifiable set containing the optional attributes of this
      *         name form.
      */
     public Set<AttributeType> getOptionalAttributes() {
@@ -460,10 +385,10 @@
     }
 
     /**
-     * Returns an unmodifiable set containing the required attributes for this
+     * Returns an unmodifiable set containing the required attributes of this
      * name form.
      *
-     * @return An unmodifiable set containing the required attributes for this
+     * @return An unmodifiable set containing the required attributes of this
      *         name form.
      */
     public Set<AttributeType> getRequiredAttributes() {
@@ -471,9 +396,9 @@
     }
 
     /**
-     * Returns the reference to the structural objectclass for this name form.
+     * Returns the structural objectclass of this name form.
      *
-     * @return The reference to the structural objectclass for this name form.
+     * @return The structural objectclass of this name form.
      */
     public ObjectClass getStructuralClass() {
         return structuralClass;
@@ -491,12 +416,13 @@
     }
 
     /**
-     * Indicates whether this schema definition has the specified name.
+     * Returns {@code true} if this name form has the specified user-friendly
+     * name.
      *
      * @param name
-     *            The name for which to make the determination.
-     * @return <code>true</code> if the specified name is assigned to this
-     *         schema definition, or <code>false</code> if not.
+     *            The name.
+     * @return {@code true} if this name form has the specified user-friendly
+     *         name.
      */
     public boolean hasName(final String name) {
         for (final String n : names) {
@@ -508,96 +434,80 @@
     }
 
     /**
-     * Indicates whether this schema definition has the specified name or OID.
+     * Returns {@code true} if this name form has the specified user-friendly
+     * name or numeric OID.
      *
-     * @param value
-     *            The value for which to make the determination.
-     * @return <code>true</code> if the provided value matches the OID or one of
-     *         the names assigned to this schema definition, or
-     *         <code>false</code> if not.
+     * @param nameOrOID
+     *            The name or numeric OID.
+     * @return {@code true} if this name form has the specified user-friendly
+     *         name or numeric OID.
      */
-    public boolean hasNameOrOID(final String value) {
-        return hasName(value) || getOID().equals(value);
+    public boolean hasNameOrOID(final String nameOrOID) {
+        return hasName(nameOrOID) || getOID().equals(nameOrOID);
     }
 
     /**
-     * Indicates whether this schema definition is declared "obsolete".
+     * Returns {@code true} if this name form is "obsolete".
      *
-     * @return <code>true</code> if this schema definition is declared
-     *         "obsolete", or <code>false</code> if not.
+     * @return {@code true} if this name form is "obsolete".
      */
     public boolean isObsolete() {
         return isObsolete;
     }
 
     /**
-     * Indicates whether the provided attribute type is included in the optional
-     * attribute list for this name form.
+     * Returns {@code true} if the provided attribute type is included in the
+     * list of optional attributes for this name form.
      *
      * @param attributeType
-     *            The attribute type for which to make the determination.
-     * @return <code>true</code> if the provided attribute type is optional for
-     *         this name form, or <code>false</code> if not.
+     *            The attribute type.
+     * @return {@code true} if the provided attribute type is included in the
+     *         list of optional attributes for this name form.
      */
     public boolean isOptional(final AttributeType attributeType) {
         return optionalAttributes.contains(attributeType);
     }
 
     /**
-     * Indicates whether the provided attribute type is included in the required
-     * attribute list for this name form.
+     * Returns {@code true} if the provided attribute type is included in the
+     * list of required attributes for this name form.
      *
      * @param attributeType
-     *            The attribute type for which to make the determination.
-     * @return <code>true</code> if the provided attribute type is required by
-     *         this name form, or <code>false</code> if not.
+     *            The attribute type.
+     * @return {@code true} if the provided attribute type is included in the
+     *         list of required attributes for this name form.
      */
     public boolean isRequired(final AttributeType attributeType) {
         return requiredAttributes.contains(attributeType);
     }
 
     /**
-     * Indicates whether the provided attribute type is in the list of required
-     * or optional attributes for this name form.
+     * Returns {@code true} if the provided attribute type is included in the
+     * list of optional or required attributes for this name form.
      *
      * @param attributeType
-     *            The attribute type for which to make the determination.
-     * @return <code>true</code> if the provided attribute type is required or
-     *         allowed for this name form, or <code>false</code> if it is not.
+     *            The attribute type.
+     * @return {@code true} if the provided attribute type is included in the
+     *         list of optional or required attributes for this name form.
      */
     public boolean isRequiredOrOptional(final AttributeType attributeType) {
         return isRequired(attributeType) || isOptional(attributeType);
     }
 
-    /**
-     * Returns the string representation of this schema definition in the form
-     * specified in RFC 2252.
-     *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
     @Override
     void toStringContent(final StringBuilder buffer) {
         buffer.append(oid);
 
         if (!names.isEmpty()) {
             final Iterator<String> iterator = names.iterator();
-
             final String firstName = iterator.next();
             if (iterator.hasNext()) {
                 buffer.append(" NAME ( '");
                 buffer.append(firstName);
-
                 while (iterator.hasNext()) {
                     buffer.append("' '");
                     buffer.append(iterator.next());
                 }
-
                 buffer.append("' )");
             } else {
                 buffer.append(" NAME '");
@@ -606,11 +516,7 @@
             }
         }
 
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
 
         if (isObsolete) {
             buffer.append(" OBSOLETE");
@@ -621,17 +527,14 @@
 
         if (!requiredAttributeOIDs.isEmpty()) {
             final Iterator<String> iterator = requiredAttributeOIDs.iterator();
-
             final String firstName = iterator.next();
             if (iterator.hasNext()) {
                 buffer.append(" MUST ( ");
                 buffer.append(firstName);
-
                 while (iterator.hasNext()) {
                     buffer.append(" $ ");
                     buffer.append(iterator.next());
                 }
-
                 buffer.append(" )");
             } else {
                 buffer.append(" MUST ");
@@ -641,17 +544,14 @@
 
         if (!optionalAttributeOIDs.isEmpty()) {
             final Iterator<String> iterator = optionalAttributeOIDs.iterator();
-
             final String firstName = iterator.next();
             if (iterator.hasNext()) {
                 buffer.append(" MAY ( ");
                 buffer.append(firstName);
-
                 while (iterator.hasNext()) {
                     buffer.append(" $ ");
                     buffer.append(iterator.next());
                 }
-
                 buffer.append(" )");
             } else {
                 buffer.append(" MAY ");
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java
index 8b82170..d2aa3b5 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java
@@ -73,9 +73,6 @@
     // The set of optional attribute types for this objectclass.
     private final Set<String> optionalAttributeOIDs;
 
-    // The definition string used to create this objectclass.
-    private final String definition;
-
     private Set<ObjectClass> superiorClasses = Collections.emptySet();
     private Set<AttributeType> declaredRequiredAttributes = Collections.emptySet();
     private Set<AttributeType> requiredAttributes = Collections.emptySet();
@@ -93,7 +90,7 @@
             final Set<String> requiredAttributeOIDs, final Set<String> optionalAttributeOIDs,
             final ObjectClassType objectClassType, final Map<String, List<String>> extraProperties,
             final String definition) {
-        super(description, extraProperties);
+        super(description, extraProperties, definition);
 
         Validator.ensureNotNull(oid, names);
         Validator.ensureNotNull(superiorClassOIDs, requiredAttributeOIDs, optionalAttributeOIDs,
@@ -105,12 +102,6 @@
         this.objectClassType = objectClassType;
         this.requiredAttributeOIDs = requiredAttributeOIDs;
         this.optionalAttributeOIDs = optionalAttributeOIDs;
-
-        if (definition != null) {
-            this.definition = definition;
-        } else {
-            this.definition = buildDefinition();
-        }
     }
 
     /**
@@ -124,7 +115,7 @@
      *            The map of "extra" properties for this schema definition
      */
     ObjectClass(final String description, final Map<String, List<String>> extraProperties) {
-        super(description, extraProperties);
+        super(description, extraProperties, null);
         this.oid = EXTENSIBLE_OBJECT_OBJECTCLASS_OID;
         this.names = Collections.singletonList(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME);
         this.isObsolete = false;
@@ -132,8 +123,6 @@
         this.objectClassType = ObjectClassType.AUXILIARY;
         this.requiredAttributeOIDs = Collections.emptySet();
         this.optionalAttributeOIDs = Collections.emptySet();
-
-        this.definition = buildDefinition();
     }
 
     /**
@@ -370,22 +359,10 @@
         return isRequired(attributeType) || isOptional(attributeType);
     }
 
-    /**
-     * Returns the string representation of this schema definition in the form
-     * specified in RFC 2252.
-     *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
     ObjectClass duplicate() {
-        return new ObjectClass(oid, names, description, isObsolete, superiorClassOIDs,
-                requiredAttributeOIDs, optionalAttributeOIDs, objectClassType, extraProperties,
-                definition);
+        return new ObjectClass(oid, names, getDescription(), isObsolete, superiorClassOIDs,
+                requiredAttributeOIDs, optionalAttributeOIDs, objectClassType,
+                getExtraProperties(), toString());
     }
 
     @Override
@@ -413,11 +390,7 @@
             }
         }
 
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
 
         if (isObsolete) {
             buffer.append(" OBSOLETE");
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
index 6551422..c512b38 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -1320,8 +1320,8 @@
             // The next set of characters must be the OID.
             final NameForm.Builder nameFormBuilder =
                     new NameForm.Builder(
-                            SchemaUtils.readOID(reader, allowMalformedNamesAndOptions), this);
-
+                            SchemaUtils.readOID(reader, allowMalformedNamesAndOptions), this)
+                            .definition(definition);
 
             // Required properties :
             String structuralOID = null;
@@ -1380,8 +1380,6 @@
                 }
             }
 
-            nameFormBuilder.definition(definition);
-
             // Make sure that a structural class was specified. If not, then
             // it cannot be valid and the name form cannot be build.
             if (structuralOID == null) {
@@ -1399,7 +1397,7 @@
             if (overwrite) {
                 nameFormBuilder.addToSchema();
             } else {
-                nameFormBuilder.addNoOverwriteToSchema();
+                nameFormBuilder.addToSchemaNoOverwrite();
             }
         } catch (final DecodeException e) {
             final LocalizableMessage msg =
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java
index dd53688..bbc470e 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java
@@ -27,10 +27,14 @@
 
 package org.forgerock.opendj.ldap.schema;
 
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.unmodifiableCopyOfExtraProperties;
+
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import com.forgerock.opendj.util.Validator;
 
@@ -43,127 +47,239 @@
  * definitions).
  */
 abstract class SchemaElement {
+    static abstract class SchemaElementBuilder<T extends SchemaElementBuilder<T>> {
+        private String definition;
+        private String description;
+        private final Map<String, List<String>> extraProperties;
+        private final SchemaBuilder schemaBuilder;
+
+        SchemaElementBuilder(final SchemaBuilder schemaBuilder) {
+            this.schemaBuilder = schemaBuilder;
+            this.description = "";
+            this.extraProperties = new LinkedHashMap<String, List<String>>(1);
+        }
+
+        SchemaElementBuilder(final SchemaBuilder schemaBuilder, final SchemaElement copy) {
+            this.schemaBuilder = schemaBuilder;
+            this.description = copy.description;
+            this.extraProperties = new LinkedHashMap<String, List<String>>(copy.extraProperties);
+        }
+
+        /**
+         * Sets the description.
+         *
+         * @param description
+         *            The description, which may be {@code null} in which case
+         *            the empty string will be used.
+         * @return This builder.
+         */
+        public T description(final String description) {
+            this.description = description == null ? "" : description;
+            return getThis();
+        }
+
+        /**
+         * Adds the provided collection of extended properties.
+         *
+         * @param extraProperties
+         *            The collection of extended properties.
+         * @return This builder.
+         */
+        public T extraProperties(final Map<String, List<String>> extraProperties) {
+            this.extraProperties.putAll(extraProperties);
+            return getThis();
+        }
+
+        /**
+         * Adds the provided extended property.
+         *
+         * @param extensionName
+         *            The name of the extended property.
+         * @param extensionValues
+         *            The optional list of values for the extended property.
+         * @return This builder.
+         */
+        public T extraProperties(final String extensionName, final String... extensionValues) {
+            if (this.extraProperties.get(extensionName) != null) {
+                final List<String> tempExtraProperties =
+                        new ArrayList<String>(this.extraProperties.get(extensionName));
+                tempExtraProperties.addAll(Arrays.asList(extensionValues));
+                this.extraProperties.put(extensionName, tempExtraProperties);
+            } else {
+                this.extraProperties.put(extensionName, Arrays.asList(extensionValues));
+            }
+            return getThis();
+        }
+
+        /**
+         * Removes all extra properties.
+         *
+         * @return This builder.
+         */
+        public T removeAllExtraProperties() {
+            this.extraProperties.clear();
+            return getThis();
+        }
+
+        /**
+         * Removes the specified extended property.
+         *
+         * @param extensionName
+         *            The name of the extended property.
+         * @param extensionValues
+         *            The optional list of values for the extended property,
+         *            which may be empty indicating that the entire property
+         *            should be removed.
+         * @return This builder.
+         */
+        public T removeExtraProperty(final String extensionName, final String... extensionValues) {
+            if (this.extraProperties.get(extensionName) != null && extensionValues.length > 0) {
+                final List<String> tempExtraProperties =
+                        new ArrayList<String>(this.extraProperties.get(extensionName));
+                tempExtraProperties.removeAll(Arrays.asList(extensionValues));
+                this.extraProperties.put(extensionName, tempExtraProperties);
+            } else if (this.extraProperties.get(extensionName) != null) {
+                this.extraProperties.remove(extensionName);
+            }
+            return getThis();
+        }
+
+        T definition(final String definition) {
+            this.definition = definition;
+            return getThis();
+        }
+
+        String getDescription() {
+            return description;
+        }
+
+        Map<String, List<String>> getExtraProperties() {
+            return extraProperties;
+        }
+
+        SchemaBuilder getSchemaBuilder() {
+            return schemaBuilder;
+        }
+
+        abstract T getThis();
+    }
+
+    /**
+     * Lazily created string representation.
+     */
+    private String definition;
+
     // The description for this definition.
-    final String description;
+    private final String description;
 
     // The set of additional name-value pairs.
-    final Map<String, List<String>> extraProperties;
+    private final Map<String, List<String>> extraProperties;
 
-    SchemaElement(final String description, final Map<String, List<String>> extraProperties) {
+    SchemaElement() {
+        this.description = "";
+        this.extraProperties = Collections.<String, List<String>> emptyMap();
+        this.definition = null;
+    }
+
+    SchemaElement(final SchemaElementBuilder<?> builder) {
+        this.description = builder.description;
+        this.extraProperties = unmodifiableCopyOfExtraProperties(builder.extraProperties);
+        this.definition = builder.definition;
+    }
+
+    SchemaElement(final String description, final Map<String, List<String>> extraProperties,
+            final String definition) {
         Validator.ensureNotNull(description, extraProperties);
         this.description = description;
-
-        // Assumes caller has made the map unmodifiable.
-        this.extraProperties = extraProperties;
+        this.extraProperties = extraProperties; // Should already be unmodifiable.
+        this.definition = definition;
     }
 
     /**
      * {@inheritDoc}
      */
+    @Override
     public abstract boolean equals(Object obj);
 
     /**
-     * Returns the description of this schema definition.
+     * Returns the description of this schema element, or the empty string if it
+     * does not have a description.
      *
-     * @return The description of this schema definition.
+     * @return The description of this schema element, or the empty string if it
+     *         does not have a description.
      */
     public final String getDescription() {
-
         return description;
     }
 
     /**
-     * Returns an unmodifiable list containing the values of the named "extra"
-     * property for this schema definition.
+     * Returns an unmodifiable map containing all of the extra properties
+     * associated with this schema element.
      *
-     * @param name
-     *            The name of the "extra" property whose values are to be
-     *            returned.
-     * @return Returns an unmodifiable list containing the values of the named
-     *         "extra" property for this schema definition, which may be empty
-     *         if no such property is defined.
+     * @return An unmodifiable map containing all of the extra properties
+     *         associated with this schema element.
      */
-    public final List<String> getExtraProperty(final String name) {
-
-        final List<String> values = extraProperties.get(name);
-        return values != null ? values : Collections.<String> emptyList();
-    }
-
-    /**
-     * Returns an unmodifiable set containing the names of the "extra"
-     * properties associated with this schema definition.
-     *
-     * @return Returns an unmodifiable set containing the names of the "extra"
-     *         properties associated with this schema definition.
-     */
-    public final Set<String> getExtraPropertyNames() {
-
-        return extraProperties.keySet();
+    public final Map<String, List<String>> getExtraProperties() {
+        return extraProperties;
     }
 
     /**
      * {@inheritDoc}
      */
+    @Override
     public abstract int hashCode();
 
     /**
-     * {@inheritDoc}
-     */
-    public abstract String toString();
-
-    /**
-     * Builds a string representation of this schema definition in the form
-     * specified in RFC 2252.
+     * Returns the string representation of this schema element as defined in
+     * RFC 2252.
      *
-     * @return The string representation of this schema definition in the form
-     *         specified in RFC 2252.
+     * @return The string representation of this schema element as defined in
+     *         RFC 2252.
      */
-    final String buildDefinition() {
+    @Override
+    public final String toString() {
+        if (definition == null) {
+            definition = buildDefinition();
+        }
+        return definition;
+    }
+
+    final void appendDescription(final StringBuilder buffer) {
+        if (description != null && description.length() > 0) {
+            buffer.append(" DESC '");
+            buffer.append(description);
+            buffer.append("'");
+        }
+    }
+
+    abstract void toStringContent(StringBuilder buffer);
+
+    private final String buildDefinition() {
         final StringBuilder buffer = new StringBuilder();
-
         buffer.append("( ");
-
         toStringContent(buffer);
-
         if (!extraProperties.isEmpty()) {
             for (final Map.Entry<String, List<String>> e : extraProperties.entrySet()) {
-
                 final String property = e.getKey();
-
                 final List<String> valueList = e.getValue();
-
                 buffer.append(" ");
                 buffer.append(property);
-
                 if (valueList.size() == 1) {
                     buffer.append(" '");
                     buffer.append(valueList.get(0));
                     buffer.append("'");
                 } else {
                     buffer.append(" ( ");
-
                     for (final String value : valueList) {
                         buffer.append("'");
                         buffer.append(value);
                         buffer.append("' ");
                     }
-
                     buffer.append(")");
                 }
             }
         }
-
         buffer.append(" )");
-
         return buffer.toString();
     }
-
-    /**
-     * Appends a string representation of this schema definition's non-generic
-     * properties to the provided buffer.
-     *
-     * @param buffer
-     *            The buffer to which the information should be appended.
-     */
-    abstract void toStringContent(StringBuilder buffer);
 }
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElementBuilder.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElementBuilder.java
deleted file mode 100644
index c852f8c..0000000
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElementBuilder.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2013 ForgeRock AS
- */
-package org.forgerock.opendj.ldap.schema;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This class defines the set of methods and structures that must be implemented
- * to define a schema element builder.
- *
- * @param <T>
- *            Builder could be : AttributeTypeBuilder, NameFormBuilder,
- *            DITContentRuleBuilder, DITStructureRuleBuilder,
- *            MatchingRuleBuilder, ObjectClassBuilder, ...
- */
-abstract class SchemaElementBuilder<T extends SchemaElementBuilder<T>> {
-    // List of attributes in common / required attributes used by builders.
-    String description = "";
-    Map<String, List<String>> extraProperties;
-    private SchemaBuilder schemaBuilder = null;
-
-    /**
-     * Creates a new abstract schema element builder implementation.
-     */
-    SchemaElementBuilder() {
-        extraProperties = new LinkedHashMap<String, List<String>>();
-    }
-
-    /**
-     * Defines the schemThe builder.
-     *
-     * @param sc
-     *            The schemThe builder.
-     * @return A builder
-     */
-    T schemaBuilder(final SchemaBuilder sc) {
-        this.schemaBuilder = sc;
-        return getThis();
-    }
-
-    /**
-     * Returns the schemThe builder.
-     *
-     * @return The schemThe builder.
-     */
-    SchemaBuilder getSchemaBuilder() {
-        return schemaBuilder;
-    }
-
-    abstract T getThis();
-
-    /**
-     * The description of the schema element.
-     *
-     * @param description
-     *            a string containing the description of the schema element.
-     * @return <T> The builder.
-     */
-    public T description(final String description) {
-        this.description = description;
-        return getThis();
-    }
-
-    /**
-     * A map containing additional properties associated with the schema element
-     * definition.
-     * <p>
-     * cf. RFC 4512 : extensions WSP RPAREN ; extensions
-     *
-     * @param extraProperties
-     *            Additional properties.
-     * @return The builder.
-     */
-    public T extraProperties(final Map<String, List<String>> extraProperties) {
-        this.extraProperties.putAll(extraProperties);
-        return getThis();
-    }
-
-    /**
-     * Additional properties associated with the schema element definition.
-     * <p>
-     * cf. RFC 4512 : extensions WSP RPAREN ; extensions
-     *
-     * @param key
-     *            like X-ORIGIN
-     * @param extensions
-     *            e.g : 'RFC 2252'
-     * @return The builder.
-     */
-    public T extraProperties(final String key, final String... extensions) {
-        if (this.extraProperties.get(key) != null) {
-            List<String> tempExtraProperties = new ArrayList<String>(this.extraProperties.get(key));
-            tempExtraProperties.addAll(Arrays.asList(extensions));
-            this.extraProperties.put(key, tempExtraProperties);
-        } else {
-            this.extraProperties.put(key, Arrays.asList(extensions));
-        }
-        return getThis();
-    }
-
-    /**
-     * Removes an extra property.
-     *
-     * @param key
-     *            The key to remove.
-     * @param extensions
-     *            The extension to remove. Can be null.
-     * @return The builder.
-     */
-    public T removeExtraProperties(final String key, final String extensions) {
-        if (this.extraProperties.get(key) != null && extensions != null) {
-            List<String> tempExtraProperties = new ArrayList<String>(this.extraProperties.get(key));
-            tempExtraProperties.remove(tempExtraProperties.indexOf(extensions));
-            this.extraProperties.put(key, tempExtraProperties);
-        } else if (this.extraProperties.get(key) != null) {
-            this.extraProperties.remove(key);
-        }
-        return getThis();
-    }
-
-    /**
-     * Clears all extra properties.
-     *
-     * @return The builder.
-     */
-    public T clearExtraProperties() {
-        this.extraProperties.clear();
-        return getThis();
-    }
-}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java
index f62e8da..11ad2ec 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java
@@ -55,7 +55,6 @@
  */
 public final class Syntax extends SchemaElement {
     private final String oid;
-    private final String definition;
     private MatchingRule equalityMatchingRule;
     private MatchingRule orderingMatchingRule;
     private MatchingRule substringMatchingRule;
@@ -65,27 +64,20 @@
 
     Syntax(final String oid) {
         super("", Collections.singletonMap("X-SUBST", Collections.singletonList(Schema
-                .getDefaultSyntax().getOID())));
+                .getDefaultSyntax().getOID())), null);
 
         Validator.ensureNotNull(oid);
         this.oid = oid;
-        this.definition = buildDefinition();
         this.impl = Schema.getDefaultSyntax().impl;
     }
 
     Syntax(final String oid, final String description,
             final Map<String, List<String>> extraProperties, final String definition,
             final SyntaxImpl implementation) {
-        super(description, extraProperties);
+        super(description, extraProperties, definition);
 
         Validator.ensureNotNull(oid);
         this.oid = oid;
-
-        if (definition != null) {
-            this.definition = definition;
-        } else {
-            this.definition = buildDefinition();
-        }
         this.impl = implementation;
     }
 
@@ -205,18 +197,6 @@
     }
 
     /**
-     * Retrieves a string representation of this attribute syntax in the format
-     * defined in RFC 2252.
-     *
-     * @return A string representation of this attribute syntax in the format
-     *         defined in RFC 2252.
-     */
-    @Override
-    public String toString() {
-        return definition;
-    }
-
-    /**
      * Indicates whether the provided value is acceptable for use in an
      * attribute with this syntax. If it is not, then the reason may be appended
      * to the provided buffer.
@@ -234,18 +214,13 @@
     }
 
     Syntax duplicate() {
-        return new Syntax(oid, description, extraProperties, definition, impl);
+        return new Syntax(oid, getDescription(), getExtraProperties(), toString(), impl);
     }
 
     @Override
     void toStringContent(final StringBuilder buffer) {
         buffer.append(oid);
-
-        if (description != null && description.length() > 0) {
-            buffer.append(" DESC '");
-            buffer.append(description);
-            buffer.append("'");
-        }
+        appendDescription(buffer);
     }
 
     void validate(final Schema schema, final List<LocalizableMessage> warnings)
@@ -253,7 +228,7 @@
         this.schema = schema;
         if (impl == null) {
             // See if we need to override the implementation of the syntax
-            for (final Map.Entry<String, List<String>> property : extraProperties.entrySet()) {
+            for (final Map.Entry<String, List<String>> property : getExtraProperties().entrySet()) {
                 // Enums are handled in the schema builder.
                 if (property.getKey().equalsIgnoreCase("x-subst")) {
                     /**
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPMessageHandler.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPMessageHandler.java
deleted file mode 100644
index 8d848f9..0000000
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPMessageHandler.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2013 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import java.io.IOException;
-
-import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.requests.AbandonRequest;
-import org.forgerock.opendj.ldap.requests.AddRequest;
-import org.forgerock.opendj.ldap.requests.CompareRequest;
-import org.forgerock.opendj.ldap.requests.DeleteRequest;
-import org.forgerock.opendj.ldap.requests.ExtendedRequest;
-import org.forgerock.opendj.ldap.requests.GenericBindRequest;
-import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
-import org.forgerock.opendj.ldap.requests.ModifyRequest;
-import org.forgerock.opendj.ldap.requests.SearchRequest;
-import org.forgerock.opendj.ldap.requests.UnbindRequest;
-import org.forgerock.opendj.ldap.responses.BindResult;
-import org.forgerock.opendj.ldap.responses.CompareResult;
-import org.forgerock.opendj.ldap.responses.ExtendedResult;
-import org.forgerock.opendj.ldap.responses.IntermediateResponse;
-import org.forgerock.opendj.ldap.responses.Result;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
-import org.forgerock.opendj.ldap.responses.SearchResultReference;
-
-/**
- * Provides a LDAPMessageHandler implementation that throw an exception for all
- * methods.
- */
-public abstract class AbstractLDAPMessageHandler implements LDAPMessageHandler {
-
-    @Override
-    public void abandonRequest(final int messageID, final AbandonRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void addRequest(final int messageID, final AddRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void addResult(final int messageID, final Result result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public void bindRequest(final int messageID, final int version, final GenericBindRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void bindResult(final int messageID, final BindResult result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public void compareRequest(final int messageID, final CompareRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void compareResult(final int messageID, final CompareResult result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public void deleteRequest(final int messageID, final DeleteRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void deleteResult(final int messageID, final Result result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public <R extends ExtendedResult> void extendedRequest(final int messageID, final ExtendedRequest<R> request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void extendedResult(final int messageID, final ExtendedResult result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public void intermediateResponse(final int messageID, final IntermediateResponse response)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, response);
-    }
-
-    @Override
-    public void modifyDNRequest(final int messageID, final ModifyDNRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void modifyDNResult(final int messageID, final Result result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public void modifyRequest(final int messageID, final ModifyRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void modifyResult(final int messageID, final Result result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public void searchRequest(final int messageID, final SearchRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void searchResult(final int messageID, final Result result)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, result);
-    }
-
-    @Override
-    public void searchResultEntry(final int messageID, final SearchResultEntry entry)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, entry);
-    }
-
-    @Override
-    public void searchResultReference(final int messageID, final SearchResultReference reference)
-            throws UnexpectedResponseException, IOException {
-        throw new UnexpectedResponseException(messageID, reference);
-    }
-
-    @Override
-    public void unbindRequest(final int messageID, final UnbindRequest request)
-            throws UnexpectedRequestException, IOException {
-        throw new UnexpectedRequestException(messageID, request);
-    }
-
-    @Override
-    public void unrecognizedMessage(final int messageID, final byte messageTag, final ByteString messageBytes)
-            throws UnsupportedMessageException, IOException {
-        throw new UnsupportedMessageException(messageID, messageTag, messageBytes);
-    }
-}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPMessageHandler.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPMessageHandler.java
deleted file mode 100644
index 3ddf76a..0000000
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPMessageHandler.java
+++ /dev/null
@@ -1,392 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2013 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import java.io.IOException;
-
-import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.requests.AbandonRequest;
-import org.forgerock.opendj.ldap.requests.AddRequest;
-import org.forgerock.opendj.ldap.requests.CompareRequest;
-import org.forgerock.opendj.ldap.requests.DeleteRequest;
-import org.forgerock.opendj.ldap.requests.ExtendedRequest;
-import org.forgerock.opendj.ldap.requests.GenericBindRequest;
-import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
-import org.forgerock.opendj.ldap.requests.ModifyRequest;
-import org.forgerock.opendj.ldap.requests.SearchRequest;
-import org.forgerock.opendj.ldap.requests.UnbindRequest;
-import org.forgerock.opendj.ldap.responses.BindResult;
-import org.forgerock.opendj.ldap.responses.CompareResult;
-import org.forgerock.opendj.ldap.responses.ExtendedResult;
-import org.forgerock.opendj.ldap.responses.IntermediateResponse;
-import org.forgerock.opendj.ldap.responses.Result;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
-import org.forgerock.opendj.ldap.responses.SearchResultReference;
-
-/**
- * Interface for handler of LDAP messages (requests and responses).
- *
- */
-public interface LDAPMessageHandler {
-
-    /**
-     * Handle an abandon request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            abandon request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void abandonRequest(int messageID, AbandonRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle an add request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            add request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void addRequest(int messageID, AddRequest request) throws UnexpectedRequestException,
-            IOException;
-
-    /**
-     * Handle a response to an add request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void addResult(int messageID, Result result) throws UnexpectedResponseException,
-            IOException;
-
-    /**
-     * Handle a bind request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param version
-     *            protocol version for this bind request
-     * @param request
-     *            bind request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void bindRequest(int messageID, int version, GenericBindRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle a response to a bind request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void bindResult(int messageID, BindResult result) throws UnexpectedResponseException,
-            IOException;
-
-    /**
-     * Handle a compare request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            compare request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void compareRequest(int messageID, CompareRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle a response to a compare request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void compareResult(int messageID, CompareResult result)
-            throws UnexpectedResponseException, IOException;
-
-    /**
-     * Handle an delete request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            delete request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void deleteRequest(int messageID, DeleteRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle a response to a delete request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void deleteResult(int messageID, Result result) throws UnexpectedResponseException,
-            IOException;
-
-    /**
-     * Handle an extended request.
-     *
-     * @param <R>
-     *            type of extended result
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            extended request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    <R extends ExtendedResult> void extendedRequest(int messageID, ExtendedRequest<R> request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle a response to an extended request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void extendedResult(int messageID, ExtendedResult result)
-            throws UnexpectedResponseException, IOException;
-
-    /**
-     * Handle an intermediate response.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param response
-     *            intermediate response to handle
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void intermediateResponse(int messageID, IntermediateResponse response)
-            throws UnexpectedResponseException, IOException;
-
-    /**
-     * Handle a modify DN request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            modify DN request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void modifyDNRequest(int messageID, ModifyDNRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle a response to a modify DN request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void modifyDNResult(int messageID, Result result) throws UnexpectedResponseException,
-            IOException;
-
-    /**
-     * Handle a modify request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            modify request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void modifyRequest(int messageID, ModifyRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle a response to a modify request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void modifyResult(int messageID, Result result) throws UnexpectedResponseException,
-            IOException;
-
-    /**
-     * Handle a search request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            search request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void searchRequest(int messageID, SearchRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle a response to a search request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param result
-     *            response received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void searchResult(int messageID, Result result) throws UnexpectedResponseException,
-            IOException;
-
-    /**
-     * Handle a response to a search request returning an entry.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param entry
-     *            entry received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void searchResultEntry(int messageID, SearchResultEntry entry)
-            throws UnexpectedResponseException, IOException;
-
-    /**
-     * Handle a response to a search request returning an reference.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param reference
-     *            reference received
-     * @throws UnexpectedResponseException
-     *             if response is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void searchResultReference(int messageID, SearchResultReference reference)
-            throws UnexpectedResponseException, IOException;
-
-    /**
-     * Handle an unbind request.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param request
-     *            unbind request to handle
-     * @throws UnexpectedRequestException
-     *             if request is not expected by this handler
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void unbindRequest(int messageID, UnbindRequest request)
-            throws UnexpectedRequestException, IOException;
-
-    /**
-     * Handle an unrecognized message.
-     *
-     * @param messageID
-     *            identifier of message
-     * @param messageTag
-     *            tag identifying the message type
-     * @param messageBytes
-     *          content of the message
-     * @throws UnsupportedMessageException
-     *             if the received message is not supported
-     * @throws IOException
-     *             if an errors occurs when sending a subsequent message
-     */
-    void unrecognizedMessage(int messageID, byte messageTag, ByteString messageBytes)
-            throws UnsupportedMessageException, IOException;
-}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedRequestException.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedRequestException.java
deleted file mode 100644
index 2d01713..0000000
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedRequestException.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2013 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import java.io.IOException;
-
-import org.forgerock.i18n.LocalizableMessage;
-import org.forgerock.opendj.ldap.requests.Request;
-
-/**
- * Thrown when an unexpected LDAP request is received.
- */
-@SuppressWarnings("serial")
-public final class UnexpectedRequestException extends IOException {
-    private final int messageID;
-    private final Request request;
-
-    /**
-     * Creates the exception with a message id and a request.
-     *
-     * @param messageID
-     *            id of message
-     * @param request
-     *            request received
-     */
-    public UnexpectedRequestException(final int messageID, final Request request) {
-        super(LocalizableMessage.raw("Unexpected LDAP request: id=%d, message=%s", messageID,
-                request).toString());
-        this.messageID = messageID;
-        this.request = request;
-    }
-
-    /**
-     * Returns the identifier of the message.
-     *
-     * @return the identifier
-     */
-    public int getMessageID() {
-        return messageID;
-    }
-
-    /**
-     * Returns the request.
-     *
-     * @return the received request
-     */
-    public Request getRequest() {
-        return request;
-    }
-}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedResponseException.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedResponseException.java
deleted file mode 100644
index 2f1b02d..0000000
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedResponseException.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2013 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import java.io.IOException;
-
-import org.forgerock.i18n.LocalizableMessage;
-import org.forgerock.opendj.ldap.responses.Response;
-
-/**
- * Thrown when an unexpected LDAP response is received.
- */
-@SuppressWarnings("serial")
-public final class UnexpectedResponseException extends IOException {
-    private final int messageID;
-    private final Response response;
-
-    /**
-     * Creates the exception with a message id and a response.
-     *
-     * @param messageID
-     *            id of message
-     * @param response
-     *            response received
-     */
-    public UnexpectedResponseException(final int messageID, final Response response) {
-        super(LocalizableMessage.raw("Unexpected LDAP response: id=%d, message=%s", messageID,
-                response).toString());
-        this.messageID = messageID;
-        this.response = response;
-    }
-
-    /**
-     * Returns the identifier of the message.
-     *
-     * @return the identifier
-     */
-    public int getMessageID() {
-        return messageID;
-    }
-
-    /**
-     * Returns the response.
-     *
-     * @return the received response
-     */
-    public Response getResponse() {
-        return response;
-    }
-}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnsupportedMessageException.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnsupportedMessageException.java
deleted file mode 100644
index 1dc337d..0000000
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnsupportedMessageException.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License").  You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
- * or http://forgerock.org/license/CDDLv1.0.html.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at legal-notices/CDDLv1_0.txt.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information:
- *      Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2013 ForgeRock AS.
- */
-
-package org.forgerock.opendj.ldap.spi;
-
-import java.io.IOException;
-
-import org.forgerock.i18n.LocalizableMessage;
-import org.forgerock.opendj.ldap.ByteString;
-
-/**
- * Thrown when an unsupported LDAP message is received.
- */
-@SuppressWarnings("serial")
-public final class UnsupportedMessageException extends IOException {
-    private final int id;
-    private final byte tag;
-    private final ByteString content;
-
-    /**
-     * Creates the exception with message id, tag and content.
-     *
-     * @param id
-     *            identifier of received message
-     * @param tag
-     *            identify message type
-     * @param content
-     *            message content
-     */
-    public UnsupportedMessageException(final int id, final byte tag, final ByteString content) {
-        super(LocalizableMessage.raw("Unsupported LDAP message: id=%d, tag=%d, content=%s", id,
-                tag, content).toString());
-        this.id = id;
-        this.tag = tag;
-        this.content = content;
-    }
-
-    /**
-     * Returns the content.
-     *
-     * @return the message content
-     */
-    public ByteString getContent() {
-        return content;
-    }
-
-    /**
-     * Returns the id.
-     *
-     * @return the message identifier
-     */
-    public int getID() {
-        return id;
-    }
-
-    /**
-     * Returns the tag.
-     *
-     * @return the message tag
-     */
-    public byte getTag() {
-        return tag;
-    }
-}
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java
index 6d56afd..aadad35 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java
@@ -46,24 +46,20 @@
 import com.forgerock.opendj.util.Validator;
 
 /**
- * Generator of entries based on a {@code TemplateFile template file}, which can
- * be provided as a file, a list of lines, an array of lines, or an input
- * stream.
+ * A template driven entry generator, as used by the make-ldif tool.
  * <p>
- * To build a generator with all default values, including default template
- * file, use the empty constructor:
+ * To build a generator with default values, including default template file,
+ * use the empty constructor:
  *
  * <pre>
- *  generator = new EntryGenerator();
+ * generator = new EntryGenerator();
  * </pre>
  * <p>
  * To build a generator with some custom values, use the non-empty constructor
  * and the <code>set</code> methods:
  *
  * <pre>
- * generator = new EntryGenerator(templatePath)
- *     .setResourcePath(path)
- *     .setSchema(schema)
+ * generator = new EntryGenerator(templatePath).setResourcePath(path).setSchema(schema)
  * </pre>
  */
 public final class EntryGenerator implements EntryReader {
@@ -175,7 +171,7 @@
      * Sets the random seed to use when generating entries.
      *
      * @param seed
-     *            Seed to use.
+     *            The random seed to use.
      * @return A reference to this {@code EntryGenerator}.
      */
     public EntryGenerator setRandomSeed(final int seed) {
@@ -188,7 +184,7 @@
      * names, last names, or other custom resources.
      *
      * @param path
-     *            Resource path.
+     *            The resource path.
      * @return A reference to this {@code EntryGenerator}.
      */
     public EntryGenerator setResourcePath(final String path) {
@@ -215,9 +211,9 @@
      * the template file.
      *
      * @param name
-     *            Name of the constant.
+     *            The name of the constant.
      * @param value
-     *            Value of the constant.
+     *            The value of the constant.
      * @return A reference to this {@code EntryGenerator}.
      */
     public EntryGenerator setConstant(String name, Object value) {
@@ -226,24 +222,24 @@
     }
 
     /**
-     * Checks if there are some warning(s) after the parsing of template file.
+     * Checks if there are some warning(s) after parsing the template file.
      * <p>
      * Warnings are available only after the first call to {@code hasNext()} or
      * {@code readEntry()} methods.
      *
-     * @return true if there is at least one warning
+     * @return {@code true} if there is at least one warning.
      */
-    public boolean hasWarning() {
+    public boolean hasWarnings() {
         return !warnings.isEmpty();
     }
 
     /**
      * Returns the warnings generated by the parsing of template file.
      * <p>
-     * Warnings are available only after the first call to {@code hasNext()}
-     * or {@code readEntry()} methods.
+     * Warnings are available only after the first call to {@code hasNext()} or
+     * {@code readEntry()} methods.
      *
-     * @return the list of warnings, which is empty if there is no warning
+     * @return The list of warnings, which is empty if there are no warnings.
      */
     public List<LocalizableMessage> getWarnings() {
         return Collections.unmodifiableList(warnings);
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java
index d97a8dc..69ec6b5 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java
@@ -43,8 +43,7 @@
 import java.util.TreeMap;
 
 import org.forgerock.opendj.io.ASN1;
-import org.forgerock.opendj.io.ASN1Reader;
-import org.forgerock.opendj.io.ASN1Writer;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.ldap.AVA;
 import org.forgerock.opendj.ldap.Attribute;
 import org.forgerock.opendj.ldap.AttributeDescription;
@@ -68,13 +67,9 @@
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
-import org.forgerock.opendj.ldap.responses.Responses;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.schema.AttributeUsage;
 import org.forgerock.opendj.ldap.schema.Schema;
 
-import com.forgerock.opendj.ldap.LDAPUtils;
-
 /**
  * This class contains common utility methods for creating and manipulating
  * readers and writers.
@@ -715,13 +710,9 @@
         return entries;
     }
 
-    private static SearchResultEntry decodeEntry(final byte[] asn1EntryFormat) {
-        final ASN1Reader readerASN1 = ASN1.getReader(asn1EntryFormat);
+    private static Entry decodeEntry(final byte[] asn1EntryFormat) {
         try {
-            final SearchResultEntry sr =
-                    LDAPUtils.decodeSearchResultEntry(readerASN1, new DecodeOptions());
-            readerASN1.close();
-            return sr;
+            return LDAP.readEntry(ASN1.getReader(asn1EntryFormat), new DecodeOptions());
         } catch (IOException ex) {
             throw new IllegalStateException(ex);
         }
@@ -729,14 +720,12 @@
 
     private static byte[][] encodeEntry(final Entry entry) {
         final byte[][] bEntry = new byte[2][];
-
-        final ByteStringBuilder bsb = new ByteStringBuilder();
-        final ASN1Writer writer = ASN1.getWriter(bsb);
         // Store normalized DN
         bEntry[0] = getBytes(entry.getName().toNormalizedString());
         try {
             // Store ASN1 representation of the entry.
-            LDAPUtils.encodeSearchResultEntry(writer, Responses.newSearchResultEntry(entry));
+            final ByteStringBuilder bsb = new ByteStringBuilder();
+            LDAP.writeEntry(ASN1.getWriter(bsb), entry);
             bEntry[1] = bsb.toByteArray();
             return bEntry;
         } catch (final IOException ioe) {
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java
index 4b5d35e..796746b 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java
@@ -90,7 +90,7 @@
  *
  * @see EntryGenerator
  */
-class TemplateFile {
+final class TemplateFile {
 
     /** Default resource path used if no resource path is provided. */
     private static final File DEFAULT_RESOURCES_PATH = new File("org/forgerock/opendj/ldif");
@@ -1196,10 +1196,9 @@
      *            The path provided for the file, which can be absolute or
      *            relative.
      * @return A reader on the file, or <code>null</code> if it could not be
-     *         found. It is the responsability of caller to close the returned
+     *         found. It is the responsibility of caller to close the returned
      *         reader.
      */
-    @SuppressWarnings("resource")
     BufferedReader getReader(final String filePath) {
         BufferedReader reader = null;
         File file = new File(filePath);
@@ -1964,15 +1963,18 @@
          * The line number on which this template line appears in the template
          * file.
          */
+        @SuppressWarnings("unused")
         private final int lineNumber;
 
         /** The set of tags for this template line. */
         private final List<TemplateTag> tags;
 
         /** Whether this line corresponds to an URL value or not. */
+        @SuppressWarnings("unused")
         private final boolean isURL;
 
         /** Whether this line corresponds to a base64 encoded value or not. */
+        @SuppressWarnings("unused")
         private final boolean isBase64;
 
         /**
diff --git a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateTag.java b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateTag.java
index 636ed92..ffbf0fe 100644
--- a/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateTag.java
+++ b/opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateTag.java
@@ -51,7 +51,6 @@
  * Represents a tag that may be used in a template line when generating entries.
  * It can be used to generate content.
  *
- * @see TemplateFile
  * @see EntryGenerator
  */
 abstract class TemplateTag {
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java
index ffcb7b6..768e9d8 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java
@@ -30,6 +30,7 @@
 import java.io.IOException;
 
 import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.DecodeException;
 import org.forgerock.opendj.ldap.Entry;
 import org.forgerock.opendj.ldap.LDAPOptions;
 import org.forgerock.opendj.ldap.LinkedHashMapEntry;
@@ -56,10 +57,6 @@
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
-import org.forgerock.opendj.ldap.spi.AbstractLDAPMessageHandler;
-import org.forgerock.opendj.ldap.spi.LDAPMessageHandler;
-import org.forgerock.opendj.ldap.spi.UnexpectedRequestException;
-import org.forgerock.opendj.ldap.spi.UnexpectedResponseException;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
@@ -92,31 +89,12 @@
 
     @DataProvider
     protected Object[][] messagesFactories() {
-        return new Object[][] {
-                abandonRequest(),
-                addRequest(),
-                addResult(),
-                abandonRequest(),
-                bindRequest(),
-                bindResult(),
-                compareRequest(),
-                compareResult(),
-                deleteRequest(),
-                deleteResult(),
-                extendedRequest(),
-                extendedResult(),
-                intermediateResponse(),
-                modifyDNRequest(),
-                modifyDNResult(),
-                modifyRequest(),
-                modifyResult(),
-                searchRequest(),
-                searchResult(),
-                searchResultEntry(),
-                searchResultReference(),
-                unbindRequest(),
-                unrecognizedMessage()
-        };
+        return new Object[][] { abandonRequest(), addRequest(), addResult(), abandonRequest(),
+            bindRequest(), bindResult(), compareRequest(), compareResult(), deleteRequest(),
+            deleteResult(), extendedRequest(), extendedResult(), intermediateResponse(),
+            modifyDNRequest(), modifyDNResult(), modifyRequest(), modifyResult(), searchRequest(),
+            searchResult(), searchResultEntry(), searchResultReference(), unbindRequest(),
+            unrecognizedMessage() };
     }
 
     Object[] abandonRequest() {
@@ -128,8 +106,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void abandonRequest(int messageID, AbandonRequest request) throws UnexpectedRequestException,
-            IOException {
+            public void abandonRequest(int messageID, AbandonRequest request)
+                    throws DecodeException, IOException {
                 assertThat(request.getRequestID()).isEqualTo(requestID);
             }
         } };
@@ -143,8 +121,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void addRequest(int messageID, AddRequest request)
-                    throws UnexpectedRequestException, IOException {
+            public void addRequest(int messageID, AddRequest request) throws DecodeException,
+                    IOException {
                 assertThat(request.getName().toString()).isEqualTo(TEST_DN);
             }
         } };
@@ -155,11 +133,12 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeAddResult(MESSAGE_ID, Responses.newResult(resultCode).setMatchedDN(TEST_DN));
+                writer.writeAddResult(MESSAGE_ID, Responses.newResult(resultCode).setMatchedDN(
+                        TEST_DN));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void addResult(int messageID, Result result) throws UnexpectedResponseException, IOException {
+            public void addResult(int messageID, Result result) throws DecodeException, IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
                 assertThat(result.getMatchedDN().toString()).isEqualTo(TEST_DN);
             }
@@ -169,17 +148,17 @@
     Object[] bindRequest() {
         final int version = 1;
         final byte type = 0x01;
-        final byte[] value = new byte[] {0x01, 0x02};
+        final byte[] value = new byte[] { 0x01, 0x02 };
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeBindRequest(MESSAGE_ID, version,
-                        Requests.newGenericBindRequest(TEST_DN, type, value));
+                writer.writeBindRequest(MESSAGE_ID, version, Requests.newGenericBindRequest(
+                        TEST_DN, type, value));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void bindRequest(final int messageID, final int version, final GenericBindRequest request)
-                    throws UnexpectedRequestException, IOException {
+            public void bindRequest(final int messageID, final int version,
+                    final GenericBindRequest request) throws DecodeException, IOException {
                 assertThat(request.getAuthenticationType()).isEqualTo(type);
                 assertThat(request.getAuthenticationValue()).isEqualTo(value);
                 assertThat(request.getName()).isEqualTo(TEST_DN);
@@ -197,7 +176,7 @@
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void bindResult(final int messageID, final BindResult result)
-                    throws UnexpectedRequestException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
             }
         } };
@@ -209,12 +188,13 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeCompareRequest(MESSAGE_ID, Requests.newCompareRequest(TEST_DN, description, value));
+                writer.writeCompareRequest(MESSAGE_ID, Requests.newCompareRequest(TEST_DN,
+                        description, value));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void compareRequest(final int messageID, final CompareRequest request)
-                    throws UnexpectedRequestException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(request.getName().toString()).isEqualTo(TEST_DN);
                 assertThat(request.getAttributeDescription().toString()).isEqualTo(description);
                 assertThat(request.getAssertionValue().toString()).isEqualTo(value);
@@ -231,8 +211,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void compareResult(int messageID, CompareResult result)
-                    throws UnexpectedResponseException, IOException {
+            public void compareResult(int messageID, CompareResult result) throws DecodeException,
+                    IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
             }
         } };
@@ -246,8 +226,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void deleteRequest(int messageID, DeleteRequest request)
-                    throws UnexpectedRequestException, IOException {
+            public void deleteRequest(int messageID, DeleteRequest request) throws DecodeException,
+                    IOException {
                 assertThat(request.getName().toString()).isEqualTo(TEST_DN);
             }
         } };
@@ -262,7 +242,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void deleteResult(int messageID, Result result) throws UnexpectedResponseException, IOException {
+            public void deleteResult(int messageID, Result result) throws DecodeException,
+                    IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
             }
         } };
@@ -274,15 +255,16 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeExtendedRequest(MESSAGE_ID, Requests.newCancelExtendedRequest(requestID));
+                writer.writeExtendedRequest(MESSAGE_ID, Requests
+                        .newCancelExtendedRequest(requestID));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public <R extends ExtendedResult> void extendedRequest(int messageID, ExtendedRequest<R> request)
-                    throws UnexpectedRequestException, IOException {
+            public <R extends ExtendedResult> void extendedRequest(int messageID,
+                    ExtendedRequest<R> request) throws DecodeException, IOException {
                 CancelExtendedRequest cancelRequest =
-                        CancelExtendedRequest.DECODER.decodeExtendedRequest(
-                                request, new LDAPOptions().getDecodeOptions());
+                        CancelExtendedRequest.DECODER.decodeExtendedRequest(request,
+                                new LDAPOptions().getDecodeOptions());
                 assertThat(cancelRequest.getOID().toString()).isEqualTo(oidCancel);
                 assertThat(cancelRequest.getRequestID()).isEqualTo(requestID);
             }
@@ -295,13 +277,13 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeExtendedResult(MESSAGE_ID,
-                        Responses.newGenericExtendedResult(resultCode).setOID(oidCancel));
+                writer.writeExtendedResult(MESSAGE_ID, Responses.newGenericExtendedResult(
+                        resultCode).setOID(oidCancel));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void extendedResult(int messageID, ExtendedResult result)
-                    throws UnexpectedResponseException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
                 assertThat(result.getOID()).isEqualTo(oidCancel);
             }
@@ -314,13 +296,13 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeIntermediateResponse(MESSAGE_ID,
-                        Responses.newGenericIntermediateResponse(oid, responseValue));
+                writer.writeIntermediateResponse(MESSAGE_ID, Responses
+                        .newGenericIntermediateResponse(oid, responseValue));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void intermediateResponse(int messageID, IntermediateResponse response)
-                    throws UnexpectedResponseException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(response.getOID()).isEqualTo(oid);
                 assertThat(response.getValue()).isEqualTo(ByteString.valueOf(responseValue));
             }
@@ -332,12 +314,13 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeModifyDNRequest(MESSAGE_ID, Requests.newModifyDNRequest(TEST_DN, newRDN));
+                writer.writeModifyDNRequest(MESSAGE_ID, Requests
+                        .newModifyDNRequest(TEST_DN, newRDN));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void modifyDNRequest(int messageID, ModifyDNRequest request)
-                    throws UnexpectedRequestException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(request.getName().toString()).isEqualTo(TEST_DN);
                 assertThat(request.getNewRDN().toString()).isEqualTo(newRDN);
             }
@@ -353,8 +336,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void modifyDNResult(int messageID, Result result)
-                    throws UnexpectedResponseException, IOException {
+            public void modifyDNResult(int messageID, Result result) throws DecodeException,
+                    IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
             }
         } };
@@ -368,8 +351,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void modifyRequest(int messageID, ModifyRequest request)
-                    throws UnexpectedRequestException, IOException {
+            public void modifyRequest(int messageID, ModifyRequest request) throws DecodeException,
+                    IOException {
                 assertThat(request.getName().toString()).isEqualTo(TEST_DN);
             }
         } };
@@ -384,7 +367,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void modifyResult(int messageID, Result result) throws UnexpectedResponseException, IOException {
+            public void modifyResult(int messageID, Result result) throws DecodeException,
+                    IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
             }
         } };
@@ -397,12 +381,13 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeSearchRequest(MESSAGE_ID, Requests.newSearchRequest(TEST_DN, scope, filter, attribute));
+                writer.writeSearchRequest(MESSAGE_ID, Requests.newSearchRequest(TEST_DN, scope,
+                        filter, attribute));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void searchRequest(int messageID, SearchRequest request)
-                    throws UnexpectedRequestException, IOException {
+            public void searchRequest(int messageID, SearchRequest request) throws DecodeException,
+                    IOException {
                 assertThat(request.getName().toString()).isEqualTo(TEST_DN);
                 assertThat(request.getScope()).isEqualTo(scope);
                 assertThat(request.getFilter().toString()).isEqualTo(filter);
@@ -420,17 +405,16 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void searchResult(int messageID, Result result) throws UnexpectedResponseException, IOException {
+            public void searchResult(int messageID, Result result) throws DecodeException,
+                    IOException {
                 assertThat(result.getResultCode()).isEqualTo(resultCode);
             }
         } };
     }
 
     Object[] searchResultEntry() {
-        final Entry entry = new LinkedHashMapEntry(
-                "dn: cn=test",
-                "objectClass: top",
-                "objectClass: test");
+        final Entry entry =
+                new LinkedHashMapEntry("dn: cn=test", "objectClass: top", "objectClass: test");
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
@@ -439,7 +423,7 @@
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void searchResultEntry(int messageID, SearchResultEntry resultEntry)
-                    throws UnexpectedResponseException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(resultEntry).isEqualTo(entry);
             }
         } };
@@ -450,12 +434,13 @@
         return new Object[] { new LDAPWrite() {
             @Override
             public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
-                writer.writeSearchResultReference(MESSAGE_ID, Responses.newSearchResultReference(uri));
+                writer.writeSearchResultReference(MESSAGE_ID, Responses
+                        .newSearchResultReference(uri));
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void searchResultReference(int messageID, SearchResultReference reference)
-                    throws UnexpectedResponseException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(reference.getURIs()).containsExactly(uri);
             }
         } };
@@ -469,8 +454,8 @@
             }
         }, new AbstractLDAPMessageHandler() {
             @Override
-            public void unbindRequest(int messageID, UnbindRequest request)
-                    throws UnexpectedRequestException, IOException {
+            public void unbindRequest(int messageID, UnbindRequest request) throws DecodeException,
+                    IOException {
                 assertThat(request).isNotNull();
             }
         } };
@@ -487,7 +472,7 @@
         }, new AbstractLDAPMessageHandler() {
             @Override
             public void unrecognizedMessage(int messageID, byte tag, ByteString message)
-                    throws UnexpectedRequestException, IOException {
+                    throws DecodeException, IOException {
                 assertThat(messageID).isEqualTo(MESSAGE_ID);
                 assertThat(tag).isEqualTo(messageTag);
                 assertThat(message).isEqualTo(messageBytes);
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
index 13286c3..c1eed21 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java
@@ -27,9 +27,7 @@
 
 package org.forgerock.opendj.ldap;
 
-import static org.forgerock.opendj.ldap.TestCaseUtils.*;
-
-import static com.forgerock.opendj.ldap.LDAPConstants.TYPE_AUTHENTICATION_SASL;
+import static org.forgerock.opendj.ldap.TestCaseUtils.findFreeSocketAddress;
 
 import java.io.IOException;
 import java.net.SocketAddress;
@@ -54,6 +52,7 @@
 
 import org.forgerock.opendj.io.ASN1;
 import org.forgerock.opendj.io.ASN1Reader;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.ldap.controls.Control;
 import org.forgerock.opendj.ldap.controls.ControlDecoder;
 import org.forgerock.opendj.ldap.requests.AbandonRequest;
@@ -209,7 +208,7 @@
             // TODO: all bind types.
             final AbandonableRequest abReq = new AbandonableRequest(request);
             requestsInProgress.put(context, abReq);
-            if (request.getAuthenticationType() == TYPE_AUTHENTICATION_SASL
+            if (request.getAuthenticationType() == LDAP.TYPE_AUTHENTICATION_SASL
                     && request instanceof GenericBindRequest) {
                 ASN1Reader reader =
                         ASN1.getReader(((GenericBindRequest) request).getAuthenticationValue());
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/MockConnectionEventListener.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/MockConnectionEventListener.java
index 1d1d20d..ac22c6e 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/MockConnectionEventListener.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/MockConnectionEventListener.java
@@ -21,7 +21,7 @@
  * CDDL HEADER END
  *
  *
- *      Copyright 2012 ForgeRock AS.
+ *      Copyright 2012-2013 ForgeRock AS.
  */
 package org.forgerock.opendj.ldap;
 
@@ -37,6 +37,7 @@
  * A connection event listener which records events and signals when it has been
  * notified.
  */
+@SuppressWarnings("javadoc")
 public final class MockConnectionEventListener implements ConnectionEventListener {
     private final CountDownLatch closedLatch = new CountDownLatch(1);
     private final CountDownLatch errorLatch = new CountDownLatch(1);
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/BindRequestTestCase.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/BindRequestTestCase.java
index fc1f7d8..5e53b96 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/BindRequestTestCase.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/BindRequestTestCase.java
@@ -28,9 +28,9 @@
 
 import static org.testng.Assert.assertNotNull;
 
+import org.forgerock.opendj.io.LDAP;
 import org.testng.annotations.Test;
 
-import com.forgerock.opendj.ldap.LDAPConstants;
 
 /**
  * Tests the BIND requests.
@@ -40,7 +40,7 @@
     @Test(dataProvider = "testRequests")
     public void testAuthType(final BindRequest request) throws Exception {
         final byte b = request.getAuthenticationType();
-        if (!(b == LDAPConstants.TYPE_AUTHENTICATION_SASL || b == LDAPConstants.TYPE_AUTHENTICATION_SIMPLE)) {
+        if (!(b == LDAP.TYPE_AUTHENTICATION_SASL || b == LDAP.TYPE_AUTHENTICATION_SIMPLE)) {
             throw new Exception("Invalid bind type");
         }
     }
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/CompareRequestTestCase.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/CompareRequestTestCase.java
index 1309f26..bfdfb1e 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/CompareRequestTestCase.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/CompareRequestTestCase.java
@@ -30,7 +30,7 @@
 import org.testng.annotations.DataProvider;
 
 /**
- * @author sin
+ * Tests compare requests.
  */
 @SuppressWarnings("javadoc")
 public class CompareRequestTestCase extends RequestTestCase {
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/GenericBindRequestTestCase.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/GenericBindRequestTestCase.java
index f3307d4..2d662a6 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/GenericBindRequestTestCase.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/GenericBindRequestTestCase.java
@@ -30,9 +30,9 @@
 import static com.forgerock.opendj.util.StaticUtils.EMPTY_BYTES;
 import static com.forgerock.opendj.util.StaticUtils.getBytes;
 
+import org.forgerock.opendj.io.LDAP;
 import org.testng.annotations.DataProvider;
 
-import com.forgerock.opendj.ldap.LDAPConstants;
 
 /**
  * Tests Generic Bind requests.
@@ -47,9 +47,9 @@
     @Override
     protected GenericBindRequest[] createTestRequests() throws Exception {
         return new GenericBindRequest[] {
-                Requests.newGenericBindRequest(LDAPConstants.TYPE_AUTHENTICATION_SASL, EMPTY_BYTES),
-                Requests.newGenericBindRequest(LDAPConstants.TYPE_AUTHENTICATION_SIMPLE, getBytes("password")),
-                Requests.newGenericBindRequest("username", LDAPConstants.TYPE_AUTHENTICATION_SIMPLE,
+                Requests.newGenericBindRequest(LDAP.TYPE_AUTHENTICATION_SASL, EMPTY_BYTES),
+                Requests.newGenericBindRequest(LDAP.TYPE_AUTHENTICATION_SIMPLE, getBytes("password")),
+                Requests.newGenericBindRequest("username", LDAP.TYPE_AUTHENTICATION_SIMPLE,
                         getBytes("password"))
         };
     }
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSchemaElementTestCase.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSchemaElementTestCase.java
index f39d647..c611d3c 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSchemaElementTestCase.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSchemaElementTestCase.java
@@ -61,7 +61,6 @@
     @Test(dataProvider = "equalsTestData")
     public final void testEquals(final SchemaElement e1, final SchemaElement e2,
             final boolean result) throws Exception {
-
         Assert.assertEquals(e1.equals(e2), result);
         Assert.assertEquals(e2.equals(e1), result);
     }
@@ -93,7 +92,7 @@
     }
 
     /**
-     * Check that the {@link SchemaElement#getExtraProperty(String)} method
+     * Check that the {@link SchemaElement#getExtraProperties()} method
      * returns values.
      *
      * @throws Exception
@@ -108,14 +107,14 @@
         final SchemaElement e = getElement("", props);
 
         int i = 0;
-        for (final String value : e.getExtraProperty("test")) {
+        for (final String value : e.getExtraProperties().get("test")) {
             Assert.assertEquals(value, values.get(i));
             i++;
         }
     }
 
     /**
-     * Check that the {@link SchemaElement#getExtraProperty(String)} method
+     * Check that the {@link SchemaElement#getExtraProperties()} method
      * returns <code>null</code> when there is no property.
      *
      * @throws Exception
@@ -124,19 +123,7 @@
     @Test
     public final void testGetExtraPropertyDefault() throws Exception {
         final SchemaElement e = getElement("", EMPTY_PROPS);
-        Assert.assertTrue(e.getExtraProperty("test").isEmpty());
-    }
-
-    /**
-     * Check that the {@link SchemaElement#getExtraPropertyNames()} method.
-     *
-     * @throws Exception
-     *             If the test failed unexpectedly.
-     */
-    @Test
-    public final void testGetExtraPropertyNames() throws Exception {
-        final SchemaElement e = getElement("", EMPTY_PROPS);
-        Assert.assertTrue(e.getExtraProperty("test").isEmpty());
+        Assert.assertNull(e.getExtraProperties().get("test"));
     }
 
     /**
@@ -154,7 +141,6 @@
     @Test(dataProvider = "equalsTestData")
     public final void testHashCode(final SchemaElement e1, final SchemaElement e2,
             final boolean result) throws Exception {
-
         Assert.assertEquals(e1.hashCode() == e2.hashCode(), result);
     }
 
diff --git a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/NameFormTestCase.java b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/NameFormTestCase.java
index 4903fe9..f732e63 100644
--- a/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/NameFormTestCase.java
+++ b/opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/NameFormTestCase.java
@@ -55,7 +55,7 @@
                 .buildNameForm("1.3.6.1.4.1.1466.115.121.1.35")
                     .structuralObjectClassOID("person")
                     .requiredAttributes("sn", "cn") // ("cn, sn") is not supported.
-                    .addNoOverwriteToSchema()
+                    .addToSchemaNoOverwrite()
                 .toSchema();
 
         assertThat(schema.getWarnings()).isEmpty();
@@ -81,7 +81,7 @@
                     .structuralObjectClassOID("person")
                     .names("MyNewForm")
                     .requiredAttributes("sn", "cn")
-                    .addNoOverwriteToSchema()
+                    .addToSchemaNoOverwrite()
                 .toSchema();
         // @formatter:on
 
@@ -114,7 +114,7 @@
                 .names("MyNewForm")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("owner")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -145,7 +145,7 @@
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("owner")
                 .extraProperties("X-ORIGIN", "RFC xxx")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -153,12 +153,10 @@
         assertThat(schema.getNameForms().size()).isGreaterThan(0);
 
         for (final NameForm nf : schema.getNameForms()) {
-
             assertThat(nf.hasName("hasAName ?")).isFalse();
             assertThat(nf.getNameOrOID()).isEqualTo("MyNewForm");
             assertThat(nf.getOID()).isEqualTo("1.3.6.1.4.1.1466.115.121.1.35");
-            assertThat(nf.getExtraProperty("X-ORIGIN").get(0)).isEqualTo("RFC xxx");
-
+            assertThat(nf.getExtraProperties().get("X-ORIGIN").get(0)).isEqualTo("RFC xxx");
             assertThat(nf.toString()).isEqualTo(
                 "( 1.3.6.1.4.1.1466.115.121.1.35 NAME 'MyNewForm' OC person "
                 + "MUST ( sn $ cn ) MAY owner X-ORIGIN 'RFC xxx' )");
@@ -182,7 +180,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .structuralObjectClassOID("person")
                 .requiredAttributes("sn, cn")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
     }
@@ -203,7 +201,7 @@
                 .names("MyNewForm")
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn, cn")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
     }
@@ -225,7 +223,7 @@
                 .structuralObjectClassOID("person")
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes()
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
     }
@@ -246,7 +244,7 @@
                 .names("MyNewForm")
                 .structuralObjectClassOID("person")
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
     }
@@ -267,7 +265,7 @@
                 .structuralObjectClassOID("person")
                 .requiredAttributes("sn, cn")
                 .requiredAttributes((String[]) null)
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
     }
@@ -289,7 +287,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 // .optionalAttributeOIDs("") empty by default.
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
     }
@@ -312,7 +310,7 @@
                 .requiredAttributes("sn")
                 .removeRequiredAttribute("unknown")
                 .removeOptionalAttribute("optionalunknown")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -352,7 +350,7 @@
         assertThat(schema.getNameForms()).isNotEmpty();
         final NameForm nf = schema.getNameForms().iterator().next();
         assertThat(nf.getOID()).isEqualTo("1.3.6.1.4.1.1466.115.121.1.35");
-        assertThat(nf.getExtraPropertyNames()).isNotEmpty();
+        assertThat(nf.getExtraProperties()).isNotEmpty();
 
         // @formatter:off
         assertThat(nf.toString()).isEqualTo(
@@ -407,7 +405,7 @@
             .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
             .requiredAttributes("sn", "cn")
             .optionalAttributes("description", "uid")
-            .addNoOverwriteToSchema();
+            .addToSchemaNoOverwrite();
 
         Schema schema = sb.toSchema();
         assertThat(schema.getWarnings()).isEmpty();
@@ -453,7 +451,7 @@
             .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
             .requiredAttributes("sn", "cn")
             .optionalAttributes("description", "uid")
-            .addNoOverwriteToSchema();
+            .addToSchemaNoOverwrite();
 
         Schema schema = sb.toSchema();
         assertThat(schema.getWarnings()).isEmpty();
@@ -488,7 +486,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -499,7 +497,7 @@
             .names("MyNewForm")
             .structuralObjectClassOID("person")
             .requiredAttributes("sn", "cn")
-            .addNoOverwriteToSchema().toSchema();
+            .addToSchemaNoOverwrite().toSchema();
         final NameForm nf2 = schema2.getNameForm("MyNewForm");
 
         assertThat(nf1.equals(nf2)).isTrue();
@@ -521,7 +519,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
         final NameForm nf1 = schema.getNameForms().iterator().next();
@@ -536,7 +534,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -592,7 +590,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -633,7 +631,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -675,7 +673,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -704,7 +702,7 @@
                 .extraProperties("X-ORIGIN", "NameFormCheckingTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -737,7 +735,7 @@
                 .optionalAttributes("owner")
                 .optionalAttributes("l")
                 .names("Rock")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -751,9 +749,9 @@
 
             assertThat(nf.getNames().get(0)).isEqualTo("multipleAttributes");
             assertThat(nf.getNames().get(1)).isEqualTo("Rock");
-            assertThat(nf.getExtraProperty("X-ORIGIN").get(0))
+            assertThat(nf.getExtraProperties().get("X-ORIGIN").get(0))
                     .isEqualTo("NameFormCheckingTestCase");
-            assertThat(nf.getExtraProperty("X-ORIGIN").get(1)).isEqualTo(
+            assertThat(nf.getExtraProperties().get("X-ORIGIN").get(1)).isEqualTo(
                     "NameFormCheckingTestCase2");
 
             assertThat(nf.getStructuralClass().getNameOrOID()).isEqualTo("person");
@@ -856,7 +854,7 @@
                 .removeName("nameform2")
                 .removeRequiredAttribute("cn")
                 .removeOptionalAttribute("l")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -892,7 +890,7 @@
                 .extraProperties("FROM", "NameFormTestCase")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .toSchema();
         // @formatter:on
 
@@ -913,9 +911,10 @@
                     .removeOptionalAttribute("nonExistentUid")
                     .requiredAttributes("street")
                     .removeRequiredAttribute("sn")
-                    .removeExtraProperties("X-ORIGIN", "extra")
-                    .removeExtraProperties("X-ORIGIN", "Forgerock")
-                    .removeExtraProperties("FROM", null);
+                    .removeExtraProperty("X-ORIGIN", "extra")
+                    .removeExtraProperty("X-ORIGIN", "Forgerock")
+                    .removeExtraProperty("FROM");
+
         // @formatter:on
         sb.addSchema(schema, true);
         sb.addSchema(nfBuilder.addToSchema().toSchema(), true);
@@ -935,8 +934,8 @@
         assertThat(dolly.getRequiredAttributes().toString()).contains("cn");
 
         assertThat(dolly.getOptionalAttributes().size()).isEqualTo(1);
-        assertThat(dolly.getExtraProperty("X-ORIGIN").size()).isEqualTo(1);
-        assertThat(dolly.getExtraProperty("FROM")).isEmpty();
+        assertThat(dolly.getExtraProperties().get("X-ORIGIN").size()).isEqualTo(1);
+        assertThat(dolly.getExtraProperties().get("FROM")).isNull();
     }
 
     /**
@@ -958,7 +957,7 @@
             .extraProperties("FROM", "NameFormTestCase")
             .requiredAttributes("sn", "cn")
             .optionalAttributes("description", "uid")
-            .addNoOverwriteToSchema();
+            .addToSchemaNoOverwrite();
 
         Schema schema = sb.toSchema();
         assertThat(schema.getWarnings()).isEmpty();
@@ -966,9 +965,9 @@
 
         final NameForm nf = schema.getNameForms().iterator().next();
         assertThat(nf.getOID()).isEqualTo("1.3.6.1.4.1.1466.115.121.1.35");
-        assertThat(nf.getRequiredAttributes().size()).isEqualTo(2);
-        assertThat(nf.getOptionalAttributes().size()).isEqualTo(2);
-        assertThat(nf.getExtraPropertyNames().size()).isEqualTo(2);
+        assertThat(nf.getRequiredAttributes()).hasSize(2);
+        assertThat(nf.getOptionalAttributes()).hasSize(2);
+        assertThat(nf.getExtraProperties()).hasSize(2);
 
         sb.buildNameForm(nf)
             .removeAllNames()
@@ -976,7 +975,7 @@
             .removeName("thisOneDoesntExist")
             .oid("1.3.6.1.4.1.1466.115.121.1.36")
             .removeAllOptionalAttributes()
-            .clearExtraProperties()
+            .removeAllExtraProperties()
             .removeAllRequiredAttributes()
             .requiredAttributes("businessCategory")
             .addToSchema();
@@ -995,10 +994,8 @@
         assertThat(dolly.getRequiredAttributes().size()).isEqualTo(1);
         assertThat(dolly.getRequiredAttributes().iterator().next().getOID()).isEqualTo("2.5.4.15");
         assertThat(dolly.getRequiredAttributes().iterator().next().getNameOrOID()).isEqualTo("businessCategory");
-
-        assertThat(dolly.getOptionalAttributes().size()).isEqualTo(0);
-
-        assertThat(dolly.getExtraPropertyNames().size()).isEqualTo(0);
+        assertThat(dolly.getOptionalAttributes()).isEmpty();
+        assertThat(dolly.getExtraProperties()).isEmpty();
     }
 
     /**
@@ -1018,7 +1015,7 @@
                 .extraProperties("X-ORIGIN", "NameFormTestCase", "Forgerock", "extra")
                 .requiredAttributes("sn", "cn")
                 .optionalAttributes("description", "uid")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .buildNameForm("1.3.6.1.4.1.1466.115.121.1.36")
                 .description("Description of the second form")
                 .names("SecondForm")
@@ -1026,7 +1023,7 @@
                 .extraProperties("X-ORIGIN", "NameFormTestCase2")
                 .requiredAttributes("name")
                 .optionalAttributes("owner")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
             .buildNameForm("1.3.6.1.4.1.1466.115.121.1.37")
                 .description("Description of the third form")
                 .names("ThirdForm")
@@ -1035,7 +1032,7 @@
                 .requiredAttributes("sn", "l")
                 .optionalAttributes("description", "uid")
                 .description("Description of the third form")
-                .addNoOverwriteToSchema()
+                .addToSchemaNoOverwrite()
                 // we overwritten the third name form.
             .buildNameForm("1.3.6.1.4.1.1466.115.121.1.37")
                 .names("ThirdFormOverwritten")
@@ -1057,7 +1054,7 @@
         assertThat(schema.getNameForm("ThirdFormOverwritten").getOID()).isEqualTo(
                 "1.3.6.1.4.1.1466.115.121.1.37");
         assertThat(schema.getNameForm("ThirdFormOverwritten").getDescription()).isEmpty();
-        assertThat(schema.getNameForm("ThirdFormOverwritten").getExtraProperty("X-ORIGIN").get(0))
+        assertThat(schema.getNameForm("ThirdFormOverwritten").getExtraProperties().get("X-ORIGIN").get(0))
                 .isEqualTo("RFC 2252");
     }
 }
diff --git a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java
index a8c6c21..e6606b3 100644
--- a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java
+++ b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java
@@ -28,12 +28,12 @@
 package com.forgerock.opendj.grizzly;
 
 import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static com.forgerock.opendj.ldap.LDAPConstants.*;
 import static com.forgerock.opendj.util.StaticUtils.*;
 
 import java.io.IOException;
 
 import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.io.ASN1;
 import org.forgerock.opendj.io.ASN1Reader;
 import org.forgerock.opendj.io.AbstractASN1Reader;
 import org.forgerock.opendj.ldap.ByteString;
@@ -50,11 +50,8 @@
 final class ASN1BufferReader extends AbstractASN1Reader {
     private final class ChildSequenceLimiter implements SequenceLimiter {
         private SequenceLimiter parent;
-
         private ChildSequenceLimiter child;
-
         private int readLimit;
-
         private int bytesRead;
 
         public void checkLimit(final int readSize) throws IOException {
@@ -62,23 +59,18 @@
                 final LocalizableMessage message = ERR_ASN1_TRUNCATED_LENGTH_BYTE.get();
                 throw DecodeException.fatalError(message);
             }
-
             parent.checkLimit(readSize);
-
             bytesRead += readSize;
         }
 
         public SequenceLimiter endSequence() throws IOException {
             parent.checkLimit(remaining());
-
             if (remaining() > 0) {
                 IO_LOG.debug("Ignoring {} unused trailing bytes in ASN.1 SEQUENCE", remaining());
             }
-
             for (int i = 0; i < remaining(); i++) {
                 buffer.get();
             }
-
             return parent;
         }
 
@@ -91,10 +83,8 @@
                 child = new ChildSequenceLimiter();
                 child.parent = this;
             }
-
             child.readLimit = readLimit;
             child.bytesRead = 0;
-
             return child;
         }
     }
@@ -123,10 +113,8 @@
                 child = new ChildSequenceLimiter();
                 child.parent = this;
             }
-
             child.readLimit = readLimit;
             child.bytesRead = 0;
-
             return child;
         }
     }
@@ -142,21 +130,13 @@
     }
 
     private static final int MAX_STRING_BUFFER_SIZE = 1024;
-
-    private int state = ELEMENT_READ_STATE_NEED_TYPE;
-
+    private int state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     private byte peekType = 0;
-
     private int peekLength = -1;
-
     private int lengthBytesNeeded = 0;
-
     private final int maxElementSize;
-
     private final CompositeBuffer buffer;
-
     private SequenceLimiter readLimiter;
-
     private final byte[] stringBuffer;
 
     /**
@@ -196,10 +176,11 @@
      *             If an error occurs while trying to decode an ASN1 element.
      */
     public boolean elementAvailable() throws IOException {
-        return !((state == ELEMENT_READ_STATE_NEED_TYPE) && !needTypeState(true))
-                && !((state == ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
+        return !((state == ASN1.ELEMENT_READ_STATE_NEED_TYPE)
+                        && !needTypeState(true))
+                && !((state == ASN1.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE)
                         && !needFirstLengthByteState(true))
-                && !((state == ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
+                && !((state == ASN1.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES)
                         && !needAdditionalLengthBytesState(true))
                 && peekLength <= readLimiter.remaining();
 
@@ -215,7 +196,7 @@
      *             If an error occurs while trying to decode an ASN1 element.
      */
     public boolean hasNextElement() throws IOException {
-        return (state != ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(true);
+        return (state != ASN1.ELEMENT_READ_STATE_NEED_TYPE) || needTypeState(true);
     }
 
     /**
@@ -225,11 +206,11 @@
         peekType();
 
         switch (state) {
-        case ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE:
+        case ASN1.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE:
             needFirstLengthByteState(false);
             break;
 
-        case ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES:
+        case ASN1.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES:
             needAdditionalLengthBytesState(false);
         }
 
@@ -240,7 +221,7 @@
      * {@inheritDoc}
      */
     public byte peekType() throws IOException {
-        if (state == ELEMENT_READ_STATE_NEED_TYPE) {
+        if (state == ASN1.ELEMENT_READ_STATE_NEED_TYPE) {
             needTypeState(false);
         }
 
@@ -262,10 +243,10 @@
         readLimiter.checkLimit(peekLength);
         final byte readByte = buffer.get();
 
-        IO_LOG.trace("READ ASN.1 BOOLEAN(type=0x{}, length={}, value={})",
-                byteToHex(peekType), peekLength, String.valueOf(readByte != 0x00));
+        IO_LOG.trace("READ ASN.1 BOOLEAN(type=0x{}, length={}, value={})", byteToHex(peekType),
+                peekLength, String.valueOf(readByte != 0x00));
 
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
         return readByte != 0x00;
     }
 
@@ -278,7 +259,7 @@
         IO_LOG.debug("READ ASN.1 END SEQUENCE");
 
         // Reset the state
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
     /**
@@ -330,7 +311,7 @@
                 longValue = (longValue << 8) | (readByte & 0xFF);
             }
 
-            state = ELEMENT_READ_STATE_NEED_TYPE;
+            state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
             return longValue;
         } else {
             int intValue = 0;
@@ -342,10 +323,10 @@
                 intValue = (intValue << 8) | (readByte & 0xFF);
             }
 
-            IO_LOG.trace("READ ASN.1 INTEGER(type=0x{}, length={}, value={})",
-                    byteToHex(peekType), peekLength, intValue);
+            IO_LOG.trace("READ ASN.1 INTEGER(type=0x{}, length={}, value={})", byteToHex(peekType),
+                    peekLength, intValue);
 
-            state = ELEMENT_READ_STATE_NEED_TYPE;
+            state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
             return intValue;
         }
     }
@@ -363,10 +344,9 @@
             throw DecodeException.fatalError(message);
         }
 
-        IO_LOG.trace("READ ASN.1 NULL(type=0x{}, length={})",
-                    byteToHex(peekType), peekLength);
+        IO_LOG.trace("READ ASN.1 NULL(type=0x{}, length={})", byteToHex(peekType), peekLength);
 
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
     /**
@@ -377,7 +357,7 @@
         peekLength();
 
         if (peekLength == 0) {
-            state = ELEMENT_READ_STATE_NEED_TYPE;
+            state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
             return ByteString.empty();
         }
 
@@ -386,9 +366,10 @@
         final byte[] value = new byte[peekLength];
         buffer.get(value);
 
-        IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType), peekLength);
+        IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType),
+                peekLength);
 
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
         return ByteString.wrap(value);
     }
 
@@ -400,7 +381,7 @@
         peekLength();
 
         if (peekLength == 0) {
-            state = ELEMENT_READ_STATE_NEED_TYPE;
+            state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
             return builder;
         }
 
@@ -411,9 +392,10 @@
             builder.append(buffer.get());
         }
 
-        IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType), peekLength);
+        IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={})", byteToHex(peekType),
+                peekLength);
 
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
         return builder;
     }
 
@@ -425,7 +407,7 @@
         peekLength();
 
         if (peekLength == 0) {
-            state = ELEMENT_READ_STATE_NEED_TYPE;
+            state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
             return "";
         }
 
@@ -439,7 +421,7 @@
         readLimiter.checkLimit(peekLength);
         buffer.get(readBuffer, 0, peekLength);
 
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
 
         String str;
         try {
@@ -451,8 +433,8 @@
             str = new String(stringBuffer, 0, peekLength);
         }
 
-        IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={}, value={})",
-                byteToHex(peekType), peekLength, str);
+        IO_LOG.trace("READ ASN.1 OCTETSTRING(type=0x{}, length={}, value={})", byteToHex(peekType),
+                peekLength, str);
 
         return str;
     }
@@ -466,11 +448,11 @@
 
         readLimiter = readLimiter.startSequence(peekLength);
 
-        IO_LOG.trace("READ ASN.1 START SEQUENCE(type=0x{}, length={})",
-                byteToHex(peekType), peekLength);
+        IO_LOG.trace("READ ASN.1 START SEQUENCE(type=0x{}, length={})", byteToHex(peekType),
+                peekLength);
 
         // Reset the state
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
     }
 
     /**
@@ -493,7 +475,7 @@
         for (int i = 0; i < peekLength; i++) {
             buffer.get();
         }
-        state = ELEMENT_READ_STATE_NEED_TYPE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_TYPE;
         return this;
     }
 
@@ -536,7 +518,7 @@
                             .get(peekLength, maxElementSize);
             throw DecodeException.fatalError(m);
         }
-        state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
+        state = ASN1.ELEMENT_READ_STATE_NEED_VALUE_BYTES;
         return true;
     }
 
@@ -568,7 +550,7 @@
             peekLength = 0x00;
 
             if (ensureRead && (readLimiter.remaining() < lengthBytesNeeded)) {
-                state = ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
+                state = ASN1.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
                 return false;
             }
 
@@ -588,7 +570,7 @@
                             .get(peekLength, maxElementSize);
             throw DecodeException.fatalError(m);
         }
-        state = ELEMENT_READ_STATE_NEED_VALUE_BYTES;
+        state = ASN1.ELEMENT_READ_STATE_NEED_VALUE_BYTES;
         return true;
     }
 
@@ -610,7 +592,7 @@
 
         readLimiter.checkLimit(1);
         peekType = buffer.get();
-        state = ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
+        state = ASN1.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
         return true;
     }
 }
diff --git a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java
index d81e72f..7726669 100644
--- a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java
+++ b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java
@@ -25,6 +25,7 @@
  */
 package com.forgerock.opendj.grizzly;
 
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.io.LDAPReader;
 import org.forgerock.opendj.io.LDAPWriter;
 import org.forgerock.opendj.ldap.DecodeOptions;
@@ -41,8 +42,6 @@
  * Common utility methods.
  */
 final class GrizzlyUtils {
-
-
     @SuppressWarnings("rawtypes")
     private static final ThreadCache.CachedTypeIndex<LDAPWriter> WRITER_INDEX = ThreadCache
             .obtainIndex(LDAPWriter.class, 1);
@@ -143,7 +142,7 @@
     public static LDAPReader<ASN1BufferReader> createReader(DecodeOptions decodeOptions, int maxASN1ElementSize,
             MemoryManager<?> memoryManager) {
         ASN1BufferReader asn1Reader = new ASN1BufferReader(maxASN1ElementSize, memoryManager);
-        return new LDAPReader<ASN1BufferReader>(asn1Reader, decodeOptions);
+        return LDAP.getReader(asn1Reader, decodeOptions);
     }
 
     /**
@@ -159,7 +158,7 @@
     public static LDAPWriter<ASN1BufferWriter> getWriter() {
         LDAPWriter<ASN1BufferWriter> writer = ThreadCache.takeFromCache(WRITER_INDEX);
         if (writer == null) {
-            writer = new LDAPWriter<ASN1BufferWriter>(new ASN1BufferWriter());
+            writer = LDAP.getWriter(new ASN1BufferWriter());
         }
         writer.getASN1Writer().reset();
         return writer;
diff --git a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPBaseFilter.java b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPBaseFilter.java
index 8236aa4..58fbe0c 100644
--- a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPBaseFilter.java
+++ b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPBaseFilter.java
@@ -27,9 +27,9 @@
 
 import java.io.IOException;
 
+import org.forgerock.opendj.io.LDAPMessageHandler;
 import org.forgerock.opendj.io.LDAPReader;
 import org.forgerock.opendj.ldap.DecodeOptions;
-import org.forgerock.opendj.ldap.spi.LDAPMessageHandler;
 import org.glassfish.grizzly.Buffer;
 import org.glassfish.grizzly.filterchain.BaseFilter;
 import org.glassfish.grizzly.filterchain.FilterChainContext;
diff --git a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java
index 0331276..70ce8c4 100644
--- a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java
+++ b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java
@@ -27,13 +27,13 @@
 
 package com.forgerock.opendj.grizzly;
 
-import static com.forgerock.opendj.ldap.LDAPConstants.*;
-
 import java.io.EOFException;
 import java.io.IOException;
 
 import javax.net.ssl.SSLEngine;
 
+import org.forgerock.opendj.io.AbstractLDAPMessageHandler;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.io.LDAPReader;
 import org.forgerock.opendj.io.LDAPWriter;
 import org.forgerock.opendj.ldap.ConnectionSecurityLayer;
@@ -57,13 +57,11 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
 import org.forgerock.opendj.ldap.spi.AbstractLDAPFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.AbstractLDAPMessageHandler;
 import org.forgerock.opendj.ldap.spi.LDAPBindFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPCompareFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPExtendedFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPFutureResultImpl;
 import org.forgerock.opendj.ldap.spi.LDAPSearchFutureResultImpl;
-import org.forgerock.opendj.ldap.spi.UnexpectedResponseException;
 import org.glassfish.grizzly.Connection;
 import org.glassfish.grizzly.EmptyCompletionHandler;
 import org.glassfish.grizzly.Grizzly;
@@ -82,7 +80,8 @@
     private static final Attribute<ClientResponseHandler> RESPONSE_HANDLER_ATTR =
             Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("ClientResponseHandler");
 
-    static final class ClientResponseHandler extends AbstractLDAPMessageHandler implements LDAPBaseHandler {
+    static final class ClientResponseHandler extends AbstractLDAPMessageHandler implements
+            LDAPBaseHandler {
 
         private final LDAPReader<ASN1BufferReader> reader;
         private FilterChainContext context;
@@ -111,8 +110,8 @@
         }
 
         @Override
-        public void addResult(final int messageID, final Result result)
-                throws UnexpectedResponseException, IOException {
+        public void addResult(final int messageID, final Result result) throws DecodeException,
+                IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -121,21 +120,20 @@
 
                 if (pendingRequest != null) {
                     if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future =
-                                (LDAPFutureResultImpl) pendingRequest;
+                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
                         if (future.getRequest() instanceof AddRequest) {
                             future.setResultOrError(result);
                             return;
                         }
                     }
-                    throw new UnexpectedResponseException(messageID, result);
+                    throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
         @Override
         public void bindResult(final int messageID, final BindResult result)
-                throws UnexpectedResponseException, IOException {
+                throws DecodeException, IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -152,8 +150,7 @@
                             if (!bindClient.evaluateResult(result)) {
                                 // The server is expecting a multi stage
                                 // bind response.
-                                final int msgID =
-                                        ldapConnection.continuePendingBindRequest(future);
+                                final int msgID = ldapConnection.continuePendingBindRequest(future);
 
                                 LDAPWriter<ASN1BufferWriter> ldapWriter = GrizzlyUtils.getWriter();
                                 try {
@@ -191,10 +188,8 @@
                             if (l != null) {
                                 // The connection needs to be secured by
                                 // the SASL mechanism.
-                                ldapConnection
-                                        .installFilter(new ConnectionSecurityLayerFilter(l,
-                                                context.getConnection().getTransport()
-                                                        .getMemoryManager()));
+                                ldapConnection.installFilter(new ConnectionSecurityLayerFilter(l,
+                                        context.getConnection().getTransport().getMemoryManager()));
                             }
                         }
 
@@ -202,14 +197,14 @@
                         future.setResultOrError(result);
                         return;
                     }
-                    throw new UnexpectedResponseException(messageID, result);
+                    throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
         @Override
         public void compareResult(final int messageID, final CompareResult result)
-                throws UnexpectedResponseException, IOException {
+                throws DecodeException, IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -223,14 +218,14 @@
                         future.setResultOrError(result);
                         return;
                     }
-                    throw new UnexpectedResponseException(messageID, result);
+                    throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
         @Override
-        public void deleteResult(final int messageID, final Result result)
-                throws UnexpectedResponseException, IOException {
+        public void deleteResult(final int messageID, final Result result) throws DecodeException,
+                IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -239,33 +234,31 @@
 
                 if (pendingRequest != null) {
                     if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future =
-                                (LDAPFutureResultImpl) pendingRequest;
+                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
                         if (future.getRequest() instanceof DeleteRequest) {
                             future.setResultOrError(result);
                             return;
                         }
                     }
-                    throw new UnexpectedResponseException(messageID, result);
+                    throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
         @Override
         public void extendedResult(final int messageID, final ExtendedResult result)
-                throws UnexpectedResponseException, IOException {
+                throws DecodeException, IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
                 if (messageID == 0) {
                     // Unsolicited notification received.
                     if ((result.getOID() != null)
-                            && result.getOID().equals(OID_NOTICE_OF_DISCONNECTION)) {
+                            && result.getOID().equals(LDAP.OID_NOTICE_OF_DISCONNECTION)) {
                         // Treat this as a connection error.
                         final Result errorResult =
-                                Responses
-                                        .newResult(result.getResultCode())
-                                        .setDiagnosticMessage(result.getDiagnosticMessage());
+                                Responses.newResult(result.getResultCode()).setDiagnosticMessage(
+                                        result.getDiagnosticMessage());
                         ldapConnection.close(null, true, errorResult);
                     } else {
                         ldapConnection.handleUnsolicitedNotification(result);
@@ -279,21 +272,18 @@
                             final LDAPExtendedFutureResultImpl<?> extendedFuture =
                                     ((LDAPExtendedFutureResultImpl<?>) pendingRequest);
                             try {
-                                handleExtendedResult0(ldapConnection, extendedFuture,
-                                        result);
+                                handleExtendedResult0(ldapConnection, extendedFuture, result);
                             } catch (final DecodeException de) {
                                 // FIXME: should the connection be closed as
                                 // well?
                                 final Result errorResult =
-                                        Responses.newResult(
-                                                ResultCode.CLIENT_SIDE_DECODING_ERROR)
-                                                .setDiagnosticMessage(
-                                                        de.getLocalizedMessage()).setCause(
-                                                        de);
+                                        Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
+                                                .setDiagnosticMessage(de.getLocalizedMessage())
+                                                .setCause(de);
                                 extendedFuture.adaptErrorResult(errorResult);
                             }
                         } else {
-                            throw new UnexpectedResponseException(messageID, result);
+                            throw newUnexpectedResponseException(messageID, result);
                         }
                     }
                 }
@@ -302,7 +292,7 @@
 
         @Override
         public void intermediateResponse(final int messageID, final IntermediateResponse response)
-                throws UnexpectedResponseException, IOException {
+                throws DecodeException, IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -317,7 +307,7 @@
 
         @Override
         public void modifyDNResult(final int messageID, final Result result)
-                throws UnexpectedResponseException, IOException {
+                throws DecodeException, IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -326,21 +316,20 @@
 
                 if (pendingRequest != null) {
                     if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future =
-                                (LDAPFutureResultImpl) pendingRequest;
+                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
                         if (future.getRequest() instanceof ModifyDNRequest) {
                             future.setResultOrError(result);
                             return;
                         }
                     }
-                    throw new UnexpectedResponseException(messageID, result);
+                    throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
         @Override
-        public void modifyResult(final int messageID, final Result result)
-                throws UnexpectedResponseException, IOException {
+        public void modifyResult(final int messageID, final Result result) throws DecodeException,
+                IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -349,21 +338,20 @@
 
                 if (pendingRequest != null) {
                     if (pendingRequest instanceof LDAPFutureResultImpl) {
-                        final LDAPFutureResultImpl future =
-                                (LDAPFutureResultImpl) pendingRequest;
+                        final LDAPFutureResultImpl future = (LDAPFutureResultImpl) pendingRequest;
                         if (future.getRequest() instanceof ModifyRequest) {
                             future.setResultOrError(result);
                             return;
                         }
                     }
-                    throw new UnexpectedResponseException(messageID, result);
+                    throw newUnexpectedResponseException(messageID, result);
                 }
             }
         }
 
         @Override
-        public void searchResult(final int messageID, final Result result)
-                throws UnexpectedResponseException, IOException {
+        public void searchResult(final int messageID, final Result result) throws DecodeException,
+                IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -372,10 +360,9 @@
 
                 if (pendingRequest != null) {
                     if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
-                        ((LDAPSearchFutureResultImpl) pendingRequest)
-                                .setResultOrError(result);
+                        ((LDAPSearchFutureResultImpl) pendingRequest).setResultOrError(result);
                     } else {
-                        throw new UnexpectedResponseException(messageID, result);
+                        throw newUnexpectedResponseException(messageID, result);
                     }
                 }
             }
@@ -383,7 +370,7 @@
 
         @Override
         public void searchResultEntry(final int messageID, final SearchResultEntry entry)
-                throws UnexpectedResponseException, IOException {
+                throws DecodeException, IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -394,7 +381,7 @@
                     if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
                         ((LDAPSearchFutureResultImpl) pendingRequest).handleEntry(entry);
                     } else {
-                        throw new UnexpectedResponseException(messageID, entry);
+                        throw newUnexpectedResponseException(messageID, entry);
                     }
                 }
             }
@@ -402,7 +389,7 @@
 
         @Override
         public void searchResultReference(final int messageID, final SearchResultReference reference)
-                throws UnexpectedResponseException, IOException {
+                throws DecodeException, IOException {
             final GrizzlyLDAPConnection ldapConnection =
                     LDAP_CONNECTION_ATTR.get(context.getConnection());
             if (ldapConnection != null) {
@@ -411,10 +398,9 @@
 
                 if (pendingRequest != null) {
                     if (pendingRequest instanceof LDAPSearchFutureResultImpl) {
-                        ((LDAPSearchFutureResultImpl) pendingRequest)
-                                .handleReference(reference);
+                        ((LDAPSearchFutureResultImpl) pendingRequest).handleReference(reference);
                     } else {
-                        throw new UnexpectedResponseException(messageID, reference);
+                        throw newUnexpectedResponseException(messageID, reference);
                     }
                 }
             }
@@ -432,8 +418,8 @@
                     try {
                         final StartTLSExtendedRequest request =
                                 (StartTLSExtendedRequest) future.getRequest();
-                        conn.startTLS(request.getSSLContext(), request
-                                .getEnabledProtocols(), request.getEnabledCipherSuites(),
+                        conn.startTLS(request.getSSLContext(), request.getEnabledProtocols(),
+                                request.getEnabledCipherSuites(),
                                 new EmptyCompletionHandler<SSLEngine>() {
                                     @Override
                                     public void completed(final SSLEngine result) {
@@ -446,8 +432,7 @@
                                         final Result errorResult =
                                                 Responses.newResult(
                                                         ResultCode.CLIENT_SIDE_LOCAL_ERROR)
-                                                        .setCause(throwable)
-                                                        .setDiagnosticMessage(
+                                                        .setCause(throwable).setDiagnosticMessage(
                                                                 "SSL handshake failed");
                                         conn.setBindOrStartTLSInProgress(false);
                                         conn.close(null, false, errorResult);
@@ -457,8 +442,8 @@
                         return;
                     } catch (final IOException e) {
                         final Result errorResult =
-                                Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
-                                        .setCause(e).setDiagnosticMessage(e.getMessage());
+                                Responses.newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e)
+                                        .setDiagnosticMessage(e.getMessage());
                         future.adaptErrorResult(errorResult);
                         conn.close(null, false, errorResult);
                         return;
@@ -518,7 +503,6 @@
         return ctx.getInvokeAction();
     }
 
-
     @Override
     protected final void handleReadException(FilterChainContext ctx, IOException e) {
         final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx.getConnection());
@@ -545,8 +529,9 @@
         Connection<?> connection = ctx.getConnection();
         ClientResponseHandler handler = RESPONSE_HANDLER_ATTR.get(connection);
         if (handler == null) {
-            LDAPReader<ASN1BufferReader> reader = GrizzlyUtils.createReader(decodeOptions,
-                    maxASN1ElementSize, connection.getTransport().getMemoryManager());
+            LDAPReader<ASN1BufferReader> reader =
+                    GrizzlyUtils.createReader(decodeOptions, maxASN1ElementSize, connection
+                            .getTransport().getMemoryManager());
             handler = new ClientResponseHandler(reader);
             RESPONSE_HANDLER_ATTR.set(connection, handler);
         }
@@ -562,7 +547,8 @@
      * @param ldapConnection
      *            LDAP connection
      */
-    void registerConnection(final Connection<?> connection, final GrizzlyLDAPConnection ldapConnection) {
+    void registerConnection(final Connection<?> connection,
+            final GrizzlyLDAPConnection ldapConnection) {
         LDAP_CONNECTION_ATTR.set(connection, ldapConnection);
     }
 }
diff --git a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java
index eea9d48..2d89ff8 100644
--- a/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java
+++ b/opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java
@@ -27,8 +27,6 @@
 
 package com.forgerock.opendj.grizzly;
 
-import static com.forgerock.opendj.ldap.LDAPConstants.OID_NOTICE_OF_DISCONNECTION;
-
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.security.GeneralSecurityException;
@@ -38,6 +36,8 @@
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLSession;
 
+import org.forgerock.opendj.io.AbstractLDAPMessageHandler;
+import org.forgerock.opendj.io.LDAP;
 import org.forgerock.opendj.io.LDAPReader;
 import org.forgerock.opendj.io.LDAPWriter;
 import org.forgerock.opendj.ldap.ByteString;
@@ -72,9 +72,6 @@
 import org.forgerock.opendj.ldap.responses.Result;
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.ldap.responses.SearchResultReference;
-import org.forgerock.opendj.ldap.spi.AbstractLDAPMessageHandler;
-import org.forgerock.opendj.ldap.spi.UnexpectedRequestException;
-import org.forgerock.opendj.ldap.spi.UnsupportedMessageException;
 import org.glassfish.grizzly.Connection;
 import org.glassfish.grizzly.Grizzly;
 import org.glassfish.grizzly.attributes.Attribute;
@@ -98,25 +95,26 @@
      * Provides an arbitrary write operation on a LDAP writer.
      */
     private interface LDAPWrite<T> {
-        void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, T message) throws IOException;
+        void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, T message)
+                throws IOException;
     }
 
     /**
      * Write operation for intermediate responses.
      */
-    private static final LDAPWrite<IntermediateResponse> INTERMEDIATE = new LDAPWrite<IntermediateResponse>() {
-        public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, IntermediateResponse resp)
-                throws IOException {
-            writer.writeIntermediateResponse(messageID, resp);
-        }
-    };
+    private static final LDAPWrite<IntermediateResponse> INTERMEDIATE =
+            new LDAPWrite<IntermediateResponse>() {
+                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID,
+                        IntermediateResponse resp) throws IOException {
+                    writer.writeIntermediateResponse(messageID, resp);
+                }
+            };
 
     private static abstract class AbstractHandler<R extends Result> implements
             IntermediateResponseHandler, ResultHandler<R> {
         protected final ClientContextImpl context;
         protected final int messageID;
 
-
         protected AbstractHandler(final ClientContextImpl context, final int messageID) {
             this.messageID = messageID;
             this.context = context;
@@ -134,12 +132,13 @@
         }
 
         /**
-         * Default implementation of result handling, that delegate
-         * the actual write operation to {@code writeResult} method.
+         * Default implementation of result handling, that delegate the actual
+         * write operation to {@code writeResult} method.
          */
         private void defaultHandleResult(final R result) {
             writeMessage(new LDAPWrite<R>() {
-                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, R res) throws IOException {
+                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, R res)
+                        throws IOException {
                     writeResult(writer, res);
                 }
             }, result);
@@ -155,8 +154,8 @@
          * @throws IOException
          *             if an error occurs during writing
          */
-        abstract protected void writeResult(final LDAPWriter<ASN1BufferWriter> ldapWriter, final R result)
-                throws IOException;
+        abstract protected void writeResult(final LDAPWriter<ASN1BufferWriter> ldapWriter,
+                final R result) throws IOException;
 
         /**
          * Write a message on LDAP writer.
@@ -259,7 +258,7 @@
             Validator.ensureNotNull(resultCode);
             final GenericExtendedResult notification =
                     Responses.newGenericExtendedResult(resultCode).setOID(
-                            OID_NOTICE_OF_DISCONNECTION).setDiagnosticMessage(message);
+                            LDAP.OID_NOTICE_OF_DISCONNECTION).setDiagnosticMessage(message);
             sendUnsolicitedNotification(notification);
             disconnect0(resultCode, message);
         }
@@ -518,15 +517,16 @@
         @Override
         public void handleResult(final ExtendedResult result) {
             writeMessage(new LDAPWrite<ExtendedResult>() {
-                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, ExtendedResult message)
-                        throws IOException {
+                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID,
+                        ExtendedResult message) throws IOException {
                     writer.writeExtendedResult(messageID, message);
                 }
             }, result);
         }
 
         @Override
-        protected void writeResult(LDAPWriter<ASN1BufferWriter> ldapWriter, R result) throws IOException {
+        protected void writeResult(LDAPWriter<ASN1BufferWriter> ldapWriter, R result)
+                throws IOException {
             // never called because handleResult(result) method is overriden in this class
         }
     }
@@ -574,8 +574,8 @@
         @Override
         public boolean handleEntry(final SearchResultEntry entry) {
             writeMessage(new LDAPWrite<SearchResultEntry>() {
-                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, SearchResultEntry sre)
-                        throws IOException {
+                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID,
+                        SearchResultEntry sre) throws IOException {
                     writer.writeSearchResultEntry(messageID, sre);
                 }
             }, entry);
@@ -590,8 +590,8 @@
         @Override
         public boolean handleReference(final SearchResultReference reference) {
             writeMessage(new LDAPWrite<SearchResultReference>() {
-                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, SearchResultReference ref)
-                        throws IOException {
+                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID,
+                        SearchResultReference ref) throws IOException {
                     writer.writeSearchResultReference(messageID, ref);
                 }
             }, reference);
@@ -658,8 +658,8 @@
 
     private final GrizzlyLDAPListener listener;
 
-    private static final class ServerRequestHandler
-        extends AbstractLDAPMessageHandler implements LDAPBaseHandler {
+    private static final class ServerRequestHandler extends AbstractLDAPMessageHandler implements
+            LDAPBaseHandler {
 
         private final Connection<?> connection;
         private final LDAPReader<ASN1BufferReader> reader;
@@ -687,10 +687,8 @@
         }
 
         @Override
-        public void abandonRequest(final int messageID, final AbandonRequest request)
-                throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public void abandonRequest(final int messageID, final AbandonRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 conn.handleAbandon(messageID, request);
@@ -698,9 +696,8 @@
         }
 
         @Override
-        public void addRequest(final int messageID, final AddRequest request) throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public void addRequest(final int messageID, final AddRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final AddHandler handler = new AddHandler(clientContext, messageID);
@@ -710,22 +707,19 @@
 
         @Override
         public void bindRequest(final int messageID, final int version,
-                final GenericBindRequest request)
-                throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+                final GenericBindRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
-                final AbstractHandler<BindResult> handler = new BindHandler(clientContext, messageID);
+                final AbstractHandler<BindResult> handler =
+                        new BindHandler(clientContext, messageID);
                 conn.handleBind(messageID, version, request, handler, handler);
             }
         }
 
         @Override
-        public void compareRequest(final int messageID, final CompareRequest request)
-                throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public void compareRequest(final int messageID, final CompareRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final CompareHandler handler = new CompareHandler(clientContext, messageID);
@@ -734,10 +728,8 @@
         }
 
         @Override
-        public void deleteRequest(final int messageID, final DeleteRequest request)
-                throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public void deleteRequest(final int messageID, final DeleteRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final DeleteHandler handler = new DeleteHandler(clientContext, messageID);
@@ -746,11 +738,9 @@
         }
 
         @Override
-        public <R extends ExtendedResult> void extendedRequest(
-                final int messageID,
-                final ExtendedRequest<R> request) throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public <R extends ExtendedResult> void extendedRequest(final int messageID,
+                final ExtendedRequest<R> request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final ExtendedHandler<R> handler = new ExtendedHandler<R>(clientContext, messageID);
@@ -759,10 +749,8 @@
         }
 
         @Override
-        public void modifyDNRequest(final int messageID, final ModifyDNRequest request)
-                throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public void modifyDNRequest(final int messageID, final ModifyDNRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final ModifyDNHandler handler = new ModifyDNHandler(clientContext, messageID);
@@ -771,10 +759,8 @@
         }
 
         @Override
-        public void modifyRequest(final int messageID, final ModifyRequest request)
-                throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public void modifyRequest(final int messageID, final ModifyRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final ModifyHandler handler = new ModifyHandler(clientContext, messageID);
@@ -783,10 +769,8 @@
         }
 
         @Override
-        public void searchRequest(final int messageID, final SearchRequest request)
-                throws UnexpectedRequestException {
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.get(connection);
+        public void searchRequest(final int messageID, final SearchRequest request) {
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.get(connection);
             if (clientContext != null) {
                 final ServerConnection<Integer> conn = clientContext.getServerConnection();
                 final SearchHandler handler = new SearchHandler(clientContext, messageID);
@@ -798,8 +782,7 @@
         public void unbindRequest(final int messageID, final UnbindRequest request) {
             // Remove the client context causing any subsequent LDAP
             // traffic to be ignored.
-            final ClientContextImpl clientContext =
-                    LDAP_CONNECTION_ATTR.remove(connection);
+            final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.remove(connection);
             if (clientContext != null) {
                 clientContext.handleClose(messageID, request);
             }
@@ -808,7 +791,7 @@
         @Override
         public void unrecognizedMessage(final int messageID, final byte messageTag,
                 final ByteString messageBytes) {
-            exceptionOccurred(connection, new UnsupportedMessageException(messageID, messageTag,
+            exceptionOccurred(connection, newUnsupportedMessageException(messageID, messageTag,
                     messageBytes));
         }
     }
@@ -890,8 +873,9 @@
         Connection<?> connection = ctx.getConnection();
         ServerRequestHandler handler = REQUEST_HANDLER_ATTR.get(connection);
         if (handler == null) {
-            LDAPReader<ASN1BufferReader> reader = GrizzlyUtils.createReader(decodeOptions,
-                    maxASN1ElementSize, connection.getTransport().getMemoryManager());
+            LDAPReader<ASN1BufferReader> reader =
+                    GrizzlyUtils.createReader(decodeOptions, maxASN1ElementSize, connection
+                            .getTransport().getMemoryManager());
             handler = new ServerRequestHandler(connection, reader);
             REQUEST_HANDLER_ATTR.set(connection, handler);
         }
diff --git a/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java b/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
index 33d889e..5cd96e1 100644
--- a/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
+++ b/opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java
@@ -80,6 +80,7 @@
 /**
  * Tests the LDAPListener class.
  */
+@SuppressWarnings("javadoc")
 public class GrizzlyLDAPListenerTestCase extends SdkTestCase {
 
     private static class MockServerConnection implements ServerConnection<Integer> {
@@ -280,7 +281,7 @@
     /**
      * Test creation of LDAP listener with unknown transport provider.
      */
-    @SuppressWarnings({ "unused", "resource", "unchecked" })
+    @SuppressWarnings({ "unchecked" })
     @Test(expectedExceptions = { ProviderNotFoundException.class },
         expectedExceptionsMessageRegExp = "^The requested provider 'unknown' .*")
     public void testCreateLDAPListenerFailureProviderNotFound() throws Exception {
diff --git a/opendj3/opendj-ldap-toolkit/src/main/assembly/bat/makeldif.bat b/opendj3/opendj-ldap-toolkit/src/main/assembly/bat/makeldif.bat
new file mode 100755
index 0000000..67cb47a
--- /dev/null
+++ b/opendj3/opendj-ldap-toolkit/src/main/assembly/bat/makeldif.bat
@@ -0,0 +1,32 @@
+
+@echo off
+rem CDDL HEADER START
+rem
+rem The contents of this file are subject to the terms of the
+rem Common Development and Distribution License, Version 1.0 only
+rem (the "License").  You may not use this file except in compliance
+rem with the License.
+rem
+rem You can obtain a copy of the license at
+rem legal-notices/CDDLv1_0.txt
+rem or http://forgerock.org/license/CDDLv1.0.html.
+rem See the License for the specific language governing permissions
+rem and limitations under the License.
+rem
+rem When distributing Covered Code, include this CDDL HEADER in each
+rem file and include the License file at legal-notices/CDDLv1_0.txt.
+rem legal-notices/CDDLv1_0.txt.  If applicable,
+rem add the following below this CDDL HEADER, with the fields enclosed
+rem by brackets "[]" replaced with your own identifying information:
+rem      Portions Copyright [yyyy] [name of copyright owner]
+rem
+rem CDDL HEADER END
+rem
+rem
+rem      Copyright 2013 ForgeRock AS.
+
+setlocal
+
+set OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.MakeLDIF"
+set SCRIPT_NAME=makeldif
+for %%i in (%~sf0) do call "%%~dPsi\..\lib\_client-script.bat" %*
diff --git a/opendj3/opendj-ldap-toolkit/src/main/assembly/bin/makeldif b/opendj3/opendj-ldap-toolkit/src/main/assembly/bin/makeldif
new file mode 100644
index 0000000..58e7261
--- /dev/null
+++ b/opendj3/opendj-ldap-toolkit/src/main/assembly/bin/makeldif
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License").  You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at
+# trunk/opendj3/legal-notices/CDDLv1_0.txt
+# or http://forgerock.org/license/CDDLv1.0.html.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at
+# trunk/opendj3/legal-notices/CDDLv1_0.txt.  If applicable,
+# add the following below this CDDL HEADER, with the fields enclosed
+# by brackets "[]" replaced with your own identifying information:
+#      Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#      Copyright 2013 ForgeRock AS.
+
+
+# This script may be used to perform LDAP add, delete, modify, and modify DN
+# operations against an LDIF file.
+OPENDJ_INVOKE_CLASS="com.forgerock.opendj.ldap.tools.MakeLDIF"
+export OPENDJ_INVOKE_CLASS
+
+SCRIPT_NAME="makeldif"
+export SCRIPT_NAME
+
+SCRIPT_DIR=`dirname "${0}"`
+"${SCRIPT_DIR}/../lib/_client-script.sh" "${@}"
diff --git a/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java b/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
index 9f64f90..29c0435 100644
--- a/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
+++ b/opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java
@@ -128,7 +128,7 @@
                 return EXIT_CODE_FAILURE;
             }
 
-            if (generator.hasWarning()) {
+            if (generator.hasWarnings()) {
                 for (LocalizableMessage warn : generator.getWarnings()) {
                     println(warn);
                 }
diff --git a/opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm b/opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm
index 64d4a6c..5a3f7ee 100644
--- a/opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm
+++ b/opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm
@@ -41,6 +41,7 @@
        <dt>ldifmodify</dt><dd>perform LDAP modify, add, delete, mod DN operations against entries contained in an LDIF file</dd>
        <dt>ldifsearch</dt><dd>perform search operations against entries contained in an LDIF file</dd>
        <dt>ldifdiff</dt><dd>compare two LDIF files and report the differences in LDIF format</dd>
+       <dt>makeldif</dt><dd>generate LDIF content from and LDIF template</dd>
        <dt>modrate</dt><dd>measure modification throughput and response time</dd>
        <dt>searchrate</dt><dd>measure search throughput and response time</dd>
       </dl>
diff --git a/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java b/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
index d459ffe..ce3b916 100644
--- a/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
+++ b/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java
@@ -23,7 +23,6 @@
 import static org.forgerock.json.fluent.JsonValue.object;
 import static org.forgerock.json.resource.PatchOperation.add;
 import static org.forgerock.json.resource.PatchOperation.increment;
-import static org.forgerock.json.resource.PatchOperation.operation;
 import static org.forgerock.json.resource.PatchOperation.remove;
 import static org.forgerock.json.resource.PatchOperation.replace;
 import static org.forgerock.json.resource.Requests.newDeleteRequest;
@@ -426,13 +425,6 @@
         connection.patch(ctx(), newPatchRequest("/test1", add("/dummy", "junk")));
     }
 
-    @Test(expectedExceptions = NotSupportedException.class)
-    public void testPatchUnknownOperation() throws Exception {
-        final Connection connection = newConnection();
-        connection.patch(ctx(), newPatchRequest("/test1", operation("dummy", "/description",
-                asList("one", "two"))));
-    }
-
     @Test(expectedExceptions = BadRequestException.class)
     public void testPatchUnknownSubAttribute() throws Exception {
         final Connection connection = newConnection();
diff --git a/opendj3/pom.xml b/opendj3/pom.xml
index 0957081..5e502ba 100644
--- a/opendj3/pom.xml
+++ b/opendj3/pom.xml
@@ -99,6 +99,7 @@
     <module>opendj-server2x-adapter</module>
   </modules>
   <properties>
+    <forgerockBuildToolsVersion>1.0.2</forgerockBuildToolsVersion>
     <currentSDKversion>${project.version}</currentSDKversion>
     <stableSDKversion>version.not.defined</stableSDKversion>
     <currentServerVersion>2.5.0-SNAPSHOT</currentServerVersion>

--
Gitblit v1.10.0