From b218bc6741a7bbddc77cb1bdc210581bb96ad74b Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Fri, 29 Sep 2006 21:27:18 +0000
Subject: [PATCH] Make several test case updates:

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/OperationTestCase.java        |   15 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java    | 1855 ++++++++++++++++++++++++++++++++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java    |  651 +++++++++++++
 opends/tests/unit-tests-testng/resource/config-changes.ldif                                    |   26 
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java |  322 ++++++
 5 files changed, 2,840 insertions(+), 29 deletions(-)

diff --git a/opends/tests/unit-tests-testng/resource/config-changes.ldif b/opends/tests/unit-tests-testng/resource/config-changes.ldif
index 2df2c49..2c7307c 100644
--- a/opends/tests/unit-tests-testng/resource/config-changes.ldif
+++ b/opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -221,6 +221,32 @@
 ds-cfg-plugin-type: startup
 ds-cfg-plugin-type: shutdown
 
+dn: cn=Short Circuit Plugin,cn=Plugins,cn=config
+changetype: add
+objectClass: top
+objectClass: ds-cfg-plugin
+cn: Short Circuit Plugin
+ds-cfg-plugin-class: org.opends.server.plugins.ShortCircuitPlugin
+ds-cfg-plugin-enabled: true
+ds-cfg-plugin-type: preParseAbandon
+ds-cfg-plugin-type: preParseAdd
+ds-cfg-plugin-type: preParseBind
+ds-cfg-plugin-type: preParseCompare
+ds-cfg-plugin-type: preParseDelete
+ds-cfg-plugin-type: preParseExtended
+ds-cfg-plugin-type: preParseModify
+ds-cfg-plugin-type: preParseModifyDN
+ds-cfg-plugin-type: preParseSearch
+ds-cfg-plugin-type: preParseUnbind
+ds-cfg-plugin-type: preOperationAdd
+ds-cfg-plugin-type: preOperationBind
+ds-cfg-plugin-type: preOperationCompare
+ds-cfg-plugin-type: preOperationDelete
+ds-cfg-plugin-type: preOperationExtended
+ds-cfg-plugin-type: preOperationModify
+ds-cfg-plugin-type: preOperationModifyDN
+ds-cfg-plugin-type: preOperationSearch
+
 dn: cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config
 changetype: add
 objectClass: top
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java
index 9ba186d..a81c501 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java
@@ -30,9 +30,9 @@
 
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.LinkedHashSet;
 
 import org.testng.annotations.BeforeClass;
-import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import org.opends.server.TestCaseUtils;
@@ -56,21 +56,28 @@
 import org.opends.server.protocols.ldap.DeleteResponseProtocolOp;
 import org.opends.server.protocols.ldap.ExtendedRequestProtocolOp;
 import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
+import org.opends.server.protocols.ldap.LDAPAttribute;
+import org.opends.server.protocols.ldap.LDAPFilter;
+import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.LDAPModification;
+import org.opends.server.protocols.ldap.LDAPResultCode;
 import org.opends.server.protocols.ldap.ModifyRequestProtocolOp;
 import org.opends.server.protocols.ldap.ModifyResponseProtocolOp;
 import org.opends.server.protocols.ldap.ModifyDNRequestProtocolOp;
 import org.opends.server.protocols.ldap.ModifyDNResponseProtocolOp;
 import org.opends.server.protocols.ldap.SearchRequestProtocolOp;
 import org.opends.server.protocols.ldap.SearchResultDoneProtocolOp;
-import org.opends.server.protocols.ldap.LDAPAttribute;
-import org.opends.server.protocols.ldap.LDAPMessage;
-import org.opends.server.protocols.ldap.LDAPResultCode;
 import org.opends.server.types.Control;
+import org.opends.server.types.DereferencePolicy;
 import org.opends.server.types.Entry;
+import org.opends.server.types.ModificationType;
 import org.opends.server.types.ResultCode;
+import org.opends.server.types.SearchScope;
 
 import static org.testng.Assert.*;
 
+import static org.opends.server.util.ServerConstants.*;
+
 
 
 /**
@@ -80,31 +87,9 @@
        extends OperationTestCase
 {
   /**
-   * Ensures that the Directory Server is running.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
+   * {@inheritDoc}
    */
-  @BeforeClass()
-  public void startServer()
-         throws Exception
-  {
-    TestCaseUtils.startServer();
-  }
-
-
-
-  /**
-   * Creates a set of valid operation instances of this type that may be used
-   * for testing the general methods defined in the Operation superclass.  Only
-   * the constructors for the operation need to be used -- it does not require
-   * any further initialization (no tests will be performed that require any
-   * further processing).
-   *
-   * @return  A set of operation instances of this type that may be used for
-   *          testing the general methods defined in the Operation superclass.
-   *
-   * @throws  Exception  If an unexpected problem occurs.
-   */
+  @Override()
   public Operation[] createTestOperations()
          throws Exception
   {
@@ -436,7 +421,7 @@
    *
    * @throws  Exception  If an unexpected problem occurs.
    */
-  @Test(groups = { "slow" })
+  @Test()
   public void testAbandonDelete()
          throws Exception
   {
@@ -505,5 +490,284 @@
     assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
     assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
   }
+
+
+
+  /**
+   * Tests the ability to abandon an extended operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAbandonExtended()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+    InvocationCounterPlugin.resetAllCounters();
+
+
+    // Establish a connection to the server and bind as a root user.
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(6000);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest);
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+    // Create a "Who Am I?" extended oepration and send it to the server.  Make
+    // sure to include the delay request control so it won't complete before we
+    // can send the abandon request.
+    ExtendedRequestProtocolOp whoAmIRequest =
+         new ExtendedRequestProtocolOp(OID_WHO_AM_I_REQUEST, null);
+    message = new LDAPMessage(2, whoAmIRequest,
+                       DelayPreOpPlugin.createDelayLDAPControlList(5000));
+    w.writeElement(message.encode());
+
+
+    // Send the abandon request to the server and wait a few seconds to ensure
+    // it has completed before closing the connection.
+    AbandonRequestProtocolOp abandonRequest = new AbandonRequestProtocolOp(2);
+    w.writeElement(new LDAPMessage(3, abandonRequest).encode());
+
+
+    // Normally, abandoned operations don't receive a response.  However, the
+    // testing configuration has been updated to ensure that if an operation
+    // does get abandoned, the server will return a response for it with a
+    // result code of "cancelled".
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    ExtendedResponseProtocolOp extendedResponse =
+         message.getExtendedResponseProtocolOp();
+    assertEquals(extendedResponse.getResultCode(), LDAPResultCode.CANCELED);
+
+    s.close();
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+  }
+
+
+
+  /**
+   * Tests the ability to abandon a modify operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAbandonModify()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+    InvocationCounterPlugin.resetAllCounters();
+
+
+    // Establish a connection to the server and bind as a root user.
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(6000);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest);
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+    // Create a modify request and send it to the server.  Make sure to include
+    // the delay request control so it won't complete before we can send the
+    // abandon request.
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>(1);
+    values.add(new ASN1OctetString("foo"));
+
+    ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>(1);
+    mods.add(new LDAPModification(ModificationType.REPLACE,
+                                  new LDAPAttribute("description", values)));
+
+    ModifyRequestProtocolOp modifyRequest =
+         new ModifyRequestProtocolOp(new ASN1OctetString("o=test"), mods);
+    message = new LDAPMessage(2, modifyRequest,
+                       DelayPreOpPlugin.createDelayLDAPControlList(5000));
+    w.writeElement(message.encode());
+
+
+    // Send the abandon request to the server and wait a few seconds to ensure
+    // it has completed before closing the connection.
+    AbandonRequestProtocolOp abandonRequest = new AbandonRequestProtocolOp(2);
+    w.writeElement(new LDAPMessage(3, abandonRequest).encode());
+
+
+    // Normally, abandoned operations don't receive a response.  However, the
+    // testing configuration has been updated to ensure that if an operation
+    // does get abandoned, the server will return a response for it with a
+    // result code of "cancelled".
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    ModifyResponseProtocolOp modifyResponse =
+         message.getModifyResponseProtocolOp();
+    assertEquals(modifyResponse.getResultCode(), LDAPResultCode.CANCELED);
+
+    s.close();
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+  }
+
+
+
+  /**
+   * Tests the ability to abandon a modify DN operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAbandonModifyDN()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+    InvocationCounterPlugin.resetAllCounters();
+
+
+    // Add an entry to the server that we can rename.
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: cn=test,o=test",
+         "objectClass: top",
+         "objectClass: device",
+         "cn: test");
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+
+    // Establish a connection to the server and bind as a root user.
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(6000);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest);
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+    // Create a modify DN request and send it to the server.  Make sure to
+    // include the delay request control so it won't complete before we can send
+    // the abandon request.
+    ModifyDNRequestProtocolOp modifyDNRequest =
+         new ModifyDNRequestProtocolOp(new ASN1OctetString("cn=test,o=test"),
+                                       new ASN1OctetString("cn=test2"), true);
+    message = new LDAPMessage(2, modifyDNRequest,
+                       DelayPreOpPlugin.createDelayLDAPControlList(5000));
+    w.writeElement(message.encode());
+
+
+    // Send the abandon request to the server and wait a few seconds to ensure
+    // it has completed before closing the connection.
+    AbandonRequestProtocolOp abandonRequest = new AbandonRequestProtocolOp(2);
+    w.writeElement(new LDAPMessage(3, abandonRequest).encode());
+
+
+    // Normally, abandoned operations don't receive a response.  However, the
+    // testing configuration has been updated to ensure that if an operation
+    // does get abandoned, the server will return a response for it with a
+    // result code of "cancelled".
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    ModifyDNResponseProtocolOp modifyDNResponse =
+         message.getModifyDNResponseProtocolOp();
+    assertEquals(modifyDNResponse.getResultCode(), LDAPResultCode.CANCELED);
+
+    s.close();
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+  }
+
+
+
+  /**
+   * Tests the ability to abandon a search operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAbandonSearch()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+    InvocationCounterPlugin.resetAllCounters();
+
+
+    // Establish a connection to the server and bind as a root user.
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(6000);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest);
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), LDAPResultCode.SUCCESS);
+
+
+    // Create a search request and send it to the server.  Make sure to include
+    // the delay request control so it won't complete before we can send the
+    // abandon request.
+    SearchRequestProtocolOp searchRequest =
+         new SearchRequestProtocolOp(new ASN1OctetString("o=test"),
+                                     SearchScope.BASE_OBJECT,
+                                     DereferencePolicy.NEVER_DEREF_ALIASES, 0,
+                                     0, false,
+                                     LDAPFilter.decode("(match=false)"),
+                                     new LinkedHashSet<String>());
+    message = new LDAPMessage(2, searchRequest,
+                       DelayPreOpPlugin.createDelayLDAPControlList(5000));
+    w.writeElement(message.encode());
+
+
+    // Send the abandon request to the server and wait a few seconds to ensure
+    // it has completed before closing the connection.
+    AbandonRequestProtocolOp abandonRequest = new AbandonRequestProtocolOp(2);
+    w.writeElement(new LDAPMessage(3, abandonRequest).encode());
+
+
+    // Normally, abandoned operations don't receive a response.  However, the
+    // testing configuration has been updated to ensure that if an operation
+    // does get abandoned, the server will return a response for it with a
+    // result code of "cancelled".
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    SearchResultDoneProtocolOp searchDone =
+         message.getSearchResultDoneProtocolOp();
+    assertEquals(searchDone.getResultCode(), LDAPResultCode.CANCELED);
+
+    s.close();
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+  }
 }
 
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
new file mode 100644
index 0000000..60f780e
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
@@ -0,0 +1,1855 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.core;
+
+
+
+import java.net.Socket;
+import java.util.ArrayList;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.BindOperation;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.plugins.DisconnectClientPlugin;
+import org.opends.server.plugins.InvocationCounterPlugin;
+import org.opends.server.plugins.ShortCircuitPlugin;
+import org.opends.server.protocols.asn1.ASN1Element;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.asn1.ASN1Reader;
+import org.opends.server.protocols.asn1.ASN1Sequence;
+import org.opends.server.protocols.asn1.ASN1Writer;
+import org.opends.server.protocols.internal.InternalClientConnection;
+import org.opends.server.protocols.ldap.BindRequestProtocolOp;
+import org.opends.server.protocols.ldap.BindResponseProtocolOp;
+import org.opends.server.protocols.ldap.LDAPMessage;
+import org.opends.server.protocols.ldap.LDAPResultCode;
+import org.opends.server.types.Attribute;
+import org.opends.server.types.AuthenticationInfo;
+import org.opends.server.types.AuthenticationType;
+import org.opends.server.types.ByteString;
+import org.opends.server.types.Control;
+import org.opends.server.types.DN;
+import org.opends.server.types.Entry;
+import org.opends.server.types.Modification;
+import org.opends.server.types.ModificationType;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+import static org.opends.server.protocols.ldap.LDAPConstants.*;
+
+
+
+/**
+ * A set of test cases for bind operations
+ */
+public class BindOperationTestCase
+       extends OperationTestCase
+{
+  /**
+   * Retrieves a set of bind operation objects using simple authentication.
+   *
+   * @return  A set of bind operation objects using simple authentication.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @DataProvider(name = "simpleBinds")
+  public Object[][] getSimpleBindOperations()
+         throws Exception
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    ArrayList<Control> noControls = new ArrayList<Control>(0);
+    ASN1OctetString nullOS = null;
+    DN nullDN = null;
+
+    BindOperation[] simpleBinds = new BindOperation[]
+    {
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new ASN1OctetString(), new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new ASN1OctetString(),
+                        new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullOS, new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullOS, new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new ASN1OctetString(), nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new ASN1OctetString(), nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullOS, nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullOS, nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new ASN1OctetString("cn=Directory Manager"),
+                        new ASN1OctetString("password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new DN(), new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new DN(), new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullDN, new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullDN, new ASN1OctetString()),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new DN(), nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new DN(), nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullDN, nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullDN, nullOS),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, DN.decode("cn=Directory Manager"),
+                        new ASN1OctetString("password"))
+    };
+
+    Object[][] array = new Object[simpleBinds.length][1];
+    for (int i=0; i < simpleBinds.length; i++)
+    {
+      array[i][0] = simpleBinds[i];
+    }
+
+    return array;
+  }
+
+
+
+  /**
+   * Retrieves a set of bind operation objects using SASL authentication.
+   *
+   * @return  A set of bind operation objects using SASL authentication.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @DataProvider(name = "saslBinds")
+  public Object[][] getSASLBindOperations()
+         throws Exception
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+    ArrayList<Control> noControls = new ArrayList<Control>(0);
+    ASN1OctetString nullOS = null;
+    DN nullDN = null;
+
+    BindOperation[] saslBinds = new BindOperation[]
+    {
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new ASN1OctetString(), "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new ASN1OctetString(), "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullOS, "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullOS, "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new ASN1OctetString(), "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new ASN1OctetString(), "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullOS, "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullOS, "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new DN(), "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new DN(), "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullDN, "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullDN, "EXTERNAL", null),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, new DN(), "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, new DN(), "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        null, nullDN, "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password")),
+      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                        noControls, nullDN, "PLAIN",
+                        new ASN1OctetString("\u0000u:test.user\u0000password"))
+    };
+
+    Object[][] array = new Object[saslBinds.length][1];
+    for (int i=0; i < saslBinds.length; i++)
+    {
+      array[i][0] = saslBinds[i];
+    }
+
+    return array;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public Operation[] createTestOperations()
+         throws Exception
+  {
+    Object[][] simpleBinds = getSimpleBindOperations();
+    Object[][] saslBinds   = getSASLBindOperations();
+
+    Operation[] bindOps = new Operation[simpleBinds.length + saslBinds.length];
+
+    int pos = 0;
+    for (int i=0; i < simpleBinds.length; i++)
+    {
+      bindOps[pos++] = (BindOperation) simpleBinds[i][0];
+    }
+
+    for (int i=0; i < saslBinds.length; i++)
+    {
+      bindOps[pos++] = (BindOperation) saslBinds[i][0];
+    }
+
+    return bindOps;
+  }
+
+
+
+  /**
+   * Tests the <CODE>getAuthenticationType</CODE> method for simple bind
+   * operations.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetAuthenticationTypeSimple(BindOperation o)
+  {
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SIMPLE);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getAuthenticationType</CODE> method for SASL bind
+   * operations.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetAuthenticationTypeSASL(BindOperation o)
+  {
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SASL);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getRawBindDN</CODE> method for simple bind operations.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetRawBindDNSimple(BindOperation o)
+  {
+    assertNotNull(o.getRawBindDN());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getRawBindDN</CODE> method for SASL bind operations.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetRawBindDNSASL(BindOperation o)
+  {
+    assertNotNull(o.getRawBindDN());
+  }
+
+
+
+  /**
+   * Tests the <CODE>setRawBindDN()</CODE> method for simple bind operations.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testSetRawBindDNSimple(BindOperation o)
+  {
+    ByteString originalRawBindDN = o.getRawBindDN();
+    assertNotNull(originalRawBindDN);
+
+    o.setRawBindDN(null);
+    assertEquals(o.getRawBindDN(), new ASN1OctetString());
+
+    o.setRawBindDN(new ASN1OctetString());
+    assertEquals(o.getRawBindDN(), new ASN1OctetString());
+
+    o.setRawBindDN(new ASN1OctetString("cn=Directory Manager"));
+    assertEquals(o.getRawBindDN(), new ASN1OctetString("cn=Directory Manager"));
+
+    o.setRawBindDN(originalRawBindDN);
+    assertEquals(o.getRawBindDN(), originalRawBindDN);
+  }
+
+
+
+  /**
+   * Tests the <CODE>setRawBindDN()</CODE> method for SASL bind operations.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testSetRawBindDNSASL(BindOperation o)
+  {
+    ByteString originalRawBindDN = o.getRawBindDN();
+    assertNotNull(originalRawBindDN);
+
+    o.setRawBindDN(null);
+    assertEquals(o.getRawBindDN(), new ASN1OctetString());
+
+    o.setRawBindDN(new ASN1OctetString());
+    assertEquals(o.getRawBindDN(), new ASN1OctetString());
+
+    o.setRawBindDN(new ASN1OctetString("cn=Directory Manager"));
+    assertEquals(o.getRawBindDN(), new ASN1OctetString("cn=Directory Manager"));
+
+    o.setRawBindDN(originalRawBindDN);
+    assertEquals(o.getRawBindDN(), originalRawBindDN);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getBindDN</CODE> and <CODE>setBindDN</CODE> methods on bind
+   * operations using simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetAndSetBindDNSimple(BindOperation o)
+         throws Exception
+  {
+    DN originalDN = o.getBindDN();
+
+    o.setBindDN(null);
+    assertNull(o.getBindDN());
+
+    o.setBindDN(new DN());
+    assertNotNull(o.getBindDN());
+    assertTrue(o.getBindDN().isNullDN());
+
+    o.setBindDN(DN.decode("cn=Directory Manager"));
+    assertNotNull(o.getBindDN());
+    assertEquals(o.getBindDN(), DN.decode("cn=Directory Manager"));
+
+    o.setBindDN(originalDN);
+    assertEquals(o.getBindDN(), originalDN);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSimplePassword</CODE> method for bind operations using
+   * simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetSimplePasswordSimple(BindOperation o)
+  {
+    assertNotNull(o.getSimplePassword());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSimplePassword</CODE> method for bind operations using
+   * SASL authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetSimplePasswordSASL(BindOperation o)
+  {
+    assertNull(o.getSimplePassword());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSASLMechanism</CODE> method for bind operations using
+   * simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetSASLMechanismSimple(BindOperation o)
+  {
+    assertNull(o.getSASLMechanism());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSASLMechanism</CODE> method for bind operations using
+   * SASL authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetSASLMechanismSASL(BindOperation o)
+  {
+    assertNotNull(o.getSASLMechanism());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSASLCredentials</CODE> method for bind operations using
+   * simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetSASLCredentialsSimple(BindOperation o)
+  {
+    assertNull(o.getSASLCredentials());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSASLCredentials</CODE> method for bind operations using
+   * SASL authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetSASLCredentialsSASL(BindOperation o)
+  {
+    // We don't know whether they should be null or not, so we'll just not
+    // bother checking what it returns.
+    o.getSASLCredentials();
+  }
+
+
+
+  /**
+   * Tests the ability to change a simple bind operation to a SASL bind
+   * operation and back again.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testChangeSimpleToSASLAndBack(BindOperation o)
+  {
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SIMPLE);
+    assertNull(o.getSASLMechanism());
+    assertNull(o.getSASLCredentials());
+
+    ByteString originalPassword = o.getSimplePassword();
+    assertNotNull(originalPassword);
+
+    o.setSASLCredentials("EXTERNAL", null);
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SASL);
+    assertNotNull(o.getSASLMechanism());
+    assertNull(o.getSASLCredentials());
+
+    o.setSimplePassword(originalPassword);
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SIMPLE);
+    assertNull(o.getSASLMechanism());
+    assertNull(o.getSASLCredentials());
+
+    o.setSASLCredentials("PLAIN",
+         new ASN1OctetString("\u0000u:test.user\u0000password"));
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SASL);
+    assertNotNull(o.getSASLMechanism());
+    assertNotNull(o.getSASLCredentials());
+
+    o.setSimplePassword(originalPassword);
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SIMPLE);
+    assertNull(o.getSASLMechanism());
+    assertNull(o.getSASLCredentials());
+  }
+
+
+
+  /**
+   * Tests the ability to change a SASL bind operation to a simple bind
+   * operation and back again.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testChangeSASLToSimpleAndBack(BindOperation o)
+  {
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SASL);
+    assertNull(o.getSimplePassword());
+
+    String          originalMech  = o.getSASLMechanism();
+    ASN1OctetString originalCreds = o.getSASLCredentials();
+    assertNotNull(originalMech);
+
+    o.setSimplePassword(null);
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SIMPLE);
+    assertNotNull(o.getSimplePassword());
+
+    o.setSASLCredentials(originalMech, originalCreds);
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SASL);
+    assertNull(o.getSimplePassword());
+
+    o.setSimplePassword(new ASN1OctetString());
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SIMPLE);
+    assertNotNull(o.getSimplePassword());
+
+    o.setSASLCredentials(originalMech, originalCreds);
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SASL);
+    assertNull(o.getSimplePassword());
+
+    o.setSimplePassword(new ASN1OctetString("password"));
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SIMPLE);
+    assertNotNull(o.getSimplePassword());
+
+    o.setSASLCredentials(originalMech, originalCreds);
+    assertEquals(o.getAuthenticationType(), AuthenticationType.SASL);
+    assertNull(o.getSimplePassword());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getServerSASLCredentials</CODE> method for bind operations
+   * using simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetServerSASLCredentialsSimple(BindOperation o)
+  {
+    assertNull(o.getServerSASLCredentials());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getServerSASLCredentials</CODE> method for bind operations
+   * using SASL authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetServerSASLCredentialsSASL(BindOperation o)
+  {
+    assertNull(o.getServerSASLCredentials());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSASLAuthUserEntry</CODE> method for bind operations
+   * using simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetSASLAuthUserEntrySimple(BindOperation o)
+  {
+    assertNull(o.getSASLAuthUserEntry());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSASLAuthUserEntry</CODE> method for bind operations
+   * using SASL authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetSASLAuthUserEntrySASL(BindOperation o)
+  {
+    assertNull(o.getSASLAuthUserEntry());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getSASLAuthUserEntry</CODE> method for completed SASL bind
+   * operations in which this value will be set.
+   */
+  public void testGetSASLAuthUserEntryNonNull()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+                       conn.processSASLBind(new DN(), "PLAIN", saslCreds);
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(bindOperation.getSASLAuthUserEntry());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getUserEntryDN</CODE> method for bind operations using
+   * simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetUserEntryDNSimple(BindOperation o)
+  {
+    assertNull(o.getUserEntryDN());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getUserEntryDN</CODE> method for a completed bind operation
+   * using simple authentication in which this value will be set.
+   */
+  @Test()
+  public void testGetUserEntryDNSimpleNonNull()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("cn=Directory Manager"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(bindOperation.getUserEntryDN());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getUserEntryDN</CODE> method for bind operations using SASL
+   * authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetUserEntryDNSASL(BindOperation o)
+{
+    assertNull(o.getUserEntryDN());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getUserEntryDN</CODE> method for a completed bind operation
+   * using SASL authentication in which this value will be set.
+   */
+  @Test()
+  public void testGetUserEntryDNSASLNonNull()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(bindOperation.getUserEntryDN());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getProcessingStartTime</CODE>,
+   * <CODE>getProcessingStopTime</CODE>, and <CODE>getProcessingTime()</CODE>
+   * methods for a completed bind operation using simple authentication.
+   */
+  @Test()
+  public void testGetProcessingStartAndStopTimesSimple()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("cn=Directory Manager"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(bindOperation.getProcessingStartTime() > 0);
+    assertTrue(bindOperation.getProcessingStopTime() >=
+               bindOperation.getProcessingStartTime());
+    assertTrue(bindOperation.getProcessingTime() >= 0);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getProcessingStartTime</CODE>,
+   * <CODE>getProcessingStopTime</CODE>, and <CODE>getProcessingTime()</CODE>
+   * methods for a completed bind operation using SASL authentication.
+   */
+  @Test()
+  public void testGetProcessingStartAndStopTimesSASL()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+    assertTrue(bindOperation.getProcessingStartTime() > 0);
+    assertTrue(bindOperation.getProcessingStopTime() >=
+               bindOperation.getProcessingStartTime());
+    assertTrue(bindOperation.getProcessingTime() >= 0);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getOperationType</CODE> method for bind operations using
+   * simple authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "simpleBinds")
+  public void testGetOperationTypeSimple(BindOperation o)
+  {
+    assertEquals(o.getOperationType(), OperationType.BIND);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getOperationType</CODE> method for bind operations using
+   * SASL authentication.
+   *
+   * @param  o  The bind operation to be tested.
+   */
+  @Test(dataProvider = "saslBinds")
+  public void testGetOperationTypeSASL(BindOperation o)
+  {
+    assertEquals(o.getOperationType(), OperationType.BIND);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getResponseLogElements</CODE> method for a completed
+   * successful bind operation using simple authentication.
+   */
+  @Test()
+  public void testGetResponseLogElementsSimple()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("cn=Directory Manager"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(bindOperation.getResponseLogElements());
+    assertTrue(bindOperation.getResponseLogElements().length > 0);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getResponseLogElements</CODE> method for a completed bind
+   * operation using SASL authentication.
+   */
+  @Test()
+  public void testGetResponseLogElementsSASL()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(bindOperation.getResponseLogElements());
+    assertTrue(bindOperation.getResponseLogElements().length > 0);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getResponseLogElements</CODE> method for a failed simple
+   * bind attempt in which the target user didn't exist.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testGetResponseLogElementsSimpleNoSuchUser()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("uid=test,o=test"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+    assertNotNull(bindOperation.getResponseLogElements());
+    assertTrue(bindOperation.getResponseLogElements().length > 0);
+  }
+
+
+
+  /**
+   * Tests a simple bind operation to ensure that all plugin types are invoked
+   * as expected.
+   */
+  @Test()
+  public void testAllPluginsCalledSimple()
+  {
+    InvocationCounterPlugin.resetAllCounters();
+
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("cn=Directory Manager"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreOperationCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPostOperationCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPostResponseCount() > 0);
+  }
+
+
+
+  /**
+   * Tests a SASL bind operation to ensure that all plugin types are invoked
+   * as expected.
+   */
+  @Test()
+  public void testAllPluginsCalledSASL()
+  {
+    InvocationCounterPlugin.resetAllCounters();
+
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreOperationCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPostOperationCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPostResponseCount() > 0);
+  }
+
+
+
+  /**
+   * Tests an anonymous simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in pre-parse plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPreParseSimpleAnonymous()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), 3,
+                                   new ASN1OctetString());
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList("PreParse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an anonymous simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in pre-operation plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPreOperationSimpleAnonymous()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), 3,
+                                   new ASN1OctetString());
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PreOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an anonymous simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in post-operation plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPostOperationSimpleAnonymous()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), 3,
+                                   new ASN1OctetString());
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an anonymous simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in post-response plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPostResponseSimpleAnonymous()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), 3,
+                                   new ASN1OctetString());
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostResponse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    while (element != null)
+    {
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertTrue((message.getProtocolOpType() == OP_TYPE_BIND_RESPONSE) ||
+                 (message.getProtocolOpType() == OP_TYPE_EXTENDED_RESPONSE));
+      element = r.readElement();
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an authenticated simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in pre-parse plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPreParseSimpleAuthenticated()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList("PreParse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an authenticated simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in pre-operation plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPreOperationSimpleAuthenticated()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PreOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an authenticated simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in post-operation plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPostOperationSimpleAuthenticated()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an authenticated simple bind operation to ensure that it's treated
+   * properly if the client connection is lost in post-response plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPostResponseSimpleAuthenticated()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostResponse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    while (element != null)
+    {
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertTrue((message.getProtocolOpType() == OP_TYPE_BIND_RESPONSE) ||
+                 (message.getProtocolOpType() == OP_TYPE_EXTENDED_RESPONSE));
+      element = r.readElement();
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a SASL bind operation to ensure that it's treated properly if the
+   * client connection is lost in pre-parse plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPreParseSASL()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), "PLAIN", saslCreds);
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList("PreParse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a SASL bind operation to ensure that it's treated properly if the
+   * client connection is lost in pre-operation plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPreOperationSASL()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), "PLAIN", saslCreds);
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PreOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a SASL bind operation to ensure that it's treated properly if the
+   * client connection is lost in post-operation plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPostOperationSASL()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), "PLAIN", saslCreds);
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a SASL bind operation to ensure that it's treated properly if the
+   * client connection is lost in post-response plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindDisconnectInPostResponseSASL()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), "PLAIN", saslCreds);
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostResponse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    while (element != null)
+    {
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertTrue((message.getProtocolOpType() == OP_TYPE_BIND_RESPONSE) ||
+                 (message.getProtocolOpType() == OP_TYPE_EXTENDED_RESPONSE));
+      element = r.readElement();
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an anonymous simple bind operation to ensure that it's treated
+   * properly if the operation gets short-circuited in pre-parse plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindShortCircuitInPreParseSimpleAnonymous()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), 3,
+                                   new ASN1OctetString());
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         ShortCircuitPlugin.createShortCircuitLDAPControlList(80, "PreParse"));
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), 80);
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an anonymous simple bind operation to ensure that it's treated
+   * properly if the operation gets short-circuited in pre-operation plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindShortCircuitInPreOperationSimpleAnonymous()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), 3,
+                                   new ASN1OctetString());
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         ShortCircuitPlugin.createShortCircuitLDAPControlList(80,
+                                                              "PreOperation"));
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), 80);
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an authenticated simple bind operation to ensure that it's treated
+   * properly if the operation gets short-circuited in pre-parse plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindShortCircuitInPreParseSimpleAuthenticated()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         ShortCircuitPlugin.createShortCircuitLDAPControlList(80, "PreParse"));
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), 80);
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an authenticated simple bind operation to ensure that it's treated
+   * properly if the operation gets short-circuited in pre-operation plugin
+   * processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindShortCircuitInPreOperationSimpleAuthenticated()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString("cn=Directory Manager"),
+                                   3, new ASN1OctetString("password"));
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         ShortCircuitPlugin.createShortCircuitLDAPControlList(80,
+                                                              "PreOperation"));
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), 80);
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a SASL bind operation to ensure that it's treated properly if the
+   * operation gets short-circuited in pre-parse plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindShortCircuitInPreParseSASL()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), "PLAIN", saslCreds);
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         ShortCircuitPlugin.createShortCircuitLDAPControlList(80, "PreParse"));
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), 80);
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a SASL bind operation to ensure that it's treated properly if the
+   * operation gets short-circuited in pre-operation plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testBindShortCircuitInPreOperationSASL()
+         throws Exception
+  {
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindRequestProtocolOp bindRequest =
+         new BindRequestProtocolOp(new ASN1OctetString(), "PLAIN", saslCreds);
+    LDAPMessage message = new LDAPMessage(1, bindRequest,
+         ShortCircuitPlugin.createShortCircuitLDAPControlList(80,
+                                                              "PreOperation"));
+    w.writeElement(message.encode());
+
+    message = LDAPMessage.decode(r.readElement().decodeAsSequence());
+    BindResponseProtocolOp bindResponse = message.getBindResponseProtocolOp();
+    assertEquals(bindResponse.getResultCode(), 80);
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation with an invalid user DN.
+   */
+  @Test()
+  public void testSimpleBindInvalidDN()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("invaliddn"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+  }
+
+
+
+  /**
+   * Tests performing a SASL bind operation with an invalid user DN.
+   */
+  @Test()
+  public void testSASLBindInvalidDN()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+         conn.processSASLBind(new ASN1OctetString("invaliddn"), "PLAIN",
+                              saslCreds);
+    assertEquals(bindOperation.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation with an unsupported control that
+   * is marked critical.
+   */
+  @Test()
+  public void testSimpleBindUnsupportedCriticalControl()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ArrayList<Control> requestControls = new ArrayList<Control>(1);
+    requestControls.add(new Control("1.2.3.4", true));
+
+    BindOperation bindOperation =
+         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                           requestControls, new DN(), new ASN1OctetString());
+    bindOperation.run();
+    assertEquals(bindOperation.getResultCode(),
+                 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
+  }
+
+
+
+  /**
+   * Tests performing a SASL bind operation with an unsupported control that is
+   * marked critical.
+   */
+  @Test()
+  public void testSASLBindUnsupportedCriticalControl()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ArrayList<Control> requestControls = new ArrayList<Control>(1);
+    requestControls.add(new Control("1.2.3.4", true));
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                           requestControls, new DN(), "PLAIN", saslCreds);
+    bindOperation.run();
+    assertEquals(bindOperation.getResultCode(),
+                 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation with an unsupported control that
+   * is not marked critical.
+   */
+  @Test()
+  public void testSimpleBindUnsupportedNonCriticalControl()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ArrayList<Control> requestControls = new ArrayList<Control>(1);
+    requestControls.add(new Control("1.2.3.4", false));
+
+    BindOperation bindOperation =
+         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                           requestControls, new DN(), new ASN1OctetString());
+    bindOperation.run();
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests performing a SASL bind operation with an unsupported control that is
+   * is not marked critical.
+   */
+  @Test()
+  public void testSASLBindUnsupportedNonCriticalControl()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    ArrayList<Control> requestControls = new ArrayList<Control>(1);
+    requestControls.add(new Control("1.2.3.4", false));
+
+    ASN1OctetString saslCreds =
+         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
+
+    BindOperation bindOperation =
+         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                           requestControls, new DN(), "PLAIN", saslCreds);
+    bindOperation.run();
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation with the DN of a user that doesn't
+   * exist.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleBindNoSuchUser()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("uid=test,o=test"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation with the DN of a valid user but
+   * without including a password in the request, with the server configured to
+   * disallow that combination.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleBindWithDNNoPasswordDisallowed()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("cn=Directory Manager"),
+                                new ASN1OctetString());
+    assertEquals(bindOperation.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation with the DN of a valid user but
+   * without including a password in the request, with the server configured to
+   * allow that combination.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleBindWithDNNoPasswordAllowed()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    String attr = "ds-cfg-bind-with-dn-requires-password";
+    ArrayList<Modification> mods = new ArrayList<Modification>();
+    mods.add(new Modification(ModificationType.REPLACE,
+                              new Attribute(attr, "false")));
+    ModifyOperation modifyOperation =
+         conn.processModify(DN.decode("cn=config"), mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("cn=Directory Manager"),
+                                new ASN1OctetString());
+    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
+
+    mods.clear();
+    mods.add(new Modification(ModificationType.REPLACE,
+                              new Attribute(attr, "true")));
+    modifyOperation =  conn.processModify(DN.decode("cn=config"), mods);
+    assertEquals(modifyOperation.getResultCode(), ResultCode.SUCCESS);
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation as a user who doesn't have a
+   * password.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSimpleBindNoUserPassword()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry e = TestCaseUtils.makeEntry(
+         "dn: uid=test,o=test",
+         "objectClass: top",
+         "objectClass: person",
+         "objectClass: organizationalPerson",
+         "objectClass: inetOrgPerson",
+         "uid: test",
+         "givenName: Test",
+         "sn: User",
+         "cn: Test User");
+
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    AddOperation addOperation =
+         conn.processAdd(e.getDN(), e.getObjectClasses(), e.getUserAttributes(),
+                         e.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("uid=test,o=test"),
+                                new ASN1OctetString("password"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+  }
+
+
+
+  /**
+   * Tests performing a simple bind operation with a valid DN but incorrect
+   * password.
+   */
+  @Test()
+  public void testSimpleBindWrongPassword()
+  {
+    InternalClientConnection conn =
+         new InternalClientConnection(new AuthenticationInfo());
+
+    BindOperation bindOperation =
+         conn.processSimpleBind(new ASN1OctetString("cn=Directory Manager"),
+                                new ASN1OctetString("wrongpassword"));
+    assertEquals(bindOperation.getResultCode(), ResultCode.INVALID_CREDENTIALS);
+  }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/OperationTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/OperationTestCase.java
index 46b970b..fe464d5 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/OperationTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/OperationTestCase.java
@@ -28,6 +28,7 @@
 
 
 
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
@@ -45,6 +46,20 @@
        extends CoreTestCase
 {
   /**
+   * Ensures that the Directory Server is running.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @BeforeClass()
+  public void startServer()
+         throws Exception
+  {
+    TestCaseUtils.startServer();
+  }
+
+
+
+  /**
    * Creates a set of valid operation instances of this type that may be used
    * for testing the general methods defined in the Operation superclass.  Only
    * the constructors for the operation need to be used -- it does not require
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java
new file mode 100644
index 0000000..33c0bd5
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/ShortCircuitPlugin.java
@@ -0,0 +1,651 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.plugins;
+
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.opends.server.api.plugin.DirectoryServerPlugin;
+import org.opends.server.api.plugin.PluginType;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.api.plugin.PreParsePluginResult;
+import org.opends.server.config.ConfigEntry;
+import org.opends.server.config.ConfigException;
+import org.opends.server.core.AbandonOperation;
+import org.opends.server.core.AddOperation;
+import org.opends.server.core.BindOperation;
+import org.opends.server.core.CompareOperation;
+import org.opends.server.core.DeleteOperation;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.ExtendedOperation;
+import org.opends.server.core.ModifyOperation;
+import org.opends.server.core.ModifyDNOperation;
+import org.opends.server.core.Operation;
+import org.opends.server.core.SearchOperation;
+import org.opends.server.core.UnbindOperation;
+import org.opends.server.protocols.asn1.ASN1Element;
+import org.opends.server.protocols.asn1.ASN1Enumerated;
+import org.opends.server.protocols.asn1.ASN1OctetString;
+import org.opends.server.protocols.asn1.ASN1Sequence;
+import org.opends.server.protocols.ldap.LDAPControl;
+import org.opends.server.types.Control;
+import org.opends.server.types.ResultCode;
+
+
+
+/**
+ * This class defines a very simple plugin that causes request processing to end
+ * immediately and send a specific result code to the client.  It will be
+ * triggered by a control contained in the client request, and may be invoked
+ * during either pre-parse or pre-operation processing.
+ */
+public class ShortCircuitPlugin
+       extends DirectoryServerPlugin
+{
+  /**
+   * The OID for the short circuit request control, which is used to flag
+   * operations that should cause the operation processing to end immediately.
+   */
+  public static final String OID_SHORT_CIRCUIT_REQUEST =
+       "1.3.6.1.4.1.26027.1.999.3";
+
+
+
+  /**
+   * Creates a new instance of this Directory Server plugin.  Every
+   * plugin must implement a default constructor (it is the only one
+   * that will be used to create plugins defined in the
+   * configuration), and every plugin constructor must call
+   * <CODE>super()</CODE> as its first element.
+   */
+  public ShortCircuitPlugin()
+  {
+    super();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public void initializePlugin(DirectoryServer directoryServer,
+                               Set<PluginType> pluginTypes,
+                               ConfigEntry configEntry)
+         throws ConfigException
+  {
+    // This plugin may only be used as a pre-parse or pre-operation plugin.
+    for (PluginType t : pluginTypes)
+    {
+      switch (t)
+      {
+        case PRE_PARSE_ABANDON:
+        case PRE_PARSE_ADD:
+        case PRE_PARSE_BIND:
+        case PRE_PARSE_COMPARE:
+        case PRE_PARSE_DELETE:
+        case PRE_PARSE_EXTENDED:
+        case PRE_PARSE_MODIFY:
+        case PRE_PARSE_MODIFY_DN:
+        case PRE_PARSE_SEARCH:
+        case PRE_PARSE_UNBIND:
+        case PRE_OPERATION_ADD:
+        case PRE_OPERATION_BIND:
+        case PRE_OPERATION_COMPARE:
+        case PRE_OPERATION_DELETE:
+        case PRE_OPERATION_EXTENDED:
+        case PRE_OPERATION_MODIFY:
+        case PRE_OPERATION_MODIFY_DN:
+        case PRE_OPERATION_SEARCH:
+          // This is fine.
+          break;
+        default:
+          throw new ConfigException(-1, "Invalid plugin type " + t +
+                                    " for the short circuit plugin.");
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(AbandonOperation abandonOperation)
+  {
+    int resultCode = shortCircuitInternal(abandonOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      abandonOperation.setResultCode(ResultCode.valueOf(resultCode));
+      abandonOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(AddOperation addOperation)
+  {
+    int resultCode = shortCircuitInternal(addOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      addOperation.setResultCode(ResultCode.valueOf(resultCode));
+      addOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(BindOperation bindOperation)
+  {
+    int resultCode = shortCircuitInternal(bindOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      bindOperation.setResultCode(ResultCode.valueOf(resultCode));
+      bindOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(CompareOperation compareOperation)
+  {
+    int resultCode = shortCircuitInternal(compareOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      compareOperation.setResultCode(ResultCode.valueOf(resultCode));
+      compareOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(DeleteOperation deleteOperation)
+  {
+    int resultCode = shortCircuitInternal(deleteOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      deleteOperation.setResultCode(ResultCode.valueOf(resultCode));
+      deleteOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ExtendedOperation extendedOperation)
+  {
+    int resultCode = shortCircuitInternal(extendedOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      extendedOperation.setResultCode(ResultCode.valueOf(resultCode));
+      extendedOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ModifyOperation modifyOperation)
+  {
+    int resultCode = shortCircuitInternal(modifyOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      modifyOperation.setResultCode(ResultCode.valueOf(resultCode));
+      modifyOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ModifyDNOperation modifyDNOperation)
+  {
+    int resultCode = shortCircuitInternal(modifyDNOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      modifyDNOperation.setResultCode(ResultCode.valueOf(resultCode));
+      modifyDNOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(SearchOperation searchOperation)
+  {
+    int resultCode = shortCircuitInternal(searchOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      searchOperation.setResultCode(ResultCode.valueOf(resultCode));
+      searchOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(UnbindOperation unbindOperation)
+  {
+    int resultCode = shortCircuitInternal(unbindOperation, "PreParse");
+    if (resultCode >= 0)
+    {
+      unbindOperation.setResultCode(ResultCode.valueOf(resultCode));
+      unbindOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreParsePluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(AddOperation addOperation)
+  {
+    int resultCode = shortCircuitInternal(addOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      addOperation.setResultCode(ResultCode.valueOf(resultCode));
+      addOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(BindOperation bindOperation)
+  {
+    int resultCode = shortCircuitInternal(bindOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      bindOperation.setResultCode(ResultCode.valueOf(resultCode));
+      bindOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(CompareOperation
+                                                      compareOperation)
+  {
+    int resultCode = shortCircuitInternal(compareOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      compareOperation.setResultCode(ResultCode.valueOf(resultCode));
+      compareOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(DeleteOperation
+                                                      deleteOperation)
+  {
+    int resultCode = shortCircuitInternal(deleteOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      deleteOperation.setResultCode(ResultCode.valueOf(resultCode));
+      deleteOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ExtendedOperation
+                                                      extendedOperation)
+  {
+    int resultCode = shortCircuitInternal(extendedOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      extendedOperation.setResultCode(ResultCode.valueOf(resultCode));
+      extendedOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ModifyOperation
+                                                      modifyOperation)
+  {
+    int resultCode = shortCircuitInternal(modifyOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      modifyOperation.setResultCode(ResultCode.valueOf(resultCode));
+      modifyOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ModifyDNOperation
+                                                      modifyDNOperation)
+  {
+    int resultCode = shortCircuitInternal(modifyDNOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      modifyDNOperation.setResultCode(ResultCode.valueOf(resultCode));
+      modifyDNOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(SearchOperation
+                                                      searchOperation)
+  {
+    int resultCode = shortCircuitInternal(searchOperation, "PreOperation");
+    if (resultCode >= 0)
+    {
+      searchOperation.setResultCode(ResultCode.valueOf(resultCode));
+      searchOperation.appendErrorMessage("Short-circuit in pre-parse");
+      return new PreOperationPluginResult(false, false, true);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * Looks for a short-circuit request control in the operation and if one is
+   * found with the correct section then generate the appropriate result.
+   *
+   * @param  operation  The operation to be processed.
+   * @param  section    The section to match in the control value.
+   *
+   * @return  The result code that should be immediately sent to the client, or
+   *          -1 if operation processing should continue as normal.
+   */
+  private int shortCircuitInternal(Operation operation, String section)
+  {
+    List<Control> requestControls = operation.getRequestControls();
+    if (requestControls != null)
+    {
+      for (Control c : requestControls)
+      {
+        if (c.getOID().equals(OID_SHORT_CIRCUIT_REQUEST))
+        {
+          try
+          {
+            ASN1Sequence sequence =
+                 ASN1Sequence.decodeAsSequence(c.getValue().value());
+            ArrayList<ASN1Element> elements = sequence.elements();
+            int resultCode = elements.get(0).decodeAsEnumerated().intValue();
+            String controlSection =
+                        elements.get(1).decodeAsOctetString().stringValue();
+            if (section.equalsIgnoreCase(controlSection))
+            {
+              return resultCode;
+            }
+          }
+          catch (Exception e)
+          {
+            System.err.println("***** ERROR:  Could not decode short circuit " +
+                               "control value:  " + e);
+            e.printStackTrace();
+            return -1;
+          }
+        }
+      }
+    }
+
+
+    // If we've gotten here, then we shouldn't short-circuit the operation
+    // processing.
+    return -1;
+  }
+
+
+
+  /**
+   * Creates a short circuit request control with the specified result code and
+   * section.
+   *
+   * @param  resultCode  The result code to return to the client.
+   * @param  section     The section to use to determine when to short circuit.
+   *
+   * @return  The appropriate short circuit request control.
+   */
+  public static Control createShortCircuitControl(int resultCode,
+                                                  String section)
+  {
+    ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(2);
+    elements.add(new ASN1Enumerated(resultCode));
+    elements.add(new ASN1OctetString(section));
+
+    ASN1OctetString controlValue =
+         new ASN1OctetString(new ASN1Sequence(elements).encode());
+
+    return new Control(OID_SHORT_CIRCUIT_REQUEST, false, controlValue);
+  }
+
+
+
+  /**
+   * Retrieves a list containing a short circuit control with the specified
+   * result code and section.
+   *
+   * @param  resultCode  The result code to return to the client.
+   * @param  section     The section to use to determine when to short circuit.
+   *
+   * @return  A list containing the appropriate short circuit request control.
+   */
+  public static List<Control> createShortCircuitControlList(int resultCode,
+                                                            String section)
+  {
+    ArrayList<Control> controlList = new ArrayList<Control>(1);
+    controlList.add(createShortCircuitControl(resultCode, section));
+    return controlList;
+  }
+
+
+
+  /**
+   * Creates a short circuit LDAP request control with the specified result code
+   * and section.
+   *
+   * @param  resultCode  The result code to return to the client.
+   * @param  section     The section to use to determine when to short circuit.
+   *
+   * @return  The appropriate short circuit LDAP request control.
+   */
+  public static LDAPControl createShortCircuitLDAPControl(int resultCode,
+                                                          String section)
+  {
+    return new LDAPControl(createShortCircuitControl(resultCode, section));
+  }
+
+
+
+  /**
+   * Retrieves a list containing a short circuit LDAP control with the specified
+   * result code and section.
+   *
+   * @param  resultCode  The result code to return to the client.
+   * @param  section     The section to use to determine when to short circuit.
+   *
+   * @return  A list containing the appropriate short circuit LDAP request
+   *          control.
+   */
+  public static ArrayList<LDAPControl> createShortCircuitLDAPControlList(
+                                            int resultCode, String section)
+  {
+    ArrayList<LDAPControl> controlList = new ArrayList<LDAPControl>(1);
+    controlList.add(createShortCircuitLDAPControl(resultCode, section));
+    return controlList;
+  }
+}
+

--
Gitblit v1.10.0