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

Nicolas Capponi
25.44.2013 38fe921bd2d09734277828b638a4947e45a99bcd
Checkpoint commit for OPENDJ-175: Decouple OpenDJ LDAP SDK from Grizzly

Move non-specific Grizzly code to opendj-core and refactor LDAPReader and LDAPWriter.

* Add new package org.forgerock.opendj.io in opendj-core for all IO related features
** org.forgerock.opendj.asn1 classes to be moved in this io package in a future commit

* Refactor LDAPReader class
** use an ASN1Reader as field instead of passing it as arg of each method
** rename methods to readXX
** add hasMessageAvailable method
** move to org.forgerock.opendj.io package in opendj-core

* Refactor LDAPWriter class
** use an ASN1Writer as field instead of passing it as arg of each method
** rename methods to writeXX
** remove implementation of LDAPMessageHandler
** move to org.forgerock.opendj.io package in opendj-core

* Adapt GrizzlyLDAPConnection, GrizzlyLDAPConnectionFactory, GrizzlyLDAPListener,
LDAPClientFilter and LDAPServerFilter classes to changes in LDAPReader and LDAPWriter
** caching of LDAPReader per connection
** caching of LDAPWriter per thread
** new utility methods in GrizzlyUtils class to handle creation and recycling of LDAPReader and
LDAPWriter instances

* Refactor LDAPMessageHandler and AbstractLDAPMessageHandler classes
** remove user provided P parameter
** move to org.forgerock.opendj.ldap.spi package in opendj-core

* Move UnexpectedRequestException, UnexpectedResponseException and UnsupportedMessageexception classes
to org.forgerock.opendj.ldap.spi package in opendj-core

* Convert TimeoutChecker class to a generic timeout facility which is not tied to connections.
** new interface TimeoutEventListener to represent listeners on timeout event
** TimeoutChecker send callbacks to listeners instead of connections
** move to org.forgerock.opendj.ldap package in opendj-core

* Fix issues with TimeoutChecker and usage in GrizzlyLDAPConnection
** prevent negative delays
** wakeup of listeners only if timeout is > 0

* Add test case support for LDAPReader/LDAPWriter in opendj-core and
specific implementation of test case in opendj-grizzly
2 files deleted
6 files added
6 files renamed
10 files modified
3610 ■■■■■ changed files
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java 452 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java 724 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/package-info.java 31 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/TimeoutChecker.java 54 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/TimeoutEventListener.java 56 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPMessageHandler.java 102 ●●●●● 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 25 ●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedResponseException.java 23 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnsupportedMessageException.java 30 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java 178 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java 11 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferWriter.java 28 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java 115 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java 7 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListener.java 2 ●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java 62 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java 195 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPMessageHandler.java 122 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java 453 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPWriter.java 481 ●●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/ASN1BufferWriterTestCase.java 4 ●●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionTestCase.java 2 ●●● patch | view | raw | blame | history
opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPReaderWriterTestCase.java 61 ●●●●● patch | view | raw | blame | history
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPReader.java
File was renamed from opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPReader.java
@@ -25,14 +25,11 @@
 *      Portions copyright 2011-2013 ForgeRock AS
 */
package com.forgerock.opendj.grizzly;
package org.forgerock.opendj.io;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static com.forgerock.opendj.ldap.LDAPConstants.*;
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 static com.forgerock.opendj.util.StaticUtils.*;
import java.io.IOException;
@@ -77,73 +74,99 @@
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;
/**
 * Static methods for decoding LDAP messages.
 * Responsible for reading LDAP messages.
 *
 * @param <R>
 *            type of ASN1 reader used to decode elements
 */
final class LDAPReader {
public final class LDAPReader<R extends ASN1Reader> {
    private final R reader;
    private final DecodeOptions options;
    LDAPReader(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) {
        this.reader = asn1Reader;
        this.options = options;
    }
    /**
     * Decodes the elements from the provided ASN.1 reader as an LDAP message.
     * Returns the ASN1 reader used to decode elements.
     *
     * @param <P>
     *            The type of {@code param}.
     * @param reader
     *            The ASN.1 reader.
     * @return ASN1 reader
     */
    public R getASN1Reader() {
        return reader;
    }
    /**
     * Read a LDAP message by decoding the elements from the provided ASN.1 asn1Reader.
     *
     * @param handler
     *            The <code>LDAPMessageHandler</code> that will handle a decoded
     *            message.
     * @param param
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    <P> void decode(final ASN1Reader reader, final LDAPMessageHandler<P> handler, final P param)
    public void readMessage(final LDAPMessageHandler handler)
            throws IOException {
        reader.readStartSequence();
        try {
            final int messageID = (int) reader.readInteger();
            decodeProtocolOp(reader, messageID, handler, param);
            readProtocolOp(messageID, handler);
        } finally {
            reader.readEndSequence();
        }
    }
    /**
     * 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 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeAbandonRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readAbandonRequest(final int messageID, final LDAPMessageHandler handler) throws IOException {
        final int msgToAbandon = (int) reader.readInteger(OP_TYPE_ABANDON_REQUEST);
        final AbandonRequest message = Requests.newAbandonRequest(msgToAbandon);
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP ABANDON REQUEST(messageID={}, request={})", messageID, message);
        handler.abandonRequest(p, messageID, message);
        handler.abandonRequest(messageID, message);
    }
    /**
@@ -151,26 +174,24 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeAddRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    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 = decodeDN(dnString, schema);
            final DN dn = readDN(dnString, schema);
            entry = options.getEntryFactory().newEntry(dn);
            reader.readStartSequence();
@@ -179,7 +200,7 @@
                    reader.readStartSequence();
                    try {
                        final String ads = reader.readOctetStringAsString();
                        final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                        final AttributeDescription ad = readAttributeDescription(ads, schema);
                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
                        reader.readStartSet();
@@ -203,11 +224,11 @@
        }
        final AddRequest message = Requests.newAddRequest(entry);
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP ADD REQUEST(messageID={}, request={})", messageID, message);
        handler.addRequest(p, messageID, message);
        handler.addRequest(messageID, message);
    }
    /**
@@ -215,19 +236,17 @@
     * protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeAddResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readAddResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_ADD_RESPONSE);
@@ -238,19 +257,19 @@
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP ADD RESULT(messageID={}, result={})", messageID, message);
        handler.addResult(p, messageID, message);
        handler.addResult(messageID, message);
    }
    private AttributeDescription decodeAttributeDescription(final String attributeDescription,
    private AttributeDescription readAttributeDescription(final String attributeDescription,
            final Schema schema) throws DecodeException {
        try {
            return AttributeDescription.valueOf(attributeDescription, schema);
@@ -264,19 +283,17 @@
     * protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeBindRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readBindRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        reader.readStartSequence(OP_TYPE_BIND_REQUEST);
        try {
            final int protocolVersion = (int) reader.readInteger();
@@ -287,12 +304,12 @@
            final GenericBindRequest request =
                    Requests.newGenericBindRequest(authName, authType, authBytes);
            decodeControls(reader, request);
            readControls(request);
            IO_LOG.trace("DECODE LDAP BIND REQUEST(messageID={}, auth=0x{}, request={})", messageID,
                    byteToHex(request.getAuthenticationType()), request);
            handler.bindRequest(p, messageID, protocolVersion, request);
            handler.bindRequest(messageID, protocolVersion, request);
        } finally {
            reader.readEndSequence();
        }
@@ -303,19 +320,17 @@
     * protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeBindResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readBindResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        BindResult message;
        reader.readStartSequence(OP_TYPE_BIND_RESPONSE);
@@ -326,7 +341,7 @@
            message =
                    Responses.newBindResult(resultCode).setMatchedDN(matchedDN)
                            .setDiagnosticMessage(diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
            if (reader.hasNextElement() && (reader.peekType() == TYPE_SERVER_SASL_CREDENTIALS)) {
                message.setServerSASLCredentials(reader
                        .readOctetString(TYPE_SERVER_SASL_CREDENTIALS));
@@ -335,11 +350,11 @@
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP BIND RESULT(messageID={}, result={})", messageID, message);
        handler.bindResult(p, messageID, message);
        handler.bindResult(messageID, message);
    }
    /**
@@ -347,31 +362,29 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeCompareRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readCompareRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        CompareRequest message;
        reader.readStartSequence(OP_TYPE_COMPARE_REQUEST);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            final DN dn = readDN(dnString, schema);
            reader.readStartSequence();
            try {
                final String ads = reader.readOctetStringAsString();
                final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                final AttributeDescription ad = readAttributeDescription(ads, schema);
                final ByteString assertionValue = reader.readOctetString();
                message = Requests.newCompareRequest(dn, ad, assertionValue);
            } finally {
@@ -381,11 +394,11 @@
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP COMPARE REQUEST(messageID={}, request={})", messageID, message);
        handler.compareRequest(p, messageID, message);
        handler.compareRequest(messageID, message);
    }
    /**
@@ -393,19 +406,17 @@
     * protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeCompareResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readCompareResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        CompareResult message;
        reader.readStartSequence(OP_TYPE_COMPARE_RESPONSE);
@@ -416,29 +427,29 @@
            message =
                    Responses.newCompareResult(resultCode).setMatchedDN(matchedDN)
                            .setDiagnosticMessage(diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP COMPARE RESULT(messageID={}, result={})", messageID, message);
        handler.compareResult(p, 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.
     *            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 decodeControl(final ASN1Reader reader, final Request request) throws IOException {
    private void readControl(final Request request) throws IOException {
        String oid;
        boolean isCritical;
        ByteString value;
@@ -466,13 +477,13 @@
     * Decodes the elements from the provided ASN.1 reader as an LDAP control.
     *
     * @param reader
     *            The ASN.1 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 decodeControl(final ASN1Reader reader, final Response response) throws IOException {
    private void readControl(final Response response) throws IOException {
        String oid;
        boolean isCritical;
        ByteString value;
@@ -500,18 +511,18 @@
     * Decodes the elements from the provided ASN.1 reader as a set of controls.
     *
     * @param reader
     *            The ASN.1 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 decodeControls(final ASN1Reader reader, final Request request) throws IOException {
    private void readControls(final Request request) throws IOException {
        if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE)) {
            reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
            try {
                while (reader.hasNextElement()) {
                    decodeControl(reader, request);
                    readControl(request);
                }
            } finally {
                reader.readEndSequence();
@@ -523,19 +534,19 @@
     * Decodes the elements from the provided ASN.1 reader as a set of controls.
     *
     * @param reader
     *            The ASN.1 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 decodeControls(final ASN1Reader reader, final Response response)
    private void readControls(final Response response)
            throws IOException {
        if (reader.hasNextElement() && (reader.peekType() == TYPE_CONTROL_SEQUENCE)) {
            reader.readStartSequence(TYPE_CONTROL_SEQUENCE);
            try {
                while (reader.hasNextElement()) {
                    decodeControl(reader, response);
                    readControl(response);
                }
            } finally {
                reader.readEndSequence();
@@ -548,29 +559,27 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeDeleteRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readDeleteRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        final String dnString = reader.readOctetStringAsString(OP_TYPE_DELETE_REQUEST);
        final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
        final DN dn = decodeDN(dnString, schema);
        final DN dn = readDN(dnString, schema);
        final DeleteRequest message = Requests.newDeleteRequest(dn);
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP DELETE REQUEST(messageID={}, request={})", messageID, message);
        handler.deleteRequest(p, messageID, message);
        handler.deleteRequest(messageID, message);
    }
    /**
@@ -578,19 +587,17 @@
     * protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeDeleteResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readDeleteResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_DELETE_RESPONSE);
@@ -601,19 +608,19 @@
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP DELETE RESULT(messageID={}, result={})", messageID, message);
        handler.deleteResult(p, messageID, message);
        handler.deleteResult(messageID, message);
    }
    private DN decodeDN(final String dn, final Schema schema) throws DecodeException {
    private DN readDN(final String dn, final Schema schema) throws DecodeException {
        try {
            return DN.valueOf(dn, schema);
        } catch (final LocalizedIllegalArgumentException e) {
@@ -626,19 +633,17 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeExtendedRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readExtendedRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        String oid;
        ByteString value;
@@ -655,11 +660,11 @@
        final GenericExtendedRequest message = Requests.newGenericExtendedRequest(oid, value);
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP EXTENDED REQUEST(messageID={}, request={})", messageID, message);
        handler.extendedRequest(p, messageID, message);
        handler.extendedRequest(messageID, message);
    }
    /**
@@ -667,19 +672,17 @@
     * response protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeExtendedResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readExtendedResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        GenericExtendedResult message;
@@ -691,7 +694,7 @@
            message =
                    Responses.newGenericExtendedResult(resultCode).setMatchedDN(matchedDN)
                            .setDiagnosticMessage(diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
            if (reader.hasNextElement() && (reader.peekType() == TYPE_EXTENDED_RESPONSE_OID)) {
                message.setOID(reader.readOctetStringAsString(TYPE_EXTENDED_RESPONSE_OID));
            }
@@ -702,11 +705,11 @@
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP EXTENDED RESULT(messageID={}, result={})", messageID, message);
        handler.extendedResult(p, messageID, message);
        handler.extendedResult(messageID, message);
    }
    /**
@@ -714,19 +717,17 @@
     * intermediate response protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeIntermediateResponse(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readIntermediateResponse(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        GenericIntermediateResponse message;
        reader.readStartSequence(OP_TYPE_INTERMEDIATE_RESPONSE);
@@ -742,12 +743,12 @@
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP INTERMEDIATE RESPONSE(messageID={}, response={})",
                messageID, message);
        handler.intermediateResponse(p, messageID, message);
        handler.intermediateResponse(messageID, message);
    }
    /**
@@ -755,29 +756,27 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyDNRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readModifyDNRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        ModifyDNRequest message;
        reader.readStartSequence(OP_TYPE_MODIFY_DN_REQUEST);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            final DN dn = readDN(dnString, schema);
            final String newRDNString = reader.readOctetStringAsString();
            final RDN newRDN = decodeRDN(newRDNString, schema);
            final RDN newRDN = readRDN(newRDNString, schema);
            message = Requests.newModifyDNRequest(dn, newRDN);
@@ -786,18 +785,18 @@
            if (reader.hasNextElement() && (reader.peekType() == TYPE_MODIFY_DN_NEW_SUPERIOR)) {
                final String newSuperiorString =
                        reader.readOctetStringAsString(TYPE_MODIFY_DN_NEW_SUPERIOR);
                final DN newSuperior = decodeDN(newSuperiorString, schema);
                final DN newSuperior = readDN(newSuperiorString, schema);
                message.setNewSuperior(newSuperior);
            }
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP MODIFY DN REQUEST(messageID={}, request={})", messageID, message);
        handler.modifyDNRequest(p, messageID, message);
        handler.modifyDNRequest(messageID, message);
    }
    /**
@@ -805,19 +804,17 @@
     * response protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyDNResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readModifyDNResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_MODIFY_DN_RESPONSE);
@@ -828,16 +825,16 @@
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE MODIFY DN RESULT(messageID={}, result={})", messageID, message);
        handler.modifyDNResult(p, messageID, message);
        handler.modifyDNResult(messageID, message);
    }
    /**
@@ -845,26 +842,24 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readModifyRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        ModifyRequest message;
        reader.readStartSequence(OP_TYPE_MODIFY_REQUEST);
        try {
            final String dnString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(dnString);
            final DN dn = decodeDN(dnString, schema);
            final DN dn = readDN(dnString, schema);
            message = Requests.newModifyRequest(dn);
            reader.readStartSequence();
@@ -882,7 +877,7 @@
                        reader.readStartSequence();
                        try {
                            final String ads = reader.readOctetStringAsString();
                            final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                            final AttributeDescription ad = readAttributeDescription(ads, schema);
                            final Attribute attribute =
                                    options.getAttributeFactory().newAttribute(ad);
@@ -909,11 +904,11 @@
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP MODIFY REQUEST(messageID={}, request={})", messageID, message);
        handler.modifyRequest(p, messageID, message);
        handler.modifyRequest(messageID, message);
    }
    /**
@@ -921,19 +916,17 @@
     * protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeModifyResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readModifyResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        Result message;
        reader.readStartSequence(OP_TYPE_MODIFY_RESPONSE);
@@ -944,16 +937,16 @@
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP MODIFY RESULT(messageID={}, result={})", messageID, message);
        handler.modifyResult(p, messageID, message);
        handler.modifyResult(messageID, message);
    }
    /**
@@ -961,24 +954,22 @@
     * op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeProtocolOp(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) 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
            decodeUnbindRequest(reader, messageID, handler, p);
            readUnbindRequest(messageID, handler);
            break;
        case 0x43: // 0x43
        case 0x44: // 0x44
@@ -987,20 +978,20 @@
        case 0x47: // 0x47
        case 0x48: // 0x48
        case 0x49: // 0x49
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_DELETE_REQUEST: // 0x4A
            decodeDeleteRequest(reader, messageID, handler, p);
            readDeleteRequest(messageID, handler);
            break;
        case 0x4B: // 0x4B
        case 0x4C: // 0x4C
        case 0x4D: // 0x4D
        case 0x4E: // 0x4E
        case 0x4F: // 0x4F
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_ABANDON_REQUEST: // 0x50
            decodeAbandonRequest(reader, messageID, handler, p);
            readAbandonRequest(messageID, handler);
            break;
        case 0x51: // 0x51
        case 0x52: // 0x52
@@ -1017,85 +1008,85 @@
        case 0x5D: // 0x5D
        case 0x5E: // 0x5E
        case 0x5F: // 0x5F
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_BIND_REQUEST: // 0x60
            decodeBindRequest(reader, messageID, handler, p);
            readBindRequest(messageID, handler);
            break;
        case OP_TYPE_BIND_RESPONSE: // 0x61
            decodeBindResult(reader, messageID, handler, p);
            readBindResult(messageID, handler);
            break;
        case 0x62: // 0x62
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_SEARCH_REQUEST: // 0x63
            decodeSearchRequest(reader, messageID, handler, p);
            readSearchRequest(messageID, handler);
            break;
        case OP_TYPE_SEARCH_RESULT_ENTRY: // 0x64
            decodeSearchResultEntry(reader, messageID, handler, p);
            readSearchResultEntry(messageID, handler);
            break;
        case OP_TYPE_SEARCH_RESULT_DONE: // 0x65
            decodeSearchResult(reader, messageID, handler, p);
            readSearchResult(messageID, handler);
            break;
        case OP_TYPE_MODIFY_REQUEST: // 0x66
            decodeModifyRequest(reader, messageID, handler, p);
            readModifyRequest(messageID, handler);
            break;
        case OP_TYPE_MODIFY_RESPONSE: // 0x67
            decodeModifyResult(reader, messageID, handler, p);
            readModifyResult(messageID, handler);
            break;
        case OP_TYPE_ADD_REQUEST: // 0x68
            decodeAddRequest(reader, messageID, handler, p);
            readAddRequest(messageID, handler);
            break;
        case OP_TYPE_ADD_RESPONSE: // 0x69
            decodeAddResult(reader, messageID, handler, p);
            readAddResult(messageID, handler);
            break;
        case 0x6A: // 0x6A
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_DELETE_RESPONSE: // 0x6B
            decodeDeleteResult(reader, messageID, handler, p);
            readDeleteResult(messageID, handler);
            break;
        case OP_TYPE_MODIFY_DN_REQUEST: // 0x6C
            decodeModifyDNRequest(reader, messageID, handler, p);
            readModifyDNRequest(messageID, handler);
            break;
        case OP_TYPE_MODIFY_DN_RESPONSE: // 0x6D
            decodeModifyDNResult(reader, messageID, handler, p);
            readModifyDNResult(messageID, handler);
            break;
        case OP_TYPE_COMPARE_REQUEST: // 0x6E
            decodeCompareRequest(reader, messageID, handler, p);
            readCompareRequest(messageID, handler);
            break;
        case OP_TYPE_COMPARE_RESPONSE: // 0x6F
            decodeCompareResult(reader, messageID, handler, p);
            readCompareResult(messageID, handler);
            break;
        case 0x70: // 0x70
        case 0x71: // 0x71
        case 0x72: // 0x72
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_SEARCH_RESULT_REFERENCE: // 0x73
            decodeSearchResultReference(reader, messageID, handler, p);
            readSearchResultReference(messageID, handler);
            break;
        case 0x74: // 0x74
        case 0x75: // 0x75
        case 0x76: // 0x76
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        case OP_TYPE_EXTENDED_REQUEST: // 0x77
            decodeExtendedRequest(reader, messageID, handler, p);
            readExtendedRequest(messageID, handler);
            break;
        case OP_TYPE_EXTENDED_RESPONSE: // 0x78
            decodeExtendedResult(reader, messageID, handler, p);
            readExtendedResult(messageID, handler);
            break;
        case OP_TYPE_INTERMEDIATE_RESPONSE: // 0x79
            decodeIntermediateResponse(reader, messageID, handler, p);
            readIntermediateResponse(messageID, handler);
            break;
        default:
            handler.unrecognizedMessage(p, messageID, type, reader.readOctetString(type));
            handler.unrecognizedMessage(messageID, type, reader.readOctetString(type));
            break;
        }
    }
    private RDN decodeRDN(final String rdn, final Schema schema) throws DecodeException {
    private RDN readRDN(final String rdn, final Schema schema) throws DecodeException {
        try {
            return RDN.valueOf(rdn, schema);
        } catch (final LocalizedIllegalArgumentException e) {
@@ -1103,7 +1094,7 @@
        }
    }
    private void decodeResponseReferrals(final ASN1Reader reader, final Result message)
    private void readResponseReferrals(final Result message)
            throws IOException {
        if (reader.hasNextElement() && (reader.peekType() == TYPE_REFERRAL_SEQUENCE)) {
            reader.readStartSequence(TYPE_REFERRAL_SEQUENCE);
@@ -1123,26 +1114,24 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readSearchRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        SearchRequest message;
        reader.readStartSequence(OP_TYPE_SEARCH_REQUEST);
        try {
            final String baseDNString = reader.readOctetStringAsString();
            final Schema schema = options.getSchemaResolver().resolveSchema(baseDNString);
            final DN baseDN = decodeDN(baseDNString, schema);
            final DN baseDN = readDN(baseDNString, schema);
            final int scopeIntValue = reader.readEnumerated();
            final SearchScope scope = SearchScope.valueOf(scopeIntValue);
@@ -1186,11 +1175,11 @@
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP SEARCH REQUEST(messageID={}, request={})", messageID, message);
        handler.searchRequest(p, messageID, message);
        handler.searchRequest(messageID, message);
    }
    /**
@@ -1198,19 +1187,17 @@
     * done protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchResult(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readSearchResult(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        Result message;
@@ -1222,16 +1209,16 @@
            message =
                    Responses.newResult(resultCode).setMatchedDN(matchedDN).setDiagnosticMessage(
                            diagnosticMessage);
            decodeResponseReferrals(reader, message);
            readResponseReferrals(message);
        } finally {
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP SEARCH RESULT(messageID={}, result={})", messageID, message);
        handler.searchResult(p, messageID, message);
        handler.searchResult(messageID, message);
    }
    /**
@@ -1239,26 +1226,25 @@
     * result entry protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchResultEntry(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    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 = decodeDN(dnString, schema);
            final DN dn = readDN(dnString, schema);
            entry = options.getEntryFactory().newEntry(dn);
            reader.readStartSequence();
@@ -1267,7 +1253,7 @@
                    reader.readStartSequence();
                    try {
                        final String ads = reader.readOctetStringAsString();
                        final AttributeDescription ad = decodeAttributeDescription(ads, schema);
                        final AttributeDescription ad = readAttributeDescription(ads, schema);
                        final Attribute attribute = options.getAttributeFactory().newAttribute(ad);
                        reader.readStartSet();
@@ -1291,11 +1277,11 @@
        }
        final SearchResultEntry message = Responses.newSearchResultEntry(entry);
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP SEARCH RESULT ENTRY(messageID={}, entry={})", messageID, message);
        handler.searchResultEntry(p, messageID, message);
        handler.searchResultEntry(messageID, message);
    }
    /**
@@ -1303,19 +1289,17 @@
     * reference protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeSearchResultReference(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readSearchResultReference(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        SearchResultReference message;
        reader.readStartSequence(OP_TYPE_SEARCH_RESULT_REFERENCE);
@@ -1328,12 +1312,12 @@
            reader.readEndSequence();
        }
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP SEARCH RESULT REFERENCE(messageID={}, result={})",
                messageID, message);
        handler.searchResultReference(p, messageID, message);
        handler.searchResultReference(messageID, message);
    }
    /**
@@ -1341,27 +1325,25 @@
     * request protocol op.
     *
     * @param reader
     *            The ASN.1 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.
     * @param p
     *            The parameter to pass into the <code>LDAPMessageHandler</code>
     * @throws IOException
     *             If an error occurred while reading bytes to decode.
     */
    private <P> void decodeUnbindRequest(final ASN1Reader reader, final int messageID,
            final LDAPMessageHandler<P> handler, final P p) throws IOException {
    private void readUnbindRequest(final int messageID,
            final LDAPMessageHandler handler) throws IOException {
        UnbindRequest message;
        reader.readNull(OP_TYPE_UNBIND_REQUEST);
        message = Requests.newUnbindRequest();
        decodeControls(reader, message);
        readControls(message);
        IO_LOG.trace("DECODE LDAP UNBIND REQUEST(messageID={}, request={})", messageID, message);
        handler.unbindRequest(p, messageID, message);
        handler.unbindRequest(messageID, message);
    }
}
opendj3/opendj-core/src/main/java/org/forgerock/opendj/io/LDAPWriter.java
New file
@@ -0,0 +1,724 @@
/*
 * 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-2010 Sun Microsystems, Inc.
 *      Portions copyright 2011-2013 ForgeRock AS
 */
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.asn1.ASN1Writer;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.DN;
import org.forgerock.opendj.ldap.Modification;
import org.forgerock.opendj.ldap.controls.Control;
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;
import com.forgerock.opendj.ldap.LDAPUtils;
import com.forgerock.opendj.util.StaticUtils;
/**
 * Responsible for writing LDAP messages.
 *
 * @param <W>
 *            type of ASN1 writer used to encode elements
 */
public final class LDAPWriter<W extends ASN1Writer> {
    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) {
        this.writer = asn1Writer;
    }
    /**
     * Returns the ASN1 writer used to encode elements.
     *
     * @return the ASN1 writer
     */
    public W getASN1Writer() {
        return writer;
    }
    /**
     * Write the provided control.
     *
     * @param control
     *            the control
     * @throws IOException
     *             if an error occurs during write operation
     */
    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.writeEndSequence();
    }
    /**
     * Write the provided entry.
     *
     * @param searchResultEntry
     *            entry
     * @throws IOException
     *             if an error occurs during write operation
     */
    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);
        }
        writer.writeEndSequence();
        writer.writeEndSequence();
    }
    private void writeAttribute(final Attribute attribute)
            throws IOException {
        writer.writeStartSequence();
        writer.writeOctetString(attribute.getAttributeDescriptionAsString());
        writer.writeStartSet();
        for (final ByteString value : attribute) {
            writer.writeOctetString(value);
        }
        writer.writeEndSequence();
        writer.writeEndSequence();
    }
    private void writeChange(final Modification change)
            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);
            }
            writer.writeEndSequence();
        }
        writer.writeEndSequence();
    }
    private void writeMessageFooter(final Response response)
            throws IOException {
        final List<Control> controls = response.getControls();
        if (!controls.isEmpty()) {
            writer.writeStartSequence(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);
    }
    private void writeResultFooter(final ASN1Writer writer) throws IOException {
        writer.writeEndSequence();
    }
    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.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
New file
@@ -0,0 +1,31 @@
/*
 * 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.
 */
/**
 * Classes providing I/O functionality.
 */
package org.forgerock.opendj.io;
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/TimeoutChecker.java
File was renamed from opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/TimeoutChecker.java
@@ -25,7 +25,7 @@
 *      Portions copyright 2013 ForgeRock AS.
 */
package com.forgerock.opendj.grizzly;
package org.forgerock.opendj.ldap;
import static com.forgerock.opendj.util.StaticUtils.DEFAULT_LOG;
import static java.util.Collections.newSetFromMap;
@@ -35,13 +35,19 @@
import com.forgerock.opendj.util.ReferenceCountedObject;
/**
 * Checks connection for pending requests that have timed out.
 * Checks {@code TimeoutEventListener listeners} for events that have timed out.
 * <p>
 * All listeners registered with the {@code #addListener()} method are called
 * back with {@code TimeoutEventListener#handleTimeout()} to be able to handle
 * the timeout.
 * <p>
 *
 */
final class TimeoutChecker {
public final class TimeoutChecker {
    /**
     * Global reference counted instance.
     * Global reference on the timeout checker.
     */
    static final ReferenceCountedObject<TimeoutChecker> TIMEOUT_CHECKER =
    public static final ReferenceCountedObject<TimeoutChecker> TIMEOUT_CHECKER =
            new ReferenceCountedObject<TimeoutChecker>() {
                @Override
                protected void destroyInstance(final TimeoutChecker instance) {
@@ -60,11 +66,12 @@
    private final Object available = new Object();
    /**
     * The connection set must be safe from CMEs because expiring requests can
     * The listener set must be safe from CMEs.
     * For example, if the listener is a connection, expiring requests can
     * cause the connection to be closed.
     */
    private final Set<GrizzlyLDAPConnection> connections =
            newSetFromMap(new ConcurrentHashMap<GrizzlyLDAPConnection, Boolean>());
    private final Set<TimeoutEventListener> listeners =
            newSetFromMap(new ConcurrentHashMap<TimeoutEventListener, Boolean>());
    /**
     * Used to signal thread shutdown.
@@ -72,19 +79,18 @@
    private volatile boolean shutdownRequested = false;
    private TimeoutChecker() {
        final Thread checkerThread = new Thread("OpenDJ LDAP SDK Connection Timeout Checker") {
        final Thread checkerThread = new Thread("OpenDJ LDAP SDK Timeout Checker") {
            @Override
            public void run() {
                DEFAULT_LOG.debug("Timeout Checker Starting");
                while (!shutdownRequested) {
                    final long currentTime = System.currentTimeMillis();
                    long delay = 0;
                    for (final GrizzlyLDAPConnection connection : connections) {
                        DEFAULT_LOG.trace("Checking connection {} delay = {}", connection, delay);
                    for (final TimeoutEventListener listener : listeners) {
                        DEFAULT_LOG.trace("Checking connection {} delay = {}", listener, delay);
                        // May update the connections set.
                        final long newDelay = connection.cancelExpiredRequests(currentTime);
                        final long newDelay = listener.handleTimeout(currentTime);
                        if (newDelay > 0) {
                            if (delay > 0) {
                                delay = Math.min(newDelay, delay);
@@ -113,13 +119,27 @@
        checkerThread.start();
    }
    void addConnection(final GrizzlyLDAPConnection connection) {
        connections.add(connection);
    /**
     * Add a listener to check.
     *
     * @param listener
     *            listener to check for timeout event
     */
    public void addListener(final TimeoutEventListener listener) {
        listeners.add(listener);
        if (listener.getTimeout() > 0) {
        signal();
    }
    }
    void removeConnection(final GrizzlyLDAPConnection connection) {
        connections.remove(connection);
    /**
     * Stop checking a listener.
     *
     * @param listener
     *            listener that was previously added to check for timeout event
     */
    public void removeListener(final TimeoutEventListener listener) {
        listeners.remove(listener);
        // No need to signal.
    }
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/TimeoutEventListener.java
New file
@@ -0,0 +1,56 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2013 ForgeRock AS.
 */
package org.forgerock.opendj.ldap;
/**
 * Listener on timeout events.
 * <p>
 * The listener must register itself with a {@code TimeoutChecker} using
 * {@code TimeoutChecker#addListener()} method to be called back with
 * {@code #handleTimeout} method.
 * <p>
 * The listener must deregister itself using
 * {@code TimeoutChecker#removeListener()} to stop being called back.
 */
public interface TimeoutEventListener {
    /**
     * Handle a timeout event.
     *
     * @param currentTime
     *            time to use as current time for any check
     * @return the delay to wait before next timeout callback in milliseconds
     */
    long handleTimeout(final long currentTime);
    /**
     * Returns the timeout for this listener.
     *
     * @return the timeout in milliseconds
     */
    long getTimeout();
}
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/AbstractLDAPMessageHandler.java
File was renamed from opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/AbstractLDAPMessageHandler.java
@@ -22,9 +22,10 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 */
package com.forgerock.opendj.grizzly;
package org.forgerock.opendj.ldap.spi;
import java.io.IOException;
@@ -48,119 +49,140 @@
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
 * Abstract LDAP message handler.
 *
 * @param <P>
 *            A user provided handler parameter.
 * Provides a LDAPMessageHandler implementation that throw an exception for all
 * methods.
 */
abstract class AbstractLDAPMessageHandler<P> implements LDAPMessageHandler<P> {
    public void abandonRequest(final P param, final int messageID, final AbandonRequest request)
public abstract class AbstractLDAPMessageHandler implements LDAPMessageHandler {
    @Override
    public void abandonRequest(final int messageID, final AbandonRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void addRequest(final P param, final int messageID, final AddRequest request)
    @Override
    public void addRequest(final int messageID, final AddRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void addResult(final P param, final int messageID, final Result result)
    @Override
    public void addResult(final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void bindRequest(final P param, final int messageID, final int version,
            final GenericBindRequest request) throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void bindResult(final P param, final int messageID, final BindResult result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void compareRequest(final P param, final int messageID, final CompareRequest request)
    @Override
    public void bindRequest(final int messageID, final int version, final GenericBindRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void compareResult(final P param, final int messageID, final CompareResult result)
    @Override
    public void bindResult(final int messageID, final BindResult result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void deleteRequest(final P param, final int messageID, final DeleteRequest request)
    @Override
    public void compareRequest(final int messageID, final CompareRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void deleteResult(final P param, final int messageID, final Result result)
    @Override
    public void compareResult(final int messageID, final CompareResult result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public <R extends ExtendedResult> void extendedRequest(final P param, final int messageID,
            final ExtendedRequest<R> request) throws UnexpectedRequestException, IOException {
    @Override
    public void deleteRequest(final int messageID, final DeleteRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void extendedResult(final P param, final int messageID, final ExtendedResult result)
    @Override
    public void deleteResult(final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void intermediateResponse(final P param, final int messageID,
            final IntermediateResponse response) throws UnexpectedResponseException, IOException {
    @Override
    public <R extends ExtendedResult> void extendedRequest(final int messageID, final ExtendedRequest<R> request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    @Override
    public void extendedResult(final int messageID, final ExtendedResult result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    @Override
    public void intermediateResponse(final int messageID, final IntermediateResponse response)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, response);
    }
    public void modifyDNRequest(final P param, final int messageID, final ModifyDNRequest request)
    @Override
    public void modifyDNRequest(final int messageID, final ModifyDNRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void modifyDNResult(final P param, final int messageID, final Result result)
    @Override
    public void modifyDNResult(final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void modifyRequest(final P param, final int messageID, final ModifyRequest request)
    @Override
    public void modifyRequest(final int messageID, final ModifyRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void modifyResult(final P param, final int messageID, final Result result)
    @Override
    public void modifyResult(final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void searchRequest(final P param, final int messageID, final SearchRequest request)
    @Override
    public void searchRequest(final int messageID, final SearchRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void searchResult(final P param, final int messageID, final Result result)
    @Override
    public void searchResult(final int messageID, final Result result)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, result);
    }
    public void searchResultEntry(final P param, final int messageID, final SearchResultEntry entry)
    @Override
    public void searchResultEntry(final int messageID, final SearchResultEntry entry)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, entry);
    }
    public void searchResultReference(final P param, final int messageID,
            final SearchResultReference reference) throws UnexpectedResponseException, IOException {
    @Override
    public void searchResultReference(final int messageID, final SearchResultReference reference)
            throws UnexpectedResponseException, IOException {
        throw new UnexpectedResponseException(messageID, reference);
    }
    public void unbindRequest(final P param, final int messageID, final UnbindRequest request)
    @Override
    public void unbindRequest(final int messageID, final UnbindRequest request)
            throws UnexpectedRequestException, IOException {
        throw new UnexpectedRequestException(messageID, request);
    }
    public void unrecognizedMessage(final P param, final int messageID, final byte messageTag,
            final ByteString messageBytes) throws UnsupportedMessageException, IOException {
    @Override
    public void unrecognizedMessage(final int messageID, final byte messageTag, final ByteString messageBytes)
            throws UnsupportedMessageException, IOException {
        throw new UnsupportedMessageException(messageID, messageTag, messageBytes);
    }
}
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/LDAPMessageHandler.java
New file
@@ -0,0 +1,392 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.spi;
import java.io.IOException;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.CompareRequest;
import org.forgerock.opendj.ldap.requests.DeleteRequest;
import org.forgerock.opendj.ldap.requests.ExtendedRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
import org.forgerock.opendj.ldap.requests.ModifyRequest;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.requests.UnbindRequest;
import org.forgerock.opendj.ldap.responses.BindResult;
import org.forgerock.opendj.ldap.responses.CompareResult;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
import org.forgerock.opendj.ldap.responses.IntermediateResponse;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
 * Interface for handler of LDAP messages (requests and responses).
 *
 */
public interface LDAPMessageHandler {
    /**
     * Handle an abandon request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            abandon request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void abandonRequest(int messageID, AbandonRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle an add request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            add request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void addRequest(int messageID, AddRequest request) throws UnexpectedRequestException,
            IOException;
    /**
     * Handle a response to an add request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void addResult(int messageID, Result result) throws UnexpectedResponseException,
            IOException;
    /**
     * Handle a bind request.
     *
     * @param messageID
     *            identifier of message
     * @param version
     *            protocol version for this bind request
     * @param request
     *            bind request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void bindRequest(int messageID, int version, GenericBindRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle a response to a bind request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void bindResult(int messageID, BindResult result) throws UnexpectedResponseException,
            IOException;
    /**
     * Handle a compare request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            compare request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void compareRequest(int messageID, CompareRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle a response to a compare request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void compareResult(int messageID, CompareResult result)
            throws UnexpectedResponseException, IOException;
    /**
     * Handle an delete request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            delete request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void deleteRequest(int messageID, DeleteRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle a response to a delete request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void deleteResult(int messageID, Result result) throws UnexpectedResponseException,
            IOException;
    /**
     * Handle an extended request.
     *
     * @param <R>
     *            type of extended result
     * @param messageID
     *            identifier of message
     * @param request
     *            extended request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    <R extends ExtendedResult> void extendedRequest(int messageID, ExtendedRequest<R> request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle a response to an extended request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void extendedResult(int messageID, ExtendedResult result)
            throws UnexpectedResponseException, IOException;
    /**
     * Handle an intermediate response.
     *
     * @param messageID
     *            identifier of message
     * @param response
     *            intermediate response to handle
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void intermediateResponse(int messageID, IntermediateResponse response)
            throws UnexpectedResponseException, IOException;
    /**
     * Handle a modify DN request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            modify DN request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void modifyDNRequest(int messageID, ModifyDNRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle a response to a modify DN request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void modifyDNResult(int messageID, Result result) throws UnexpectedResponseException,
            IOException;
    /**
     * Handle a modify request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            modify request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void modifyRequest(int messageID, ModifyRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle a response to a modify request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void modifyResult(int messageID, Result result) throws UnexpectedResponseException,
            IOException;
    /**
     * Handle a search request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            search request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void searchRequest(int messageID, SearchRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle a response to a search request.
     *
     * @param messageID
     *            identifier of message
     * @param result
     *            response received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void searchResult(int messageID, Result result) throws UnexpectedResponseException,
            IOException;
    /**
     * Handle a response to a search request returning an entry.
     *
     * @param messageID
     *            identifier of message
     * @param entry
     *            entry received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void searchResultEntry(int messageID, SearchResultEntry entry)
            throws UnexpectedResponseException, IOException;
    /**
     * Handle a response to a search request returning an reference.
     *
     * @param messageID
     *            identifier of message
     * @param reference
     *            reference received
     * @throws UnexpectedResponseException
     *             if response is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void searchResultReference(int messageID, SearchResultReference reference)
            throws UnexpectedResponseException, IOException;
    /**
     * Handle an unbind request.
     *
     * @param messageID
     *            identifier of message
     * @param request
     *            unbind request to handle
     * @throws UnexpectedRequestException
     *             if request is not expected by this handler
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void unbindRequest(int messageID, UnbindRequest request)
            throws UnexpectedRequestException, IOException;
    /**
     * Handle an unrecognized message.
     *
     * @param messageID
     *            identifier of message
     * @param messageTag
     *            tag identifying the message type
     * @param messageBytes
     *          content of the message
     * @throws UnsupportedMessageException
     *             if the received message is not supported
     * @throws IOException
     *             if an errors occurs when sending a subsequent message
     */
    void unrecognizedMessage(int messageID, byte messageTag, ByteString messageBytes)
            throws UnsupportedMessageException, IOException;
}
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedRequestException.java
File was renamed from opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/UnexpectedRequestException.java
@@ -22,9 +22,10 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 */
package com.forgerock.opendj.grizzly;
package org.forgerock.opendj.ldap.spi;
import java.io.IOException;
@@ -32,13 +33,21 @@
import org.forgerock.opendj.ldap.requests.Request;
/**
 * Thrown when an expected LDAP request is received.
 * Thrown when an unexpected LDAP request is received.
 */
@SuppressWarnings("serial")
final class UnexpectedRequestException extends IOException {
public final class UnexpectedRequestException extends IOException {
    private final int messageID;
    private final Request request;
    /**
     * Creates the exception with a message id and a request.
     *
     * @param messageID
     *            id of message
     * @param request
     *            request received
     */
    public UnexpectedRequestException(final int messageID, final Request request) {
        super(LocalizableMessage.raw("Unexpected LDAP request: id=%d, message=%s", messageID,
                request).toString());
@@ -46,10 +55,20 @@
        this.request = request;
    }
    /**
     * Returns the identifier of the message.
     *
     * @return the identifier
     */
    public int getMessageID() {
        return messageID;
    }
    /**
     * Returns the request.
     *
     * @return the received request
     */
    public Request getRequest() {
        return request;
    }
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnexpectedResponseException.java
File was renamed from opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/UnexpectedResponseException.java
@@ -22,9 +22,10 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 */
package com.forgerock.opendj.grizzly;
package org.forgerock.opendj.ldap.spi;
import java.io.IOException;
@@ -35,10 +36,18 @@
 * Thrown when an unexpected LDAP response is received.
 */
@SuppressWarnings("serial")
final class UnexpectedResponseException extends IOException {
public final class UnexpectedResponseException extends IOException {
    private final int messageID;
    private final Response response;
    /**
     * Creates the exception with a message id and a response.
     *
     * @param messageID
     *            id of message
     * @param response
     *            response received
     */
    public UnexpectedResponseException(final int messageID, final Response response) {
        super(LocalizableMessage.raw("Unexpected LDAP response: id=%d, message=%s", messageID,
                response).toString());
@@ -46,10 +55,20 @@
        this.response = response;
    }
    /**
     * Returns the identifier of the message.
     *
     * @return the identifier
     */
    public int getMessageID() {
        return messageID;
    }
    /**
     * Returns the response.
     *
     * @return the received response
     */
    public Response getResponse() {
        return response;
    }
opendj3/opendj-core/src/main/java/org/forgerock/opendj/ldap/spi/UnsupportedMessageException.java
File was renamed from opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/UnsupportedMessageException.java
@@ -22,9 +22,10 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2013 ForgeRock AS.
 */
package com.forgerock.opendj.grizzly;
package org.forgerock.opendj.ldap.spi;
import java.io.IOException;
@@ -35,11 +36,21 @@
 * Thrown when an unsupported LDAP message is received.
 */
@SuppressWarnings("serial")
final class UnsupportedMessageException extends IOException {
public final class UnsupportedMessageException extends IOException {
    private final int id;
    private final byte tag;
    private final ByteString content;
    /**
     * Creates the exception with message id, tag and content.
     *
     * @param id
     *            identifier of received message
     * @param tag
     *            identify message type
     * @param content
     *            message content
     */
    public UnsupportedMessageException(final int id, final byte tag, final ByteString content) {
        super(LocalizableMessage.raw("Unsupported LDAP message: id=%d, tag=%d, content=%s", id,
                tag, content).toString());
@@ -48,14 +59,29 @@
        this.content = content;
    }
    /**
     * Returns the content.
     *
     * @return the message content
     */
    public ByteString getContent() {
        return content;
    }
    /**
     * Returns the id.
     *
     * @return the message identifier
     */
    public int getID() {
        return id;
    }
    /**
     * Returns the tag.
     *
     * @return the message tag
     */
    public byte getTag() {
        return tag;
    }
opendj3/opendj-core/src/test/java/org/forgerock/opendj/io/LDAPReaderWriterTestCase.java
New file
@@ -0,0 +1,178 @@
/*
 * 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 static org.fest.assertions.Assertions.*;
import java.io.IOException;
import org.forgerock.opendj.asn1.ASN1Reader;
import org.forgerock.opendj.asn1.ASN1Writer;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SdkTestCase;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
import org.forgerock.opendj.ldap.requests.GenericBindRequest;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
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;
/**
 * Support class for testing {@code LDAPWriter} and {@code LDAPReader}
 * classes in a specific transport provider.
 *
 * A specific transport provider should provide a test case by :
 * <ul>
 *   <li>Extending this class</li>
 *   <li>Implementing the 3 abstract methods {@code getLDAPReader()}, {@code getLDAPReader()}
 *   and {@code transferFromWriterToReader()}</li>
 * </ul>
 */
public abstract class LDAPReaderWriterTestCase extends SdkTestCase {
    // DN used is several tests
    private static final String TEST_DN = "cn=test";
    interface LDAPWrite {
        public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException;
    }
    @DataProvider
    protected Object[][] messagesFactories() {
        return new Object[][] {
                addRequest(),
                addResult(),
                abandonRequest(),
                bindRequest(),
        };
    }
    Object[] addRequest() {
        return new Object[] { new LDAPWrite() {
            @Override
            public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
                writer.writeAddRequest(0, Requests.newAddRequest(TEST_DN));
            }
        }, new AbstractLDAPMessageHandler() {
            @Override
            public void addRequest(int messageID, AddRequest request) throws UnexpectedRequestException, IOException {
                assertThat(request.getName().toString()).isEqualTo(TEST_DN);
            }
        } };
    }
    Object[] addResult() {
        final ResultCode resultCode = ResultCode.SUCCESS;
        return new Object[] { new LDAPWrite() {
            @Override
            public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
                writer.writeAddResult(1, Responses.newResult(resultCode).setMatchedDN(TEST_DN));
            }
        }, new AbstractLDAPMessageHandler() {
            @Override
            public void addResult(int messageID, Result result) throws UnexpectedResponseException, IOException {
                assertThat(result.getResultCode()).isEqualTo(resultCode);
                assertThat(result.getMatchedDN().toString()).isEqualTo(TEST_DN);
            }
        } };
    }
    Object[] abandonRequest() {
        final int requestID = 1;
        return new Object[] { new LDAPWrite() {
            @Override
            public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
                writer.writeAbandonRequest(0, Requests.newAbandonRequest(requestID));
            }
        }, new AbstractLDAPMessageHandler() {
            @Override
            public void abandonRequest(int messageID, AbandonRequest request) throws UnexpectedRequestException,
            IOException {
                assertThat(request.getRequestID()).isEqualTo(requestID);
            }
        } };
    }
    Object[] bindRequest() {
        final byte type = 0x01;
        final byte[] value = new byte[] {0x01, 0x02};
        return new Object[] { new LDAPWrite() {
            @Override
            public void perform(LDAPWriter<? extends ASN1Writer> writer) throws IOException {
                writer.writeBindRequest(0, 1,
                        Requests.newGenericBindRequest(TEST_DN, type, value));
            }
        }, new AbstractLDAPMessageHandler() {
            @Override
            public void bindRequest(final int messageID, final int version, final GenericBindRequest request)
                    throws UnexpectedRequestException, IOException {
                assertThat(request.getAuthenticationType()).isEqualTo(type);
                assertThat(request.getAuthenticationValue()).isEqualTo(value);
                assertThat(request.getName()).isEqualTo(TEST_DN);
            }
        } };
    }
    /**
     * Test that a LDAP message written by LDAPWriter is read correctly using LDAPReader.
     *
     * @param writing write instruction to perform
     * @param messageHandler handler of message read, containing assertion
     *
     * @throws Exception
     */
    @Test(dataProvider = "messagesFactories")
    public void testWriteReadMessage(LDAPWrite writing, LDAPMessageHandler messageHandler)
            throws Exception {
        LDAPWriter<? extends ASN1Writer> writer = getLDAPWriter();
        writing.perform(writer);
        LDAPReader<? extends ASN1Reader> reader = getLDAPReader();
        transferFromWriterToReader(writer, reader);
        reader.readMessage(messageHandler);
    }
    /**
     * Returns a writer specific to the transport module.
     */
    abstract protected LDAPWriter<? extends ASN1Writer> getLDAPWriter();
    /**
     * Returns a reader specific to the transport module.
     */
    abstract protected LDAPReader<? extends ASN1Reader> getLDAPReader();
    /**
     * Transfer raw data from writer to the reader.
     */
    abstract protected void transferFromWriterToReader(LDAPWriter<? extends ASN1Writer> writer,
            LDAPReader<? extends ASN1Reader> reader);
}
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferReader.java
@@ -27,15 +27,12 @@
package com.forgerock.opendj.grizzly;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_ADDITIONAL_LENGTH_BYTES;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_FIRST_LENGTH_BYTE;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_TYPE;
import static com.forgerock.opendj.ldap.LDAPConstants.ELEMENT_READ_STATE_NEED_VALUE_BYTES;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import static com.forgerock.opendj.util.StaticUtils.IO_LOG;
import static com.forgerock.opendj.util.StaticUtils.byteToHex;
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.asn1.ASN1Reader;
import org.forgerock.opendj.asn1.AbstractASN1Reader;
@@ -50,7 +47,7 @@
/**
 * Grizzly ASN1 reader implementation.
 */
final class ASN1BufferReader extends AbstractASN1Reader implements ASN1Reader {
final class ASN1BufferReader extends AbstractASN1Reader {
    private final class ChildSequenceLimiter implements SequenceLimiter {
        private SequenceLimiter parent;
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/ASN1BufferWriter.java
@@ -40,7 +40,6 @@
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Cacheable;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.memory.ByteBufferWrapper;
import com.forgerock.opendj.util.StaticUtils;
@@ -48,7 +47,7 @@
/**
 * Grizzly ASN1 writer implementation.
 */
final class ASN1BufferWriter extends AbstractASN1Writer implements ASN1Writer, Cacheable {
final class ASN1BufferWriter extends AbstractASN1Writer implements Cacheable {
    private class ChildSequenceBuffer implements SequenceBuffer {
        private SequenceBuffer parent;
@@ -162,21 +161,16 @@
    }
    private static final int BUFFER_INIT_SIZE = 1024;
    private final static ThreadCache.CachedTypeIndex<ASN1BufferWriter> WRITER_INDEX = ThreadCache
            .obtainIndex(ASN1BufferWriter.class, 1);
    static ASN1BufferWriter getWriter() {
        ASN1BufferWriter asn1Writer = ThreadCache.takeFromCache(WRITER_INDEX);
        if (asn1Writer == null) {
            asn1Writer = new ASN1BufferWriter();
        }
        if (!asn1Writer.outBuffer.usable) {
    /**
     * Reset the writer.
     */
    void reset() {
        if (!outBuffer.usable) {
            // If the output buffer is unusable, create a new one.
            asn1Writer.outBuffer = new RecyclableBuffer();
            outBuffer = new RecyclableBuffer();
        }
        asn1Writer.outBuffer.clear();
        return asn1Writer;
        outBuffer.clear();
    }
    private SequenceBuffer sequenceBuffer;
@@ -187,7 +181,7 @@
    /**
     * Creates a new ASN.1 writer that writes to a StreamWriter.
     */
    private ASN1BufferWriter() {
    ASN1BufferWriter() {
        this.sequenceBuffer = this.rootBuffer = new RootSequenceBuffer();
        this.outBuffer = new RecyclableBuffer();
    }
@@ -213,10 +207,12 @@
        // Do nothing
    }
    /**
     * Recycle the writer to allow re-use.
     */
    public void recycle() {
        sequenceBuffer = rootBuffer;
        outBuffer.clear();
        ThreadCache.putToCache(WRITER_INDEX, this);
    }
    /**
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnection.java
@@ -44,8 +44,8 @@
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import org.forgerock.opendj.io.LDAPWriter;
import org.forgerock.opendj.ldap.AbstractAsynchronousConnection;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionEventListener;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.FutureResult;
@@ -55,6 +55,7 @@
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.SSLContextBuilder;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.TimeoutEventListener;
import org.forgerock.opendj.ldap.TrustManagers;
import org.forgerock.opendj.ldap.requests.AbandonRequest;
import org.forgerock.opendj.ldap.requests.AddRequest;
@@ -93,7 +94,7 @@
/**
 * LDAP connection implementation.
 */
final class GrizzlyLDAPConnection extends AbstractAsynchronousConnection implements Connection {
final class GrizzlyLDAPConnection extends AbstractAsynchronousConnection implements TimeoutEventListener {
    /**
     * A dummy SSL client engine configurator as SSLFilter only needs client
     * config. This prevents Grizzly from needlessly using JVM defaults which
@@ -113,7 +114,6 @@
    private final AtomicBoolean bindOrStartTLSInProgress = new AtomicBoolean(false);
    private final org.glassfish.grizzly.Connection<?> connection;
    private final LDAPWriter ldapWriter = new LDAPWriter();
    private final AtomicInteger nextMsgID = new AtomicInteger(1);
    private final GrizzlyLDAPConnectionFactory factory;
    private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>> pendingRequests =
@@ -126,6 +126,15 @@
    private boolean isFailed = false;
    private List<ConnectionEventListener> listeners = null;
    /**
     * Create a LDAP Connection with provided Grizzly connection and LDAP
     * connection factory.
     *
     * @param connection
     *            actual connection
     * @param factory
     *            factory that provides LDAP connections
     */
    GrizzlyLDAPConnection(final org.glassfish.grizzly.Connection<?> connection,
            final GrizzlyLDAPConnectionFactory factory) {
        this.connection = connection;
@@ -155,13 +164,13 @@
            }
            pendingRequest.cancel(false);
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.abandonRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeAbandonRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                    return new CompletedFutureResult<Void>((Void) null, messageID);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                throw adaptRequestIOException(e);
@@ -186,12 +195,12 @@
                pendingRequests.put(messageID, future);
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.addRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeAddRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -275,15 +284,15 @@
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    // Use the bind client to get the initial request instead of
                    // using the bind request passed to this method.
                    final GenericBindRequest initialRequest = context.nextBindRequest();
                    ldapWriter.bindRequest(asn1Writer, messageID, 3, initialRequest);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeBindRequest(messageID, 3, initialRequest);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -320,12 +329,12 @@
                pendingRequests.put(messageID, future);
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.compareRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeCompareRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -352,12 +361,12 @@
                pendingRequests.put(messageID, future);
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.deleteRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeDeleteRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -404,12 +413,12 @@
                pendingRequests.put(messageID, future);
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.extendedRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeExtendedRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -451,12 +460,12 @@
                pendingRequests.put(messageID, future);
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.modifyRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeModifyRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -483,12 +492,12 @@
                pendingRequests.put(messageID, future);
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.modifyDNRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeModifyDNRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -525,12 +534,12 @@
                pendingRequests.put(messageID, future);
            }
            try {
                final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
                final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
                try {
                    ldapWriter.searchRequest(asn1Writer, messageID, request);
                    connection.write(asn1Writer.getBuffer(), null);
                    writer.writeSearchRequest(messageID, request);
                    connection.write(writer.getASN1Writer().getBuffer(), null);
                } finally {
                    asn1Writer.recycle();
                    GrizzlyUtils.recycleWriter(writer);
                }
            } catch (final IOException e) {
                pendingRequests.remove(messageID);
@@ -553,20 +562,27 @@
        return builder.toString();
    }
    long cancelExpiredRequests(final long currentTime) {
        final long timeout = factory.getLDAPOptions().getTimeout(TimeUnit.MILLISECONDS);
    @Override
    public long handleTimeout(final long currentTime) {
        final long timeout = getTimeout();
        long delay = timeout;
        if (timeout > 0) {
            for (final int requestID : pendingRequests.keySet()) {
                final AbstractLDAPFutureResultImpl<?> future = pendingRequests.get(requestID);
                if (future != null && future.checkForTimeout()) {
                    final long diff = (future.getTimestamp() + timeout) - currentTime;
                    if (diff <= 0 && pendingRequests.remove(requestID) != null) {
                    if (diff <= 0) {
                        if (pendingRequests.remove(requestID) != null) {
                        DEFAULT_LOG.debug("Cancelling expired future result: {}", future);
                        final Result result = Responses.newResult(ResultCode.CLIENT_SIDE_TIMEOUT);
                        future.adaptErrorResult(result);
                        abandonAsync(Requests.newAbandonRequest(future.getRequestID()));
                    } else {
                            DEFAULT_LOG.debug(
                                    "Pending request {} has already been removed, not cancelling future result",
                                    requestID);
                        }
                    } else {
                        delay = Math.min(delay, diff);
                    }
                }
@@ -575,6 +591,11 @@
        return delay;
    }
    @Override
    public long getTimeout() {
        return factory.getLDAPOptions().getTimeout(TimeUnit.MILLISECONDS);
    }
    /**
     * Closes this connection, invoking event listeners as needed.
     *
@@ -633,19 +654,19 @@
         * connection and release resources.
         */
        if (notifyClose) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
            try {
                ldapWriter.unbindRequest(asn1Writer, nextMsgID.getAndIncrement(), unbindRequest);
                connection.write(asn1Writer.getBuffer(), null);
                writer.writeUnbindRequest(nextMsgID.getAndIncrement(), unbindRequest);
                connection.write(writer.getASN1Writer().getBuffer(), null);
            } catch (final Exception ignore) {
                /*
                 * Underlying channel probably blown up. Ignore all errors,
                 * including possibly runtime exceptions (see OPENDJ-672).
                 */
            } finally {
                asn1Writer.recycle();
                GrizzlyUtils.recycleWriter(writer);
            }
            factory.getTimeoutChecker().removeConnection(this);
            factory.getTimeoutChecker().removeListener(this);
            connection.closeSilently();
            factory.releaseTransportAndTimeoutChecker();
        }
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionFactory.java
@@ -28,8 +28,8 @@
package com.forgerock.opendj.grizzly;
import static com.forgerock.opendj.grizzly.DefaultTCPNIOTransport.DEFAULT_TRANSPORT;
import static com.forgerock.opendj.grizzly.TimeoutChecker.TIMEOUT_CHECKER;
import static org.forgerock.opendj.ldap.ErrorResultException.*;
import static org.forgerock.opendj.ldap.TimeoutChecker.TIMEOUT_CHECKER;
import java.io.IOException;
import java.net.SocketAddress;
@@ -45,6 +45,7 @@
import org.forgerock.opendj.ldap.LDAPOptions;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
import org.forgerock.opendj.ldap.TimeoutChecker;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
import org.forgerock.opendj.ldap.responses.ExtendedResult;
@@ -165,7 +166,7 @@
            connection.configureBlocking(true);
            final GrizzlyLDAPConnection ldapConnection =
                    new GrizzlyLDAPConnection(connection, GrizzlyLDAPConnectionFactory.this);
            timeoutChecker.get().addConnection(ldapConnection);
            timeoutChecker.get().addListener(ldapConnection);
            clientFilter.registerConnection(connection, ldapConnection);
            return ldapConnection;
        }
@@ -254,7 +255,7 @@
        this.transport = DEFAULT_TRANSPORT.acquireIfNull(transport);
        this.socketAddress = address;
        this.options = new LDAPOptions(options);
        this.clientFilter = new LDAPClientFilter(new LDAPReader(this.options.getDecodeOptions()), 0);
        this.clientFilter = new LDAPClientFilter(this.options.getDecodeOptions(), 0);
        this.defaultFilterChain = GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), clientFilter);
    }
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyLDAPListener.java
@@ -102,7 +102,7 @@
        this.connectionFactory = factory;
        final DecodeOptions decodeOptions = new DecodeOptions(options.getDecodeOptions());
        final LDAPServerFilter serverFilter = new LDAPServerFilter(this, new LDAPReader(decodeOptions), options
        final LDAPServerFilter serverFilter = new LDAPServerFilter(this, decodeOptions, options
                .getMaxRequestSize());
        final FilterChain ldapChain = GrizzlyUtils.buildFilterChain(this.transport.get().getProcessor(), serverFilter);
        final TCPNIOBindingHandler bindingHandler =
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/GrizzlyUtils.java
@@ -25,11 +25,16 @@
 */
package com.forgerock.opendj.grizzly;
import org.forgerock.opendj.io.LDAPReader;
import org.forgerock.opendj.io.LDAPWriter;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.glassfish.grizzly.Processor;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.filterchain.Filter;
import org.glassfish.grizzly.filterchain.FilterChain;
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.memory.MemoryManager;
import org.glassfish.grizzly.ssl.SSLFilter;
/**
@@ -38,6 +43,10 @@
final class GrizzlyUtils {
    @SuppressWarnings("rawtypes")
    private static final ThreadCache.CachedTypeIndex<LDAPWriter> WRITER_INDEX = ThreadCache
            .obtainIndex(LDAPWriter.class, 1);
    /**
     * Build a filter chain from the provided processor if possible and the
     * provided filter.
@@ -57,7 +66,7 @@
     *         is a {@code FilterChain}, and having the provided filter as the
     *         last filter
     */
    public static FilterChain buildFilterChain(Processor processor, Filter filter) {
    public static FilterChain buildFilterChain(Processor<?> processor, Filter filter) {
        if (processor instanceof FilterChain) {
            return FilterChainBuilder.stateless().addAll((FilterChain) processor).add(filter).build();
        } else {
@@ -118,6 +127,57 @@
        return FilterChainBuilder.stateless().addAll(chain).add(indexToAddFilter, filter).build();
    }
    /**
     * Creates a new LDAP Reader with the provided maximum size of ASN1 element,
     * options and memory manager.
     *
     * @param decodeOptions
     *            allow to control how responses and requests are decoded
     * @param maxASN1ElementSize
     *            The maximum BER element size, or <code>0</code> to indicate
     *            that there is no limit.
     * @param memoryManager
     *            The memory manager to use for buffering.
     * @return a LDAP reader
     */
    public static LDAPReader<ASN1BufferReader> createReader(DecodeOptions decodeOptions, int maxASN1ElementSize,
            MemoryManager<?> memoryManager) {
        ASN1BufferReader asn1Reader = new ASN1BufferReader(maxASN1ElementSize, memoryManager);
        return new LDAPReader<ASN1BufferReader>(asn1Reader, decodeOptions);
    }
    /**
     * Returns a LDAP writer, with a clean ASN1Writer, possibly from
     * the thread local cache.
     * <p>
     * The writer is either returned from thread local cache or created.
     * In the former case, the writer is removed from the cache.
     *
     * @return a LDAP writer
     */
    @SuppressWarnings("unchecked")
    public static LDAPWriter<ASN1BufferWriter> getWriter() {
        LDAPWriter<ASN1BufferWriter> writer = ThreadCache.takeFromCache(WRITER_INDEX);
        if (writer == null) {
            writer = new LDAPWriter<ASN1BufferWriter>(new ASN1BufferWriter());
        }
        writer.getASN1Writer().reset();
        return writer;
    }
    /**
     * Recycle a LDAP writer to a thread local cache.
     * <p>
     * The LDAP writer is then available for the thread using the
     * {@get()} method.
     *
     * @param writer LDAP writer to recycle
     */
    public static void recycleWriter(LDAPWriter<ASN1BufferWriter> writer) {
        writer.getASN1Writer().recycle();
        ThreadCache.putToCache(WRITER_INDEX, writer);
    }
    // Prevent instantiation.
    private GrizzlyUtils() {
        // No implementation required.
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPClientFilter.java
@@ -34,8 +34,11 @@
import javax.net.ssl.SSLEngine;
import org.forgerock.opendj.io.LDAPReader;
import org.forgerock.opendj.io.LDAPWriter;
import org.forgerock.opendj.ldap.ConnectionSecurityLayer;
import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.requests.AddRequest;
@@ -54,11 +57,13 @@
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.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.EmptyCompletionHandler;
@@ -75,19 +80,46 @@
final class LDAPClientFilter extends BaseFilter {
    private static final Attribute<GrizzlyLDAPConnection> LDAP_CONNECTION_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPClientConnection");
    private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
    private static final Attribute<ClientResponseHandler> RESPONSE_HANDLER_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("ClientResponseHandler");
    private final int maxASN1ElementSize;
    private final LDAPReader ldapReader;
    private final DecodeOptions decodeOptions;
    private static final AbstractLDAPMessageHandler<FilterChainContext> CLIENT_RESPONSE_HANDLER =
            new AbstractLDAPMessageHandler<FilterChainContext>() {
    private static final class ClientResponseHandler extends AbstractLDAPMessageHandler {
        private final LDAPReader<ASN1BufferReader> reader;
        private FilterChainContext context;
        /**
         * Creates a handler with the provided reader.
         *
         * @param reader
         *            LDAP reader to use for reading incoming messages
         */
        public ClientResponseHandler(LDAPReader<ASN1BufferReader> reader) {
            this.reader = reader;
        }
        void setFilterChainContext(FilterChainContext context) {
            this.context = context;
        }
        /**
         * Returns the LDAP reader.
         *
         * @return the reader to read incoming LDAP messages
         */
        public LDAPReader<ASN1BufferReader> getReader() {
            return this.reader;
        }
                @Override
                public void addResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
        public void addResult(final int messageID, final Result result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
@@ -107,10 +139,10 @@
                }
                @Override
                public void bindResult(final FilterChainContext ctx, final int messageID,
                        final BindResult result) throws UnexpectedResponseException, IOException {
        public void bindResult(final int messageID, final BindResult result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
@@ -128,16 +160,14 @@
                                        final int msgID =
                                                ldapConnection.continuePendingBindRequest(future);
                                        final ASN1BufferWriter asn1Writer =
                                                ASN1BufferWriter.getWriter();
                                LDAPWriter<ASN1BufferWriter> ldapWriter = GrizzlyUtils.getWriter();
                                        try {
                                            final GenericBindRequest nextRequest =
                                                    bindClient.nextBindRequest();
                                            new LDAPWriter().bindRequest(asn1Writer, msgID, 3,
                                                    nextRequest);
                                            ctx.write(asn1Writer.getBuffer(), null);
                                    ldapWriter.writeBindRequest(msgID, 3, nextRequest);
                                    context.write(ldapWriter.getASN1Writer().getBuffer(), null);
                                        } finally {
                                            asn1Writer.recycle();
                                    GrizzlyUtils.recycleWriter(ldapWriter);
                                        }
                                        return;
                                    }
@@ -168,7 +198,7 @@
                                        // the SASL mechanism.
                                        ldapConnection
                                                .installFilter(new ConnectionSecurityLayerFilter(l,
                                                        ctx.getConnection().getTransport()
                                                context.getConnection().getTransport()
                                                                .getMemoryManager()));
                                    }
                                }
@@ -183,10 +213,10 @@
                }
                @Override
                public void compareResult(final FilterChainContext ctx, final int messageID,
                        final CompareResult result) throws UnexpectedResponseException, IOException {
        public void compareResult(final int messageID, final CompareResult result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
@@ -204,10 +234,10 @@
                }
                @Override
                public void deleteResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
        public void deleteResult(final int messageID, final Result result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
@@ -227,11 +257,10 @@
                }
                @Override
                public void extendedResult(final FilterChainContext ctx, final int messageID,
                        final ExtendedResult result) throws UnexpectedResponseException,
                        IOException {
        public void extendedResult(final int messageID, final ExtendedResult result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        if (messageID == 0) {
                            // Unsolicited notification received.
@@ -277,11 +306,10 @@
                }
                @Override
                public void intermediateResponse(final FilterChainContext ctx, final int messageID,
                        final IntermediateResponse response) throws UnexpectedResponseException,
                        IOException {
        public void intermediateResponse(final int messageID, final IntermediateResponse response)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.getPendingRequest(messageID);
@@ -293,10 +321,10 @@
                }
                @Override
                public void modifyDNResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
        public void modifyDNResult(final int messageID, final Result result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
@@ -316,10 +344,10 @@
                }
                @Override
                public void modifyResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
        public void modifyResult(final int messageID, final Result result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
@@ -339,10 +367,10 @@
                }
                @Override
                public void searchResult(final FilterChainContext ctx, final int messageID,
                        final Result result) throws UnexpectedResponseException, IOException {
        public void searchResult(final int messageID, final Result result)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.removePendingRequest(messageID);
@@ -359,11 +387,10 @@
                }
                @Override
                public void searchResultEntry(final FilterChainContext ctx, final int messageID,
                        final SearchResultEntry entry) throws UnexpectedResponseException,
                        IOException {
        public void searchResultEntry(final int messageID, final SearchResultEntry entry)
                throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.getPendingRequest(messageID);
@@ -379,11 +406,10 @@
                }
                @Override
                public void searchResultReference(final FilterChainContext ctx,
                        final int messageID, final SearchResultReference reference)
        public void searchResultReference(final int messageID, final SearchResultReference reference)
                        throws UnexpectedResponseException, IOException {
                    final GrizzlyLDAPConnection ldapConnection =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(context.getConnection());
                    if (ldapConnection != null) {
                        final AbstractLDAPFutureResultImpl<?> pendingRequest =
                                ldapConnection.getPendingRequest(messageID);
@@ -447,10 +473,20 @@
                    future.setResultOrError(decodedResponse);
                }
            };
    }
    LDAPClientFilter(final LDAPReader ldapReader, final int maxASN1ElementSize) {
        this.ldapReader = ldapReader;
    /**
     * Creates a client filter with provided options and max size of ASN1
     * elements.
     *
     * @param options
     *            allow to control how request and responses are decoded
     * @param maxASN1ElementSize
     *            The maximum BER element size, or <code>0</code> to indicate
     *            that there is no limit.
     */
    LDAPClientFilter(final DecodeOptions options, final int maxASN1ElementSize) {
        this.decodeOptions = options;
        this.maxASN1ElementSize = maxASN1ElementSize;
    }
@@ -490,27 +526,23 @@
    @Override
    public NextAction handleRead(final FilterChainContext ctx) throws IOException {
        final ClientResponseHandler handler = getResponseHandler(ctx);
        final LDAPReader<ASN1BufferReader> reader = handler.getReader();
        final ASN1BufferReader asn1Reader = reader.getASN1Reader();
        final Buffer buffer = (Buffer) ctx.getMessage();
        ASN1BufferReader asn1Reader = LDAP_ASN1_READER_ATTR.get(ctx.getConnection());
        if (asn1Reader == null) {
            asn1Reader =
                    new ASN1BufferReader(maxASN1ElementSize, ctx.getConnection().getTransport()
                            .getMemoryManager());
            LDAP_ASN1_READER_ATTR.set(ctx.getConnection(), asn1Reader);
        }
        asn1Reader.appendBytesRead(buffer);
        asn1Reader.appendBytesRead(buffer);
        try {
            while (asn1Reader.elementAvailable()) {
                ldapReader.decode(asn1Reader, CLIENT_RESPONSE_HANDLER, ctx);
            while (reader.hasMessageAvailable()) {
                reader.readMessage(handler);
            }
        } catch (IOException ioe) {
        } catch (IOException e) {
            final GrizzlyLDAPConnection ldapConnection = LDAP_CONNECTION_ATTR.get(ctx.getConnection());
            final Result errorResult =
                    Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR).setCause(ioe)
                            .setDiagnosticMessage(ioe.getMessage());
                    Responses.newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR).setCause(e)
                            .setDiagnosticMessage(e.getMessage());
            ldapConnection.close(null, false, errorResult);
            throw ioe;
            throw e;
        } finally {
            asn1Reader.disposeBytesRead();
        }
@@ -518,6 +550,39 @@
        return ctx.getStopAction();
    }
    /**
     * Returns the response handler associated to the provided connection and
     * context.
     * <p>
     * If no handler exists yet for this context, a new one is created and
     * recorded for the connection.
     *
     * @param ctx
     *            current filter chain context
     * @return the response handler associated to the context, which can be a
     *         new one if no handler have been created yet
     */
    private ClientResponseHandler getResponseHandler(final FilterChainContext ctx) {
        Connection<?> connection = ctx.getConnection();
        ClientResponseHandler handler = RESPONSE_HANDLER_ATTR.get(connection);
        if (handler == null) {
            LDAPReader<ASN1BufferReader> reader = GrizzlyUtils.createReader(decodeOptions,
                    maxASN1ElementSize, connection.getTransport().getMemoryManager());
            handler = new ClientResponseHandler(reader);
            RESPONSE_HANDLER_ATTR.set(connection, handler);
        }
        handler.setFilterChainContext(ctx);
        return handler;
    }
    /**
     * Associate a LDAP connection to the provided Grizzly connection.
     *
     * @param connection
     *            Grizzly connection
     * @param ldapConnection
     *            LDAP connection
     */
    void registerConnection(final Connection<?> connection, final GrizzlyLDAPConnection ldapConnection) {
        LDAP_CONNECTION_ATTR.set(connection, ldapConnection);
    }
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPMessageHandler.java
File was deleted
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPServerFilter.java
@@ -38,8 +38,11 @@
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
import org.forgerock.opendj.io.LDAPReader;
import org.forgerock.opendj.io.LDAPWriter;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ConnectionSecurityLayer;
import org.forgerock.opendj.ldap.DecodeOptions;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.IntermediateResponseHandler;
import org.forgerock.opendj.ldap.LDAPClientContext;
@@ -69,6 +72,9 @@
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.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Grizzly;
@@ -89,33 +95,113 @@
 * side logic for SSL and SASL operations over LDAP.
 */
final class LDAPServerFilter extends BaseFilter {
    private abstract class AbstractHandler<R extends Result> implements
    /**
     * Provides an arbitrary write operation on a LDAP writer.
     */
    private interface LDAPWrite<T> {
        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 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;
        }
        @Override
        public void handleResult(final R result) {
            defaultHandleResult(result);
        }
        @Override
        public final boolean handleIntermediateResponse(final IntermediateResponse response) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            writeMessage(INTERMEDIATE, response);
            return true;
        }
        /**
         * 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 {
                    writeResult(writer, res);
                }
            }, result);
        }
        /**
         * Write a result to provided LDAP writer.
         *
         * @param ldapWriter
         *            provided writer
         * @param result
         *            to write
         * @throws IOException
         *             if an error occurs during writing
         */
        abstract protected void writeResult(final LDAPWriter<ASN1BufferWriter> ldapWriter, final R result)
                throws IOException;
        /**
         * Write a message on LDAP writer.
         *
         * @param <T>
         *            type of message to write
         * @param ldapWrite
         *            the specific write operation
         * @param message
         *            the message to write
         */
        protected final <T> void writeMessage(final LDAPWrite<T> ldapWrite, final T message) {
            final LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
            try {
                LDAP_WRITER.intermediateResponse(asn1Writer, messageID, response);
                context.write(asn1Writer);
                ldapWrite.perform(writer, messageID, message);
                context.write(writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
                return false;
            } finally {
                asn1Writer.recycle();
            }
            return true;
                GrizzlyUtils.recycleWriter(writer);
        }
    }
    private final class AddHandler extends AbstractHandler<Result> {
        /**
         * Copy diagnostic message, matched DN and cause to new result from the
         * given result.
         *
         * @param newResult
         *            to update
         * @param result
         *            contains parameters to copy
         */
        protected final void populateNewResultFromResult(final R newResult, final Result result) {
            newResult.setDiagnosticMessage(result.getDiagnosticMessage());
            newResult.setMatchedDN(result.getMatchedDN());
            newResult.setCause(result.getCause());
            for (final Control control : result.getControls()) {
                newResult.addControl(control);
            }
        }
    }
    private static final class AddHandler extends AbstractHandler<Result> {
        private AddHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
        }
@@ -126,20 +212,13 @@
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.addResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            }
        public void writeResult(LDAPWriter<ASN1BufferWriter> writer, final Result result)
                throws IOException {
            writer.writeAddResult(messageID, result);
        }
    }
    private final class BindHandler extends AbstractHandler<BindResult> {
    private static final class BindHandler extends AbstractHandler<BindResult> {
        private BindHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
        }
@@ -151,31 +230,19 @@
                handleResult((BindResult) result);
            } else {
                final BindResult newResult = Responses.newBindResult(result.getResultCode());
                newResult.setDiagnosticMessage(result.getDiagnosticMessage());
                newResult.setMatchedDN(result.getMatchedDN());
                newResult.setCause(result.getCause());
                for (final Control control : result.getControls()) {
                    newResult.addControl(control);
                }
                populateNewResultFromResult(newResult, result);
                handleResult(newResult);
            }
        }
        @Override
        public void handleResult(final BindResult result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.bindResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            }
        protected void writeResult(LDAPWriter<ASN1BufferWriter> writer, BindResult result)
                throws IOException {
            writer.writeBindResult(messageID, result);
        }
    }
    private final class ClientContextImpl implements LDAPClientContext {
    private static final class ClientContextImpl implements LDAPClientContext {
        private final Connection<?> connection;
        private final AtomicBoolean isClosed = new AtomicBoolean();
        private ServerConnection<Integer> serverConnection = null;
@@ -263,14 +330,14 @@
        @Override
        public void sendUnsolicitedNotification(final ExtendedResult notification) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            LDAPWriter<ASN1BufferWriter> writer = GrizzlyUtils.getWriter();
            try {
                LDAP_WRITER.extendedResult(asn1Writer, 0, notification);
                connection.write(asn1Writer.getBuffer(), null);
                writer.writeExtendedResult(0, notification);
                connection.write(writer.getASN1Writer().getBuffer(), null);
            } catch (final IOException ioe) {
                handleError(ioe);
            } finally {
                asn1Writer.recycle();
                GrizzlyUtils.recycleWriter(writer);
            }
        }
@@ -288,8 +355,8 @@
            return builder.toString();
        }
        public void write(final ASN1BufferWriter asn1Writer) {
            connection.write(asn1Writer.getBuffer(), null);
        public void write(final LDAPWriter<ASN1BufferWriter> writer) {
            connection.write(writer.getASN1Writer().getBuffer(), null);
        }
        private void disconnect0(final ResultCode resultCode, final String message) {
@@ -385,7 +452,7 @@
        }
    }
    private final class CompareHandler extends AbstractHandler<CompareResult> {
    private static final class CompareHandler extends AbstractHandler<CompareResult> {
        private CompareHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
        }
@@ -397,31 +464,20 @@
                handleResult((CompareResult) result);
            } else {
                final CompareResult newResult = Responses.newCompareResult(result.getResultCode());
                newResult.setDiagnosticMessage(result.getDiagnosticMessage());
                newResult.setMatchedDN(result.getMatchedDN());
                newResult.setCause(result.getCause());
                for (final Control control : result.getControls()) {
                    newResult.addControl(control);
                }
                populateNewResultFromResult(newResult, result);
                handleResult(newResult);
            }
        }
        @Override
        public void handleResult(final CompareResult result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.compareResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            }
        protected void writeResult(LDAPWriter<ASN1BufferWriter> writer, CompareResult result)
                throws IOException {
            writer.writeCompareResult(messageID, result);
        }
    }
    private final class DeleteHandler extends AbstractHandler<Result> {
    private static final class DeleteHandler extends AbstractHandler<Result> {
        private DeleteHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
        }
@@ -432,20 +488,13 @@
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.deleteResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            }
        protected void writeResult(LDAPWriter<ASN1BufferWriter> writer, Result result)
                throws IOException {
            writer.writeDeleteResult(messageID, result);
        }
    }
    private final class ExtendedHandler<R extends ExtendedResult> extends AbstractHandler<R> {
    private static final class ExtendedHandler<R extends ExtendedResult> extends AbstractHandler<R> {
        private ExtendedHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
        }
@@ -470,19 +519,21 @@
        @Override
        public void handleResult(final ExtendedResult result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.extendedResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            writeMessage(new LDAPWrite<ExtendedResult>() {
                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 {
            // never called because handleResult(result) method is overriden in this class
        }
    }
    private final class ModifyDNHandler extends AbstractHandler<Result> {
    private static final class ModifyDNHandler extends AbstractHandler<Result> {
        private ModifyDNHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
        }
@@ -493,20 +544,13 @@
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.modifyDNResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            }
        protected void writeResult(LDAPWriter<ASN1BufferWriter> writer, Result result)
                throws IOException {
            writer.writeModifyDNResult(messageID, result);
        }
    }
    private final class ModifyHandler extends AbstractHandler<Result> {
    private static final class ModifyHandler extends AbstractHandler<Result> {
        private ModifyHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
        }
@@ -517,20 +561,13 @@
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.modifyResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            }
        protected void writeResult(LDAPWriter<ASN1BufferWriter> writer, Result result)
                throws IOException {
            writer.writeModifyResult(messageID, result);
        }
    }
    private final class SearchHandler extends AbstractHandler<Result> implements
    private static final class SearchHandler extends AbstractHandler<Result> implements
            SearchResultHandler {
        private SearchHandler(final ClientContextImpl context, final int messageID) {
            super(context, messageID);
@@ -538,16 +575,12 @@
        @Override
        public boolean handleEntry(final SearchResultEntry entry) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.searchResultEntry(asn1Writer, messageID, entry);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
                return false;
            } finally {
                asn1Writer.recycle();
            writeMessage(new LDAPWrite<SearchResultEntry>() {
                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, SearchResultEntry sre)
                        throws IOException {
                    writer.writeSearchResultEntry(messageID, sre);
            }
            }, entry);
            return true;
        }
@@ -558,30 +591,19 @@
        @Override
        public boolean handleReference(final SearchResultReference reference) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.searchResultReference(asn1Writer, messageID, reference);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
                return false;
            } finally {
                asn1Writer.recycle();
            writeMessage(new LDAPWrite<SearchResultReference>() {
                public void perform(LDAPWriter<ASN1BufferWriter> writer, int messageID, SearchResultReference ref)
                        throws IOException {
                    writer.writeSearchResultReference(messageID, ref);
            }
            }, reference);
            return true;
        }
        @Override
        public void handleResult(final Result result) {
            final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
            try {
                LDAP_WRITER.searchResult(asn1Writer, messageID, result);
                context.write(asn1Writer);
            } catch (final IOException ioe) {
                context.handleError(ioe);
            } finally {
                asn1Writer.recycle();
            }
        protected void writeResult(LDAPWriter<ASN1BufferWriter> writer, Result result)
                throws IOException {
            writer.writeSearchResult(messageID, result);
        }
    }
@@ -613,13 +635,11 @@
    // Default maximum request size for incoming requests.
    private static final int DEFAULT_MAX_REQUEST_SIZE = 5 * 1024 * 1024;
    private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
    private static final Attribute<ClientContextImpl> LDAP_CONNECTION_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPServerConnection");
    private static final LDAPWriter LDAP_WRITER = new LDAPWriter();
    private static final Attribute<ServerRequestHandler> REQUEST_HANDLER_ATTR =
            Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("ServerRequestHandler");
    /**
     * A dummy SSL client engine configurator as SSLFilter only needs server
@@ -638,17 +658,42 @@
        }
    }
    private final LDAPReader ldapReader;
    private final GrizzlyLDAPListener listener;
    private final int maxASN1ElementSize;
    private final DecodeOptions decodeOptions;
    private final AbstractLDAPMessageHandler<FilterChainContext> serverRequestHandler =
            new AbstractLDAPMessageHandler<FilterChainContext>() {
    private static final class ServerRequestHandler extends AbstractLDAPMessageHandler {
        private final Connection<?> connection;
        private final LDAPReader<ASN1BufferReader> reader;
        /**
         * Creates the handler with a connection.
         *
         * @param connection
         *            connection this handler is associated with
         * @param reader
         *            LDAP reader to use for reading incoming messages
         */
        ServerRequestHandler(Connection<?> connection, LDAPReader<ASN1BufferReader> reader) {
            this.connection = connection;
            this.reader = reader;
        }
        /**
         * Returns the LDAP reader.
         *
         * @return the reader to read incoming LDAP messages
         */
        public LDAPReader<ASN1BufferReader> getReader() {
            return reader;
        }
                @Override
                public void abandonRequest(final FilterChainContext ctx, final int messageID,
                        final AbandonRequest request) throws UnexpectedRequestException {
        public void abandonRequest(final int messageID, final AbandonRequest request)
                throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        conn.handleAbandon(messageID, request);
@@ -656,10 +701,9 @@
                }
                @Override
                public void addRequest(final FilterChainContext ctx, final int messageID,
                        final AddRequest request) throws UnexpectedRequestException {
        public void addRequest(final int messageID, final AddRequest request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final AddHandler handler = new AddHandler(clientContext, messageID);
@@ -668,23 +712,23 @@
                }
                @Override
                public void bindRequest(final FilterChainContext ctx, final int messageID,
                        final int version, final GenericBindRequest request)
        public void bindRequest(final int messageID, final int version,
                final GenericBindRequest request)
                        throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final BindHandler 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 FilterChainContext ctx, final int messageID,
                        final CompareRequest request) throws UnexpectedRequestException {
        public void compareRequest(final int messageID, final CompareRequest request)
                throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final CompareHandler handler = new CompareHandler(clientContext, messageID);
@@ -693,10 +737,10 @@
                }
                @Override
                public void deleteRequest(final FilterChainContext ctx, final int messageID,
                        final DeleteRequest request) throws UnexpectedRequestException {
        public void deleteRequest(final int messageID, final DeleteRequest request)
                throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final DeleteHandler handler = new DeleteHandler(clientContext, messageID);
@@ -706,36 +750,34 @@
                @Override
                public <R extends ExtendedResult> void extendedRequest(
                        final FilterChainContext ctx, final int messageID,
                final int messageID,
                        final ExtendedRequest<R> request) throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final ExtendedHandler<R> handler =
                                new ExtendedHandler<R>(clientContext, messageID);
                final ExtendedHandler<R> handler = new ExtendedHandler<R>(clientContext, messageID);
                        conn.handleExtendedRequest(messageID, request, handler, handler);
                    }
                }
                @Override
                public void modifyDNRequest(final FilterChainContext ctx, final int messageID,
                        final ModifyDNRequest request) throws UnexpectedRequestException {
        public void modifyDNRequest(final int messageID, final ModifyDNRequest request)
                throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final ModifyDNHandler handler =
                                new ModifyDNHandler(clientContext, messageID);
                final ModifyDNHandler handler = new ModifyDNHandler(clientContext, messageID);
                        conn.handleModifyDN(messageID, request, handler, handler);
                    }
                }
                @Override
                public void modifyRequest(final FilterChainContext ctx, final int messageID,
                        final ModifyRequest request) throws UnexpectedRequestException {
        public void modifyRequest(final int messageID, final ModifyRequest request)
                throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final ModifyHandler handler = new ModifyHandler(clientContext, messageID);
@@ -744,10 +786,10 @@
                }
                @Override
                public void searchRequest(final FilterChainContext ctx, final int messageID,
                        final SearchRequest request) throws UnexpectedRequestException {
        public void searchRequest(final int messageID, final SearchRequest request)
                throws UnexpectedRequestException {
                    final ClientContextImpl clientContext =
                            LDAP_CONNECTION_ATTR.get(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.get(connection);
                    if (clientContext != null) {
                        final ServerConnection<Integer> conn = clientContext.getServerConnection();
                        final SearchHandler handler = new SearchHandler(clientContext, messageID);
@@ -756,36 +798,51 @@
                }
                @Override
                public void unbindRequest(final FilterChainContext ctx, final int messageID,
                        final UnbindRequest request) {
        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(ctx.getConnection());
                    LDAP_CONNECTION_ATTR.remove(connection);
                    if (clientContext != null) {
                        clientContext.handleClose(messageID, request);
                    }
                }
                @Override
                public void unrecognizedMessage(final FilterChainContext ctx, final int messageID,
                        final byte messageTag, final ByteString messageBytes) {
                    exceptionOccurred(ctx, new UnsupportedMessageException(messageID, messageTag,
        public void unrecognizedMessage(final int messageID, final byte messageTag,
                final ByteString messageBytes) {
            exceptionOccurred(connection, new UnsupportedMessageException(messageID, messageTag,
                            messageBytes));
                }
            };
    }
    LDAPServerFilter(final GrizzlyLDAPListener listener, final LDAPReader ldapReader,
    /**
     * Creates a server filter with provided listener, options and max size of
     * ASN1 element.
     *
     * @param listener
     *            listen for incoming connections
     * @param options
     *            control how to decode requests and responses
     * @param maxASN1ElementSize
     *            The maximum BER element size, or <code>0</code> to indicate
     *            that there is no limit.
     */
    LDAPServerFilter(final GrizzlyLDAPListener listener, final DecodeOptions options,
            final int maxASN1ElementSize) {
        this.listener = listener;
        this.ldapReader = ldapReader;
        this.decodeOptions = options;
        this.maxASN1ElementSize =
                maxASN1ElementSize <= 0 ? DEFAULT_MAX_REQUEST_SIZE : maxASN1ElementSize;
    }
    @Override
    public void exceptionOccurred(final FilterChainContext ctx, final Throwable error) {
        final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.remove(ctx.getConnection());
        exceptionOccurred(ctx.getConnection(), error);
    }
    private static void exceptionOccurred(final Connection<?> connection, final Throwable error) {
        final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR.remove(connection);
        if (clientContext != null) {
            clientContext.handleError(error);
        }
@@ -819,19 +876,15 @@
    @Override
    public NextAction handleRead(final FilterChainContext ctx) throws IOException {
        final ServerRequestHandler requestHandler = getRequestHandler(ctx.getConnection());
        final LDAPReader<ASN1BufferReader> reader = requestHandler.getReader();
        final ASN1BufferReader asn1Reader = reader.getASN1Reader();
        final Buffer buffer = (Buffer) ctx.getMessage();
        ASN1BufferReader asn1Reader = LDAP_ASN1_READER_ATTR.get(ctx.getConnection());
        if (asn1Reader == null) {
            asn1Reader =
                    new ASN1BufferReader(maxASN1ElementSize, ctx.getConnection().getTransport()
                            .getMemoryManager());
            LDAP_ASN1_READER_ATTR.set(ctx.getConnection(), asn1Reader);
        }
        asn1Reader.appendBytesRead(buffer);
        asn1Reader.appendBytesRead(buffer);
        try {
            while (asn1Reader.elementAvailable()) {
                ldapReader.decode(asn1Reader, serverRequestHandler, ctx);
            while (reader.hasMessageAvailable()) {
                reader.readMessage(requestHandler);
            }
        } catch (IOException e) {
            exceptionOccurred(ctx, e);
@@ -842,4 +895,26 @@
        return ctx.getStopAction();
    }
    /**
     * Returns the request handler associated to a connection.
     * <p>
     * If no handler exists yet for this context, a new one is created and
     * recorded for the context.
     *
     * @param ctx
     *            Context
     * @return the response handler associated to the context, which can be a
     *         new one if no handler have been created yet
     */
    private ServerRequestHandler getRequestHandler(final Connection<?> connection) {
        ServerRequestHandler handler = REQUEST_HANDLER_ATTR.get(connection);
        if (handler == null) {
            LDAPReader<ASN1BufferReader> reader = GrizzlyUtils.createReader(decodeOptions,
                    maxASN1ElementSize, connection.getTransport().getMemoryManager());
            handler = new ServerRequestHandler(connection, reader);
            REQUEST_HANDLER_ATTR.set(connection, handler);
        }
        return handler;
    }
}
opendj3/opendj-grizzly/src/main/java/com/forgerock/opendj/grizzly/LDAPWriter.java
File was deleted
opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/ASN1BufferWriterTestCase.java
@@ -22,7 +22,7 @@
 *
 *
 *      Copyright 2010 Sun Microsystems, Inc.
 *      Portions copyright 2011 ForgeRock AS
 *      Portions copyright 2011-2013 ForgeRock AS
 */
package com.forgerock.opendj.grizzly;
@@ -43,7 +43,7 @@
 */
public class ASN1BufferWriterTestCase extends ASN1WriterTestCase {
    private final ASN1BufferWriter writer = ASN1BufferWriter.getWriter();
    private final ASN1BufferWriter writer = new ASN1BufferWriter();
    @Override
    protected byte[] getEncodedBytes() throws IOException, DecodeException {
opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPConnectionTestCase.java
@@ -102,7 +102,7 @@
            connection.searchAsync(request, null, handler);
            // Pass in a time which is guaranteed to trigger expiration.
            connection.cancelExpiredRequests(System.currentTimeMillis() + 1000000);
            connection.handleTimeout(System.currentTimeMillis() + 1000000);
            if (isPersistentSearch) {
                verifyZeroInteractions(handler);
            } else {
opendj3/opendj-grizzly/src/test/java/com/forgerock/opendj/grizzly/GrizzlyLDAPReaderWriterTestCase.java
New file
@@ -0,0 +1,61 @@
/*
 * 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 com.forgerock.opendj.grizzly;
import org.forgerock.opendj.asn1.ASN1Reader;
import org.forgerock.opendj.asn1.ASN1Writer;
import org.forgerock.opendj.io.LDAPReader;
import org.forgerock.opendj.io.LDAPReaderWriterTestCase;
import org.forgerock.opendj.io.LDAPWriter;
import org.forgerock.opendj.ldap.LDAPOptions;
import org.glassfish.grizzly.memory.HeapMemoryManager;
/**
 * Tests for LDAPWriter / LDAPReader classes using specific implementations of
 * ASN1 writer and ASN1 reader with Grizzly.
 */
public class GrizzlyLDAPReaderWriterTestCase extends LDAPReaderWriterTestCase {
    @Override
    protected LDAPWriter<? extends ASN1Writer> getLDAPWriter() {
        return GrizzlyUtils.getWriter();
    }
    @Override
    protected LDAPReader<? extends ASN1Reader> getLDAPReader() {
        return GrizzlyUtils.createReader(new LDAPOptions().getDecodeOptions(),
                0, new HeapMemoryManager());
    }
    @Override
    protected void transferFromWriterToReader(LDAPWriter<? extends ASN1Writer> writer,
            LDAPReader<? extends ASN1Reader> reader) {
        ASN1BufferReader asn1Reader = (ASN1BufferReader) reader.getASN1Reader();
        ASN1BufferWriter asn1Writer = (ASN1BufferWriter) writer.getASN1Writer();
        asn1Reader.appendBytesRead(asn1Writer.getBuffer());
    }
}