mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Matthew Swift
26.30.2013 23766aa898d550284dea2ed51a7828584c303b6c
Minor code cleanup and improvements:

* remove lots of duplicate and unused code
* moved LDAPMessageHandler into io package since it is required by LDAPReader
* added LDAP utility class for creating LDAPReader / LDAPWriter as well as static methods for common encoding/decoding (previously in LDAPUtils)
* minor Javadoc improvements simplifications
* minor simplifications to NameForm builder
* added makeldif shell scripts and updated site
* fixed rest2ldap build which was broken due to recent changes to json-resource.

8 files deleted
5 files added
42 files modified
7559 ■■■■■ changed files
opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPConstants.java 326 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPUtils.java 622 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/ASN1.java 12 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/AbstractLDAPMessageHandler.java 254 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAP.java 843 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPMessageHandler.java 399 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java 1007 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java 1167 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/package-info.java 3 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/AssertionRequestControl.java 14 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/MatchedValuesRequestControl.java 10 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PostReadResponseControl.java 21 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/controls/PreReadResponseControl.java 21 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/AbstractSASLBindRequest.java 5 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/requests/SimpleBindRequestImpl.java 5 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java 37 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRule.java 33 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java 33 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRule.java 31 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUse.java 33 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameForm.java 594 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java 39 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java 8 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java 244 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElementBuilder.java 157 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java 35 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPMessageHandler.java 188 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPMessageHandler.java 392 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedRequestException.java 75 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedResponseException.java 75 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnsupportedMessageException.java 88 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/EntryGenerator.java 34 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java 21 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateFile.java 8 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldif/TemplateTag.java 1 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java 145 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/LDAPServer.java 7 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/MockConnectionEventListener.java 3 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/BindRequestTestCase.java 4 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/CompareRequestTestCase.java 2 ●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/requests/GenericBindRequestTestCase.java 8 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AbstractSchemaElementTestCase.java 22 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/NameFormTestCase.java 93 ●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java 98 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java 7 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPBaseFilter.java 2 ●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java 124 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java 124 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListenerTestCase.java 3 ●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/makeldif.bat 32 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/makeldif 38 ●●●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/MakeLDIF.java 2 ●●● patch | view | raw | blame | history
opendj3/opendj-ldap-toolkit/src/site/xdoc/index.xml.vm 1 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/BasicRequestsTest.java 8 ●●●●● patch | view | raw | blame | history
opendj3/pom.xml 1 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPConstants.java
File was deleted
opendj3/opendj-core/src/main/java/com/forgerock/opendj/ldap/LDAPUtils.java
File was deleted
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.
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/AbstractLDAPMessageHandler.java
New file
@@ -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));
    }
}
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAP.java
New file
@@ -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.
    }
}
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPMessageHandler.java
New file
@@ -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;
}
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);
    }
}
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();
    }
}
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
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.
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();
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.
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.
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
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
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");
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");
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");
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");
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");
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 ");
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");
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 =
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);
}
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElementBuilder.java
File was deleted
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")) {
                    /**
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPMessageHandler.java
File was deleted
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPMessageHandler.java
File was deleted
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedRequestException.java
File was deleted
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedResponseException.java
File was deleted
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnsupportedMessageException.java
File was deleted
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);
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) {
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;
        /**
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 {
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);
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());
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);
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");
        }
    }
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 {
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"))
        };
    }
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);
    }
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");
    }
}
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;
    }
}
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;
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;
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);
    }
}
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);
        }
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 {
opendj3/opendj-ldap-toolkit/src/main/assembly/bat/makeldif.bat
New file
@@ -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" %*
opendj3/opendj-ldap-toolkit/src/main/assembly/bin/makeldif
New file
@@ -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" "${@}"
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);
                }
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>
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();
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>