From e1cc3de403b42ef84bf5bd91af4e32cc40185a81 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Tue, 26 Sep 2006 20:30:39 +0000
Subject: [PATCH] Add several changes related to testing:

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/OperationTestCase.java               |  370 ++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/DirectoryExceptionTestCase.java      |  293 +++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/InitializationExceptionTestCase.java |  130 ++
 opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java                        |   10 
 opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/DisconnectClientPlugin.java       |  941 ++++++++++++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/InvocationCounterPlugin.java      | 1067 ++++++++++++++++++
 opends/tests/unit-tests-testng/resource/config-changes.ldif                                           |  102 +
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java        |  509 ++++++++
 8 files changed, 3,422 insertions(+), 0 deletions(-)

diff --git a/opends/tests/unit-tests-testng/resource/config-changes.ldif b/opends/tests/unit-tests-testng/resource/config-changes.ldif
index 3567d55..5389b42 100644
--- a/opends/tests/unit-tests-testng/resource/config-changes.ldif
+++ b/opends/tests/unit-tests-testng/resource/config-changes.ldif
@@ -1,3 +1,8 @@
+dn: cn=config
+changetype: modify
+replace: ds-cfg-notify-abandoned-operations
+ds-cfg-notify-abandoned-operations: true
+
 dn: cn=LDAP Connection Handler,cn=Connection Handlers,cn=config
 changeType: modify
 replace: ds-cfg-listen-port
@@ -119,6 +124,103 @@
 ds-cfg-plugin-type: preOperationModifyDN
 ds-cfg-plugin-type: preOperationSearch
 
+dn: cn=Disconnect Client Plugin,cn=Plugins,cn=config
+changetype: add
+objectClass: top
+objectClass: ds-cfg-plugin
+cn: Disconnect Client Plugin
+ds-cfg-plugin-class: org.opends.server.plugins.DisconnectClientPlugin
+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
+ds-cfg-plugin-type: postOperationAbandon
+ds-cfg-plugin-type: postOperationAdd
+ds-cfg-plugin-type: postOperationBind
+ds-cfg-plugin-type: postOperationCompare
+ds-cfg-plugin-type: postOperationDelete
+ds-cfg-plugin-type: postOperationExtended
+ds-cfg-plugin-type: postOperationModify
+ds-cfg-plugin-type: postOperationModifyDN
+ds-cfg-plugin-type: postOperationSearch
+ds-cfg-plugin-type: postOperationUnbind
+ds-cfg-plugin-type: postResponseAdd
+ds-cfg-plugin-type: postResponseBind
+ds-cfg-plugin-type: postResponseCompare
+ds-cfg-plugin-type: postResponseDelete
+ds-cfg-plugin-type: postResponseExtended
+ds-cfg-plugin-type: postResponseModify
+ds-cfg-plugin-type: postResponseModifyDN
+ds-cfg-plugin-type: postResponseSearch
+
+dn: cn=Invocation Counter Plugin,cn=Plugins,cn=config
+changetype: add
+objectClass: top
+objectClass: ds-cfg-plugin
+cn: Invocation Counter Plugin
+ds-cfg-plugin-class: org.opends.server.plugins.InvocationCounterPlugin
+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
+ds-cfg-plugin-type: postOperationAbandon
+ds-cfg-plugin-type: postOperationAdd
+ds-cfg-plugin-type: postOperationBind
+ds-cfg-plugin-type: postOperationCompare
+ds-cfg-plugin-type: postOperationDelete
+ds-cfg-plugin-type: postOperationExtended
+ds-cfg-plugin-type: postOperationModify
+ds-cfg-plugin-type: postOperationModifyDN
+ds-cfg-plugin-type: postOperationSearch
+ds-cfg-plugin-type: postOperationUnbind
+ds-cfg-plugin-type: postResponseAdd
+ds-cfg-plugin-type: postResponseBind
+ds-cfg-plugin-type: postResponseCompare
+ds-cfg-plugin-type: postResponseDelete
+ds-cfg-plugin-type: postResponseExtended
+ds-cfg-plugin-type: postResponseModify
+ds-cfg-plugin-type: postResponseModifyDN
+ds-cfg-plugin-type: postResponseSearch
+ds-cfg-plugin-type: searchResultEntry
+ds-cfg-plugin-type: searchResultReference
+ds-cfg-plugin-type: intermediateResponse
+ds-cfg-plugin-type: postConnect
+ds-cfg-plugin-type: postDisconnect
+ds-cfg-plugin-type: ldifImport
+ds-cfg-plugin-type: ldifExport
+ds-cfg-plugin-type: startup
+ds-cfg-plugin-type: shutdown
+
 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/TestCaseUtils.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
index 952e055..5d9e7bf 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/TestCaseUtils.java
@@ -43,10 +43,13 @@
 import org.opends.server.core.InitializationException;
 import org.opends.server.loggers.Error;
 import org.opends.server.loggers.Debug;
+import org.opends.server.plugins.InvocationCounterPlugin;
 import org.opends.server.types.DN;
 import org.opends.server.types.FilePermission;
 import org.opends.server.types.OperatingSystem;
 
+import static org.testng.Assert.*;
+
 import static org.opends.server.util.ServerConstants.*;
 import static org.opends.server.util.StaticUtils.*;
 
@@ -118,6 +121,8 @@
       return;
     }
 
+    InvocationCounterPlugin.resetStartupCalled();
+
     // Get the build root and use it to create a test package directory.
     String buildRoot = System.getProperty(PROPERTY_BUILD_ROOT);
     File   testRoot  = new File(buildRoot + File.separator + "build" +
@@ -252,6 +257,9 @@
     Error.removeAllErrorLoggers(false);
     Debug.removeAllDebugLoggers(false);
     directoryServer.startServer();
+
+    assertTrue(InvocationCounterPlugin.startupCalled());
+
     SERVER_STARTED = true;
   }
 
@@ -263,7 +271,9 @@
   {
     if (SERVER_STARTED)
     {
+      InvocationCounterPlugin.resetShutdownCalled();
       DirectoryServer.shutDown("org.opends.server.TestCaseUtils", reason);
+      assertTrue(InvocationCounterPlugin.shutdownCalled());
       SERVER_STARTED = false;
     }
   }
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
new file mode 100644
index 0000000..9ba186d
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AbandonOperationTestCase.java
@@ -0,0 +1,509 @@
+/*
+ * 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.plugins.DelayPreOpPlugin;
+import org.opends.server.plugins.DisconnectClientPlugin;
+import org.opends.server.plugins.InvocationCounterPlugin;
+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.AbandonRequestProtocolOp;
+import org.opends.server.protocols.ldap.AddRequestProtocolOp;
+import org.opends.server.protocols.ldap.AddResponseProtocolOp;
+import org.opends.server.protocols.ldap.BindRequestProtocolOp;
+import org.opends.server.protocols.ldap.BindResponseProtocolOp;
+import org.opends.server.protocols.ldap.CompareRequestProtocolOp;
+import org.opends.server.protocols.ldap.CompareResponseProtocolOp;
+import org.opends.server.protocols.ldap.DeleteRequestProtocolOp;
+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.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.Entry;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of test cases for abandon operations
+ */
+public class AbandonOperationTestCase
+       extends OperationTestCase
+{
+  /**
+   * 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
+   * 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.
+   */
+  public Operation[] createTestOperations()
+         throws Exception
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    return new Operation[]
+    {
+      new AbandonOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                           new ArrayList<Control>(), 1)
+    };
+  }
+
+
+
+  /**
+   * Tests the <CODE>getIDToAbandon</CODE> method.
+   */
+  @Test()
+  public void testGetIDToAbandon()
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    AbandonOperation abandonOperation =
+         new AbandonOperation(conn, conn.nextOperationID(),
+                  conn.nextMessageID(), new ArrayList<Control>(), 1);
+    assertEquals(abandonOperation.getIDToAbandon(), 1);
+  }
+
+
+
+  /**
+   * Tests the <CODE>cancel</CODE> method.
+   */
+  @Test()
+  public void testCancel()
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    AbandonOperation abandonOperation =
+         new AbandonOperation(conn, conn.nextOperationID(),
+                  conn.nextMessageID(), new ArrayList<Control>(), 1);
+
+    CancelRequest cancelRequest = new CancelRequest(true, "Test Cancel");
+    assertEquals(abandonOperation.cancel(cancelRequest),
+                 CancelResult.CANNOT_CANCEL);
+  }
+
+
+
+  /**
+   * Tests the <CODE>getCancelRequest</CODE> method.
+   */
+  @Test()
+  public void testGetCancelRequest()
+  {
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    AbandonOperation abandonOperation =
+         new AbandonOperation(conn, conn.nextOperationID(),
+                  conn.nextMessageID(), new ArrayList<Control>(), 1);
+    assertNull(abandonOperation.getCancelRequest());
+  }
+
+
+
+  /**
+   * Invokes a number of operation methods on the provided abandon operation
+   * for which all processing has been completed.
+   *
+   * @param  abandonOperation  The operation to be tested.
+   */
+  private void examineCompletedOperation(AbandonOperation abandonOperation)
+  {
+    assertTrue(abandonOperation.getIDToAbandon() > 0);
+    assertTrue(abandonOperation.getProcessingStartTime() > 0);
+    assertTrue(abandonOperation.getProcessingStopTime() > 0);
+    assertTrue(abandonOperation.getProcessingTime() >= 0);
+    assertNotNull(abandonOperation.getResponseLogElements());
+  }
+
+
+
+  /**
+   * Attempts an internal abandon operation, which will fail because internal
+   * operations cannot be abandoned.
+   */
+  @Test()
+  public void testAbandonInternal()
+  {
+    InvocationCounterPlugin.resetAllCounters();
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    AbandonOperation abandonOperation =
+         new AbandonOperation(conn, conn.nextOperationID(),
+                  conn.nextMessageID(), new ArrayList<Control>(), 1);
+    abandonOperation.run();
+    assertEquals(abandonOperation.getResultCode(),
+                 ResultCode.NO_SUCH_OPERATION);
+    examineCompletedOperation(abandonOperation);
+
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPostOperationCount() > 0);
+  }
+
+
+
+  /**
+   * Tests performing an abandon operation on a client connection that gets
+   * terminated during pre-parse plugin processing.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(groups = { "slow" })
+  public void testDisconnectInPreParse()
+         throws Exception
+  {
+    InvocationCounterPlugin.resetAllCounters();
+
+    // Establish a connection to the server.  It can be unauthenticated for the
+    // purpose of this test.
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Writer w = new ASN1Writer(s);
+
+
+    // 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(1);
+    LDAPMessage message = new LDAPMessage(2, abandonRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList("PreParse"));
+    w.writeElement(message.encode());
+
+    Thread.sleep(3000);
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPostDisconnectCount() > 0);
+
+    // NOTE:  We can't check to see if pre-parse plugins were called yet
+    //        because there's no plugin ordering.  It's possible that the
+    //        disconnect plugin was called before the invocation counter plugin,
+    //        in which case the pre-parse count wouldn't be incremented.
+  }
+
+
+
+  /**
+   * Tests the use of the abandon operation with a target operation that doesn't
+   * exist.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(groups = { "slow" })
+  public void testNoSuchOperation()
+         throws Exception
+  {
+    InvocationCounterPlugin.resetAllCounters();
+
+    // Establish a connection to the server.  It can be unauthenticated for the
+    // purpose of this test.
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Writer w = new ASN1Writer(s);
+
+
+    // 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(1);
+    w.writeElement(new LDAPMessage(2, abandonRequest).encode());
+
+    Thread.sleep(3000);
+
+    s.close();
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPostOperationCount() > 0);
+  }
+
+
+
+  /**
+   * Tests the ability to abandon an add operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAbandonAdd()
+         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 an add 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<LDAPAttribute> attributes = new ArrayList<LDAPAttribute>();
+
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>(2);
+    values.add(new ASN1OctetString("top"));
+    values.add(new ASN1OctetString("organizationalUnit"));
+    attributes.add(new LDAPAttribute("objectClass", values));
+
+    values = new ArrayList<ASN1OctetString>(1);
+    values.add(new ASN1OctetString("People"));
+    attributes.add(new LDAPAttribute("ou", values));
+
+    AddRequestProtocolOp addRequest =
+         new AddRequestProtocolOp(new ASN1OctetString("ou=People,o=test"),
+                                  attributes);
+    message = new LDAPMessage(2, addRequest,
+                       DelayPreOpPlugin.createDelayLDAPControlList(5000));
+    w.writeElement(message.encode());
+
+
+    // Send the abandon request to the server.
+    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());
+    AddResponseProtocolOp addResponse = message.getAddResponseProtocolOp();
+    assertEquals(addResponse.getResultCode(), LDAPResultCode.CANCELED);
+
+    s.close();
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+  }
+
+
+
+  /**
+   * Tests the ability to abandon a compare operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testAbandonCompare()
+         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 compare 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.
+    CompareRequestProtocolOp compareRequest =
+      new CompareRequestProtocolOp(new ASN1OctetString("o=test"), "o",
+                                   new ASN1OctetString("test"));
+    message = new LDAPMessage(2, compareRequest,
+                       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());
+    CompareResponseProtocolOp compareResponse =
+         message.getCompareResponseProtocolOp();
+    assertEquals(compareResponse.getResultCode(), LDAPResultCode.CANCELED);
+
+    s.close();
+
+    assertTrue(InvocationCounterPlugin.getPostConnectCount() > 0);
+    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
+  }
+
+
+
+  /**
+   * Tests the ability to abandon a delete operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(groups = { "slow" })
+  public void testAbandonDelete()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+    InvocationCounterPlugin.resetAllCounters();
+
+
+    // Add an entry to the server that we can delete.
+    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 delete 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.
+    DeleteRequestProtocolOp deleteRequest =
+         new DeleteRequestProtocolOp(new ASN1OctetString("cn=test,o=test"));
+    message = new LDAPMessage(2, deleteRequest,
+                       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());
+    DeleteResponseProtocolOp deleteResponse =
+         message.getDeleteResponseProtocolOp();
+    assertEquals(deleteResponse.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/DirectoryExceptionTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/DirectoryExceptionTestCase.java
new file mode 100644
index 0000000..6686a62
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/DirectoryExceptionTestCase.java
@@ -0,0 +1,293 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.types.DN;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of generic test cases for the directory exception class.
+ */
+public class DirectoryExceptionTestCase
+       extends CoreTestCase
+{
+  /**
+   * Retrieves the set of result codes that may be used by the server.
+   *
+   * @return  The set of result codes that may be used by the server.
+   */
+  @DataProvider(name = "resultCodes")
+  public Object[][] getResultCodes()
+  {
+    ResultCode[] resultCodes = ResultCode.values();
+    Object[][]   array       = new Object[resultCodes.length][1];
+
+    for (int i=0; i < resultCodes.length; i++)
+    {
+      array[i][0] = resultCodes[i];
+    }
+
+    return array;
+  }
+
+
+
+  /**
+   * Tests the first constructor, which takes ResultCode, String, and int
+   * arguments.
+   *
+   * @param  resultCode  The result code to use for the test.
+   */
+  @Test(dataProvider = "resultCodes")
+  public void testConstructor1(ResultCode resultCode)
+  {
+    String msg = "Test Constructor 1";
+
+    validateException(new DirectoryException(resultCode, msg, 1));
+    validateException(new DirectoryException(resultCode, null, 1));
+    validateException(new DirectoryException(resultCode, msg, -1));
+    validateException(new DirectoryException(resultCode, null, -1));
+  }
+
+
+
+  /**
+   * Tests the second constructor, which takes ResultCode, String, int, and
+   * Throwable arguments.
+   *
+   * @param  resultCode  The result code to use for the test.
+   */
+  @Test(dataProvider = "resultCodes")
+  public void testConstructor2(ResultCode resultCode)
+  {
+    String    msg = "Test Constructor 2";
+    Exception e   = new Exception("Test Constructor 2 Exception");
+
+    validateException(new DirectoryException(resultCode, msg, 1, e));
+    validateException(new DirectoryException(resultCode, null, 1, e));
+    validateException(new DirectoryException(resultCode, msg, -1, e));
+    validateException(new DirectoryException(resultCode, null, -1, e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null));
+    validateException(new DirectoryException(resultCode, null, 1, null));
+    validateException(new DirectoryException(resultCode, msg, -1, null));
+    validateException(new DirectoryException(resultCode, null, -1, null));
+  }
+
+
+
+  /**
+   * Tests the third constructor, which takes ResultCode, String, int, DN, and
+   * Throwable arguments.
+   *
+   * @param  resultCode  The result code to use for the test.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(dataProvider = "resultCodes")
+  public void testConstructor3(ResultCode resultCode)
+         throws Exception
+  {
+    String    msg = "Test Constructor 3";
+    DN        dn  = DN.decode("cn=Test Constructor 3,dc=example,dc=com");
+    Exception e   = new Exception("Test Constructor 3 Exception");
+
+    validateException(new DirectoryException(resultCode, msg, 1, dn, e));
+    validateException(new DirectoryException(resultCode, null, 1, dn, e));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, e));
+    validateException(new DirectoryException(resultCode, null, -1, dn, e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, dn, null));
+    validateException(new DirectoryException(resultCode, null, 1, dn, null));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, null));
+    validateException(new DirectoryException(resultCode, null, -1, dn, null));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, e));
+    validateException(new DirectoryException(resultCode, null, 1, null, e));
+    validateException(new DirectoryException(resultCode, msg, -1, null, e));
+    validateException(new DirectoryException(resultCode, null, -1, null, e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, null));
+    validateException(new DirectoryException(resultCode, null, 1, null, null));
+    validateException(new DirectoryException(resultCode, msg, -1, null, null));
+    validateException(new DirectoryException(resultCode, null, -1, null, null));
+  }
+
+
+
+  /**
+   * Tests the fourth constructor, which takes ResultCode, String, int, DN,
+   * List<String>, and Throwable arguments.
+   *
+   * @param  resultCode  The result code to use for the test.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(dataProvider = "resultCodes")
+  public void testConstructor4(ResultCode resultCode)
+         throws Exception
+  {
+    String    msg     = "Test Constructor 4";
+    DN        dn      = DN.decode("cn=Test Constructor 4,dc=example,dc=com");
+    Exception e       = new Exception("Test Constructor 4 Exception");
+    List<String> refs = new ArrayList<String>();
+    refs.add("ldap://ldap.example.com/cn=Test Constructor 4,dc=example,dc=com");
+
+    validateException(new DirectoryException(resultCode, msg, 1, dn, refs, e));
+    validateException(new DirectoryException(resultCode, null, 1, dn, refs, e));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, refs, e));
+    validateException(new DirectoryException(resultCode, null, -1, dn, refs,
+                                             e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, dn, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, 1, dn, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, -1, dn, refs,
+                                             null));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, refs,
+                                             e));
+    validateException(new DirectoryException(resultCode, null, 1, null, refs,
+                                             e));
+    validateException(new DirectoryException(resultCode, msg, -1, null, refs,
+                                             e));
+    validateException(new DirectoryException(resultCode, null, -1, null, refs,
+                                             e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, 1, null, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, msg, -1, null, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, -1, null, refs,
+                                             null));
+
+    refs.clear();
+    validateException(new DirectoryException(resultCode, msg, 1, dn, refs, e));
+    validateException(new DirectoryException(resultCode, null, 1, dn, refs, e));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, refs, e));
+    validateException(new DirectoryException(resultCode, null, -1, dn, refs,
+                                             e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, dn, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, 1, dn, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, -1, dn, refs,
+                                             null));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, refs,
+                                             e));
+    validateException(new DirectoryException(resultCode, null, 1, null, refs,
+                                             e));
+    validateException(new DirectoryException(resultCode, msg, -1, null, refs,
+                                             e));
+    validateException(new DirectoryException(resultCode, null, -1, null, refs,
+                                             e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, 1, null, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, msg, -1, null, refs,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, -1, null, refs,
+                                             null));
+
+    validateException(new DirectoryException(resultCode, msg, 1, dn, null, e));
+    validateException(new DirectoryException(resultCode, null, 1, dn, null, e));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, null, e));
+    validateException(new DirectoryException(resultCode, null, -1, dn, null,
+                                             e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, dn, null,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, 1, dn, null,
+                                             null));
+    validateException(new DirectoryException(resultCode, msg, -1, dn, null,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, -1, dn, null,
+                                             null));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, null,
+                                             e));
+    validateException(new DirectoryException(resultCode, null, 1, null, null,
+                                             e));
+    validateException(new DirectoryException(resultCode, msg, -1, null, null,
+                                             e));
+    validateException(new DirectoryException(resultCode, null, -1, null, null,
+                                             e));
+
+    validateException(new DirectoryException(resultCode, msg, 1, null, null,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, 1, null, null,
+                                             null));
+    validateException(new DirectoryException(resultCode, msg, -1, null, null,
+                                             null));
+    validateException(new DirectoryException(resultCode, null, -1, null, null,
+                                             null));
+  }
+
+
+
+  /**
+   * Verifies that everything is OK with the provided exception (i.e., that it
+   * is possible to get the result code, error message, message ID, matched DN,
+   * and referrals.
+   *
+   * @param  de  The directory exception to be validated.
+   */
+  private void validateException(DirectoryException de)
+  {
+    assertNotNull(de.getResultCode());
+
+    de.getErrorMessage();
+    de.getErrorMessageID();
+    de.getMatchedDN();
+    de.getReferralURLs();
+  }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/InitializationExceptionTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/InitializationExceptionTestCase.java
new file mode 100644
index 0000000..8dc4b1e
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/InitializationExceptionTestCase.java
@@ -0,0 +1,130 @@
+/*
+ * 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 org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of generic test cases for the initialization exception class.
+ */
+public class InitializationExceptionTestCase
+       extends CoreTestCase
+{
+  /**
+   * Tests the first constructor, which takes int and String arguments.
+   */
+  @Test()
+  public void testConstructor1()
+  {
+    String message = "Test Constructor 1";
+
+    validateException(new InitializationException(1, message), 1, message);
+    validateException(new InitializationException(1, ""), 1, "");
+    validateException(new InitializationException(1, null), 1, null);
+
+    validateException(new InitializationException(0, message), 0, message);
+    validateException(new InitializationException(0, ""), 0, "");
+    validateException(new InitializationException(0, null), 0, null);
+
+    validateException(new InitializationException(-1, message), -1, message);
+    validateException(new InitializationException(-1, ""), -1, "");
+    validateException(new InitializationException(-1, null), -1, null);
+  }
+
+
+
+  /**
+   * Tests the second constructor, which takes int, String, and Throwable
+   * arguments.
+   */
+  @Test()
+  public void testConstructor2()
+  {
+    String    message = "Test Constructor 2";
+    Exception e       = new Exception("Test Constructor 2 Exception");
+
+    validateException(new InitializationException(1, message, e), 1, message);
+    validateException(new InitializationException(1, "", e), 1, "");
+    validateException(new InitializationException(1, null, e), 1, null);
+
+    validateException(new InitializationException(0, message, e), 0, message);
+    validateException(new InitializationException(0, "", e), 0, "");
+    validateException(new InitializationException(0, null, e), 0, null);
+
+    validateException(new InitializationException(-1, message, e), -1, message);
+    validateException(new InitializationException(-1, "", e), -1, "");
+    validateException(new InitializationException(-1, null, e), -1, null);
+
+    validateException(new InitializationException(1, message, null), 1,
+                      message);
+    validateException(new InitializationException(1, "", null), 1, "");
+    validateException(new InitializationException(1, null, null), 1, null);
+
+    validateException(new InitializationException(0, message, null), 0,
+                      message);
+    validateException(new InitializationException(0, "", null), 0, "");
+    validateException(new InitializationException(0, null, null), 0, null);
+
+    validateException(new InitializationException(-1, message, null), -1,
+                      message);
+    validateException(new InitializationException(-1, "", null), -1, "");
+    validateException(new InitializationException(-1, null, null), -1, null);
+  }
+
+
+
+  /**
+   * Verifies the contents of the provided initialization exception.
+   *
+   * @param  ie         The initialization exception to verify.
+   * @param  messageID  The expected message ID for the exception.
+   * @param  message    The expected message for the exception.
+   */
+  private void validateException(InitializationException ie, int messageID,
+                                 String message)
+  {
+    assertEquals(ie.getMessageID(), messageID);
+
+    if (message == null)
+    {
+      assertNull(ie.getMessage());
+    }
+    else
+    {
+      assertEquals(ie.getMessage(), message);
+    }
+  }
+}
+
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
new file mode 100644
index 0000000..46b970b
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/OperationTestCase.java
@@ -0,0 +1,370 @@
+/*
+ * 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 org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.types.ResultCode;
+
+import static org.testng.Assert.*;
+
+
+
+/**
+ * A set of generic test cases for operations
+ */
+public abstract class OperationTestCase
+       extends CoreTestCase
+{
+  /**
+   * 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.
+   */
+  public abstract Operation[] createTestOperations()
+         throws Exception;
+
+
+
+  /**
+   * Retrieves a set of valid operation instances that may be used to test
+   * methods common to all operations.
+   *
+   * @return  A set of valid operation instances that may be used to test
+   *          methods common to all operations.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @DataProvider(name = "testOperations")
+  public Object[][] getTestOperations()
+         throws Exception
+  {
+    Operation[] operationArray = createTestOperations();
+    Object[][]  objectArray    = new Object[operationArray.length][1];
+
+    for (int i=0; i < operationArray.length; i++)
+    {
+      objectArray[i][0] = operationArray[i];
+    }
+
+    return objectArray;
+  }
+
+
+
+  /**
+   * Tests the <CODE>getOperationType</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetOperationType(Operation operation)
+  {
+    assertNotNull(operation.getOperationType());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getCommonLogElements</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetCommonLogElements(Operation operation)
+  {
+    assertNotNull(operation.getCommonLogElements());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getRequestLogElements</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetRequestLogElements(Operation operation)
+  {
+    assertNotNull(operation.getRequestLogElements());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getClientConnection</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetClientConnection(Operation operation)
+  {
+    assertNotNull(operation.getClientConnection());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getConnectionID</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetConnectionID(Operation operation)
+  {
+    operation.getConnectionID();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getOperationID</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetOperationID(Operation operation)
+  {
+    operation.getOperationID();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getMessageID</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetMessageID(Operation operation)
+  {
+    operation.getMessageID();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getRequestControls</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetRequestControls(Operation operation)
+  {
+    operation.getRequestControls();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getResponseControls</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetResponseControls(Operation operation)
+  {
+    operation.getResponseControls();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getResultCode</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetResultCode(Operation operation)
+  {
+    assertNotNull(operation.getResultCode());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getErrorMessage</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetErrorMessage(Operation operation)
+  {
+    assertNotNull(operation.getErrorMessage());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getAdditionalLogMessage</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetAdditionalLogMessage(Operation operation)
+  {
+    assertNotNull(operation.getAdditionalLogMessage());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getMatchedDN</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetMatchedDN(Operation operation)
+  {
+    operation.getMatchedDN();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getReferralURLs</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetReferralURLs(Operation operation)
+  {
+    operation.getReferralURLs();
+  }
+
+
+
+  /**
+   * Tests the <CODE>isInternalOperation</CODE> method for the provided \
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testIsInternalOperation(Operation operation)
+  {
+    operation.isInternalOperation();
+  }
+
+
+
+  /**
+   * Tests the <CODE>isSynchronizationOperation</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testIsSynchronizationOperation(Operation operation)
+  {
+    operation.isSynchronizationOperation();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getAuthorizationDN</CODE> method for the provided
+   * operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetAuthorizationDN(Operation operation)
+  {
+    assertNotNull(operation.getAuthorizationDN());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getMatchedDN</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetAttachments(Operation operation)
+  {
+    assertNotNull(operation.getAttachments());
+  }
+
+
+
+  /**
+   * Tests the <CODE>getCancelRequest</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetCancelRequest(Operation operation)
+  {
+    operation.getCancelRequest();
+  }
+
+
+
+  /**
+   * Tests the <CODE>getCancelResult</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testGetCancelResult(Operation operation)
+  {
+    operation.getCancelResult();
+  }
+
+
+
+  /**
+   * Tests the <CODE>toString</CODE> method for the provided operation.
+   *
+   * @param  operation  The operation to test.
+   */
+  @Test(dataProvider = "testOperations")
+  public void testToString(Operation operation)
+  {
+    assertNotNull(operation.toString());
+  }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/DisconnectClientPlugin.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/DisconnectClientPlugin.java
new file mode 100644
index 0000000..bea935b
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/DisconnectClientPlugin.java
@@ -0,0 +1,941 @@
+/*
+ * 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.PostOperationPluginResult;
+import org.opends.server.api.plugin.PostResponsePluginResult;
+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.ASN1OctetString;
+import org.opends.server.protocols.ldap.LDAPControl;
+import org.opends.server.types.Control;
+import org.opends.server.types.DisconnectReason;
+
+
+
+/**
+ * This class defines a very simple plugin that terminates the client connection
+ * at any point in the plugin processing if the client request contains an
+ * appropriate control with a value string that matches the plugin type.  The
+ * valid sections for this plugin include:
+ * <BR>
+ * <UL>
+ *   <LI>PreParse -- available for all types of operations</LI>
+ *   <LI>PreOperation -- available for all types of operations except abandon
+ *       and unbind</LI>
+ *   <LI>PostOperation -- available for all types of operations</LI>
+ *   <LI>PostResponse -- available for all types of operations except abandon
+ *       and unbind</LI>
+ * </UL>
+ */
+public class DisconnectClientPlugin
+       extends DirectoryServerPlugin
+{
+  /**
+   * The OID for the disconnect request control, which is used to flag
+   * operations that should cause the client connection to be terminated.
+   */
+  public static final String OID_DISCONNECT_REQUEST =
+       "1.3.6.1.4.1.26027.1.999.2";
+
+
+
+  /**
+   * 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 DisconnectClientPlugin()
+  {
+    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, pre-operation,
+    // post-operation, or post-response 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:
+        case POST_OPERATION_ABANDON:
+        case POST_OPERATION_ADD:
+        case POST_OPERATION_BIND:
+        case POST_OPERATION_COMPARE:
+        case POST_OPERATION_DELETE:
+        case POST_OPERATION_EXTENDED:
+        case POST_OPERATION_MODIFY:
+        case POST_OPERATION_MODIFY_DN:
+        case POST_OPERATION_SEARCH:
+        case POST_OPERATION_UNBIND:
+        case POST_RESPONSE_ADD:
+        case POST_RESPONSE_BIND:
+        case POST_RESPONSE_COMPARE:
+        case POST_RESPONSE_DELETE:
+        case POST_RESPONSE_EXTENDED:
+        case POST_RESPONSE_MODIFY:
+        case POST_RESPONSE_MODIFY_DN:
+        case POST_RESPONSE_SEARCH:
+          // This is fine.
+          break;
+        default:
+          throw new ConfigException(-1, "Invalid plugin type " + t +
+                                    " for the disconnect plugin.");
+      }
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(AbandonOperation abandonOperation)
+  {
+    if (disconnectInternal(abandonOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(AddOperation addOperation)
+  {
+    if (disconnectInternal(addOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(BindOperation bindOperation)
+  {
+    if (disconnectInternal(bindOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(CompareOperation compareOperation)
+  {
+    if (disconnectInternal(compareOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(DeleteOperation deleteOperation)
+  {
+    if (disconnectInternal(deleteOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ExtendedOperation extendedOperation)
+  {
+    if (disconnectInternal(extendedOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ModifyOperation modifyOperation)
+  {
+    if (disconnectInternal(modifyOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ModifyDNOperation modifyDNOperation)
+  {
+    if (disconnectInternal(modifyDNOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(SearchOperation searchOperation)
+  {
+    if (disconnectInternal(searchOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(UnbindOperation unbindOperation)
+  {
+    if (disconnectInternal(unbindOperation, "PreParse"))
+    {
+      return new PreParsePluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreParsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(AddOperation addOperation)
+  {
+    if (disconnectInternal(addOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(BindOperation bindOperation)
+  {
+    if (disconnectInternal(bindOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(CompareOperation
+                                                      compareOperation)
+  {
+    if (disconnectInternal(compareOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(DeleteOperation
+                                                      deleteOperation)
+  {
+    if (disconnectInternal(deleteOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ExtendedOperation
+                                                      extendedOperation)
+  {
+    if (disconnectInternal(extendedOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ModifyOperation
+                                                      modifyOperation)
+  {
+    if (disconnectInternal(modifyOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ModifyDNOperation
+                                                      modifyDNOperation)
+  {
+    if (disconnectInternal(modifyDNOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(SearchOperation
+                                                      searchOperation)
+  {
+    if (disconnectInternal(searchOperation, "PreOperation"))
+    {
+      return new PreOperationPluginResult(true, false, false);
+    }
+    else
+    {
+      return new PreOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(AbandonOperation
+                                                        abandonOperation)
+  {
+    if (disconnectInternal(abandonOperation, "PreOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(AddOperation addOperation)
+  {
+    if (disconnectInternal(addOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(BindOperation bindOperation)
+  {
+    if (disconnectInternal(bindOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(CompareOperation
+                                                        compareOperation)
+  {
+    if (disconnectInternal(compareOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(DeleteOperation
+                                                        deleteOperation)
+  {
+    if (disconnectInternal(deleteOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(ExtendedOperation
+                                                        extendedOperation)
+  {
+    if (disconnectInternal(extendedOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(ModifyOperation
+                                                        modifyOperation)
+  {
+    if (disconnectInternal(modifyOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(ModifyDNOperation
+                                                        modifyDNOperation)
+  {
+    if (disconnectInternal(modifyDNOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(SearchOperation
+                                                        searchOperation)
+  {
+    if (disconnectInternal(searchOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(UnbindOperation
+                                                        unbindOperation)
+  {
+    if (disconnectInternal(unbindOperation, "PostOperation"))
+    {
+      return new PostOperationPluginResult(true, false);
+    }
+    else
+    {
+      return new PostOperationPluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(AddOperation addOperation)
+  {
+    if (disconnectInternal(addOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(BindOperation bindOperation)
+  {
+    if (disconnectInternal(bindOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(CompareOperation
+                                                        compareOperation)
+  {
+    if (disconnectInternal(compareOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(DeleteOperation
+                                                        deleteOperation)
+  {
+    if (disconnectInternal(deleteOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(ExtendedOperation
+                                                        extendedOperation)
+  {
+    if (disconnectInternal(extendedOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(ModifyOperation
+                                                        modifyOperation)
+  {
+    if (disconnectInternal(modifyOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(ModifyDNOperation
+                                                        modifyDNOperation)
+  {
+    if (disconnectInternal(modifyDNOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(SearchOperation
+                                                        searchOperation)
+  {
+    if (disconnectInternal(searchOperation, "PostResponse"))
+    {
+      return new PostResponsePluginResult(true, false);
+    }
+    else
+    {
+      return new PostResponsePluginResult();
+    }
+  }
+
+
+
+  /**
+   * Looks for a disconnect request control in the operation and if one is found
+   * with the correct section then terminate the client connection.
+   *
+   * @param  operation  The operation to be processed.
+   * @param  section    The section to match in the control value.
+   *
+   * @return  <CODE>true</CODE> if the client connection was terminated, or
+   *          <CODE>false</CODE> if it was not.
+   */
+  private boolean disconnectInternal(Operation operation, String section)
+  {
+    List<Control> requestControls = operation.getRequestControls();
+    if (requestControls != null)
+    {
+      for (Control c : requestControls)
+      {
+        if (c.getOID().equals(OID_DISCONNECT_REQUEST))
+        {
+          if (c.getValue().stringValue().equalsIgnoreCase(section))
+          {
+            operation.getClientConnection().disconnect(
+                 DisconnectReason.CLOSED_BY_PLUGIN, true,
+                 "Closed by disconnect client plugin (section " + section + ")",
+                 -1);
+
+            return true;
+          }
+        }
+      }
+    }
+
+
+    // If we've gotten here, then we shouldn't disconnect the client.
+    return false;
+  }
+
+
+
+  /**
+   * Creates a disconnect request control with the specified section.
+   *
+   * @param  section  The section to use for the disconnect.
+   *
+   * @return  The appropriate disconnect request control.
+   */
+  public static Control createDisconnectControl(String section)
+  {
+    return new Control(OID_DISCONNECT_REQUEST, false,
+                       new ASN1OctetString(section));
+  }
+
+
+
+  /**
+   * Retrieves a list containing a disconnect control with the specified
+   * section.
+   *
+   * @param  section  The section to use for the disconnect.
+   *
+   * @return  A list containing the appropriate disconnect request control.
+   */
+  public static List<Control> createDisconnectControlList(String section)
+  {
+    ArrayList<Control> controlList = new ArrayList<Control>(1);
+
+    controlList.add(new Control(OID_DISCONNECT_REQUEST, false,
+                                new ASN1OctetString(section)));
+
+    return controlList;
+  }
+
+
+
+  /**
+   * Creates a disconnect request LDAP control with the specified section.
+   *
+   * @param  section  The section to use for the disconnect.
+   *
+   * @return  The appropriate disconnect request LDAP control.
+   */
+  public static LDAPControl createDisconnectLDAPControl(String section)
+  {
+    return new LDAPControl(OID_DISCONNECT_REQUEST, false,
+                           new ASN1OctetString(section));
+  }
+
+
+
+  /**
+   * Retrieves a list containing a disconnect request LDAP control with the
+   * specified section.
+   *
+   * @param  section  The section to use for the disconnect.
+   *
+   * @return  A list containing the appropriate disconnect request LDAP control.
+   */
+  public static ArrayList<LDAPControl> createDisconnectLDAPControlList(
+                                            String section)
+  {
+    ArrayList<LDAPControl> controlList = new ArrayList<LDAPControl>(1);
+
+    controlList.add(new LDAPControl(OID_DISCONNECT_REQUEST, false,
+                                    new ASN1OctetString(section)));
+
+    return controlList;
+  }
+}
+
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/InvocationCounterPlugin.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/InvocationCounterPlugin.java
new file mode 100644
index 0000000..800e9e7
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/plugins/InvocationCounterPlugin.java
@@ -0,0 +1,1067 @@
+/*
+ * 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 java.util.concurrent.atomic.AtomicInteger;
+
+import org.opends.server.api.ClientConnection;
+import org.opends.server.api.plugin.DirectoryServerPlugin;
+import org.opends.server.api.plugin.PluginType;
+import org.opends.server.api.plugin.IntermediateResponsePluginResult;
+import org.opends.server.api.plugin.LDIFPluginResult;
+import org.opends.server.api.plugin.PostConnectPluginResult;
+import org.opends.server.api.plugin.PostDisconnectPluginResult;
+import org.opends.server.api.plugin.PostOperationPluginResult;
+import org.opends.server.api.plugin.PostResponsePluginResult;
+import org.opends.server.api.plugin.PreOperationPluginResult;
+import org.opends.server.api.plugin.PreParsePluginResult;
+import org.opends.server.api.plugin.SearchEntryPluginResult;
+import org.opends.server.api.plugin.SearchReferencePluginResult;
+import org.opends.server.api.plugin.StartupPluginResult;
+import org.opends.server.config.ConfigEntry;
+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.types.DisconnectReason;
+import org.opends.server.types.Entry;
+import org.opends.server.types.IntermediateResponse;
+import org.opends.server.types.LDIFExportConfig;
+import org.opends.server.types.LDIFImportConfig;
+import org.opends.server.types.SearchResultEntry;
+import org.opends.server.types.SearchResultReference;
+
+
+
+/**
+ * This class defines a very simple plugin that simply increments a counter each
+ * time a plugin is called.  There will be separate counters for each basic
+ * type of plugin, including:
+ * <BR>
+ * <UL>
+ *   <LI>pre-parse</LI>
+ *   <LI>pre-operation</LI>
+ *   <LI>post-operation</LI>
+ *   <LI>post-response</LI>
+ *   <LI>search result entry</LI>
+ *   <LI>search result reference</LI>
+ *   <LI>intermediate response</LI>
+ *   <LI>post-connect</LI>
+ *   <LI>post-disconnect</LI>
+ *   <LI>LDIF import</LI>
+ *   <LI>LDIF export</LI>
+ *   <LI>startup</LI>
+ *   <LI>shutdown</LI>
+ * </UL>
+ */
+public class InvocationCounterPlugin
+       extends DirectoryServerPlugin
+{
+  // Define the counters that will be used to keep track of everything.
+  private static AtomicInteger preParseCounter        = new AtomicInteger(0);
+  private static AtomicInteger preOperationCounter    = new AtomicInteger(0);
+  private static AtomicInteger postOperationCounter   = new AtomicInteger(0);
+  private static AtomicInteger postResponseCounter    = new AtomicInteger(0);
+  private static AtomicInteger searchEntryCounter     = new AtomicInteger(0);
+  private static AtomicInteger searchReferenceCounter = new AtomicInteger(0);
+  private static AtomicInteger intermediateResponseCounter =
+                                    new AtomicInteger(0);
+  private static AtomicInteger postConnectCounter     = new AtomicInteger(0);
+  private static AtomicInteger postDisconnectCounter  = new AtomicInteger(0);
+  private static AtomicInteger ldifImportCounter      = new AtomicInteger(0);
+  private static AtomicInteger ldifExportCounter      = new AtomicInteger(0);
+  private static boolean       startupCalled          = false;
+  private static boolean       shutdownCalled         = false;
+
+
+
+  /**
+   * 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 InvocationCounterPlugin()
+  {
+    super();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public void initializePlugin(DirectoryServer directoryServer,
+                               Set<PluginType> pluginTypes,
+                               ConfigEntry configEntry)
+  {
+    // No implementation required.
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(AbandonOperation abandonOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(AddOperation addOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(BindOperation bindOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(CompareOperation compareOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(DeleteOperation deleteOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ExtendedOperation extendedOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ModifyOperation modifyOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(ModifyDNOperation modifyDNOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(SearchOperation searchOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreParsePluginResult doPreParse(UnbindOperation unbindOperation)
+  {
+    preParseCounter.incrementAndGet();
+    return new PreParsePluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the pre-parse plugins have been called
+   * since the last reset.
+   *
+   * @return  The number of times that the pre-parse plugins have been called
+   *          since the last reset.
+   */
+  public static int getPreParseCount()
+  {
+    return preParseCounter.get();
+  }
+
+
+
+  /**
+   * Resets the pre-parse plugin invocation count to zero.
+   *
+   * @return  The pre-parse plugin invocation count before it was reset.
+   */
+  public static int resetPreParseCount()
+  {
+    return preParseCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(AddOperation addOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(BindOperation bindOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(CompareOperation
+                                                      compareOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(DeleteOperation
+                                                      deleteOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ExtendedOperation
+                                                      extendedOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ModifyOperation
+                                                      modifyOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(ModifyDNOperation
+                                                      modifyDNOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PreOperationPluginResult doPreOperation(SearchOperation
+                                                      searchOperation)
+  {
+    preOperationCounter.incrementAndGet();
+    return new PreOperationPluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the pre-operation plugins have been
+   * called since the last reset.
+   *
+   * @return  The number of times that the pre-operation plugins have been
+   *          called since the last reset.
+   */
+  public static int getPreOperationCount()
+  {
+    return preOperationCounter.get();
+  }
+
+
+
+  /**
+   * Resets the pre-operation plugin invocation count to zero.
+   *
+   * @return  The pre-operation plugin invocation count before it was reset.
+   */
+  public static int resetPreOperationCount()
+  {
+    return preOperationCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(AbandonOperation
+                                                        abandonOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(AddOperation addOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(BindOperation bindOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(CompareOperation
+                                                        compareOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(DeleteOperation
+                                                        deleteOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(ExtendedOperation
+                                                        extendedOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(ModifyOperation
+                                                        modifyOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(ModifyDNOperation
+                                                        modifyDNOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(SearchOperation
+                                                        searchOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostOperationPluginResult doPostOperation(UnbindOperation
+                                                        unbindOperation)
+  {
+    postOperationCounter.incrementAndGet();
+    return new PostOperationPluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the post-operation plugins have been
+   * called since the last reset.
+   *
+   * @return  The number of times that the post-operation plugins have been
+   *          called since the last reset.
+   */
+  public static int getPostOperationCount()
+  {
+    return postOperationCounter.get();
+  }
+
+
+
+  /**
+   * Resets the post-operation plugin invocation count to zero.
+   *
+   * @return  The post-operation plugin invocation count before it was reset.
+   */
+  public static int resetPostOperationCount()
+  {
+    return postOperationCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(AddOperation addOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(BindOperation bindOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(CompareOperation
+                                                        compareOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(DeleteOperation
+                                                        deleteOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(ExtendedOperation
+                                                        extendedOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(ModifyOperation
+                                                        modifyOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(ModifyDNOperation
+                                                        modifyDNOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostResponsePluginResult doPostResponse(SearchOperation
+                                                        searchOperation)
+  {
+    postResponseCounter.incrementAndGet();
+    return new PostResponsePluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the post-response plugins have been
+   * called since the last reset.
+   *
+   * @return  The number of times that the post-response plugins have been
+   *          called since the last reset.
+   */
+  public static int getPostResponseCount()
+  {
+    return postResponseCounter.get();
+  }
+
+
+
+  /**
+   * Resets the post-response plugin invocation count to zero.
+   *
+   * @return  The post-response plugin invocation count before it was reset.
+   */
+  public static int resetPostResponseCount()
+  {
+    return postResponseCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public SearchEntryPluginResult processSearchEntry(
+                                      SearchOperation searchOperation,
+                                      SearchResultEntry searchEntry)
+  {
+    searchEntryCounter.incrementAndGet();
+    return new SearchEntryPluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the search result entry plugins have
+   * been called since the last reset.
+   *
+   * @return  The number of times that the search result entry plugins have been
+   *          called since the last reset.
+   */
+  public static int getSearchEntryCount()
+  {
+    return searchEntryCounter.get();
+  }
+
+
+
+  /**
+   * Resets the search result entry plugin invocation count to zero.
+   *
+   * @return  The search result entry plugin invocation count before it was
+   *          reset.
+   */
+  public static int resetSearchEntryCount()
+  {
+    return searchEntryCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public SearchReferencePluginResult processSearchReference(
+                                          SearchOperation searchOperation,
+                                          SearchResultReference searchReference)
+  {
+    searchReferenceCounter.incrementAndGet();
+    return new SearchReferencePluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the search result reference plugins have
+   * been called since the last reset.
+   *
+   * @return  The number of times that the search result reference plugins have
+   *          been called since the last reset.
+   */
+  public static int getSearchReferenceCount()
+  {
+    return searchReferenceCounter.get();
+  }
+
+
+
+  /**
+   * Resets the search result reference plugin invocation count to zero.
+   *
+   * @return  The search result reference plugin invocation count before it was
+   *          reset.
+   */
+  public static int resetSearchReferenceCount()
+  {
+    return searchReferenceCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public IntermediateResponsePluginResult processIntermediateResponse(
+              IntermediateResponse intermediateResponse)
+  {
+    intermediateResponseCounter.incrementAndGet();
+    return new IntermediateResponsePluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times the intermediate response plugins have been
+   * called since the last reset.
+   *
+   * @return  The number of times the intermediate response plugins have been
+   *          called since the last reset.
+   */
+  public static int getIntermediateResponseCount()
+  {
+    return intermediateResponseCounter.get();
+  }
+
+
+
+  /**
+   * Resets the intermediate response plugin invocation count to zero.
+   *
+   * @return  The intermediate response plugin invocation count before it was
+   *          reset.
+   */
+  public static int resetIntermediateResponseCount()
+  {
+    return intermediateResponseCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostConnectPluginResult doPostConnect(ClientConnection
+                                                    clientConnection)
+  {
+    postConnectCounter.incrementAndGet();
+    return new PostConnectPluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the post-connect plugins have been
+   * called since the last reset.
+   *
+   * @return  The number of times that the post-connect plugins have been called
+   *          since the last reset.
+   */
+  public static int getPostConnectCount()
+  {
+    return postConnectCounter.get();
+  }
+
+
+
+  /**
+   * Resets the post-connect plugin invocation count to zero.
+   *
+   * @return  The post-connect plugin invocation count before it was reset.
+   */
+  public static int resetPostConnectCount()
+  {
+    return postConnectCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public PostDisconnectPluginResult doPostDisconnect(
+       ClientConnection clientConnection, DisconnectReason disconnectReason,
+       int msgID, String message)
+  {
+    postDisconnectCounter.incrementAndGet();
+    return new PostDisconnectPluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the post-disconnect plugins have been
+   * called since the last reset.
+   *
+   * @return  The number of times that the post-disconnect plugins have been
+   *          called since the last reset.
+   */
+  public static int getPostDisconnectCount()
+  {
+    return postDisconnectCounter.get();
+  }
+
+
+
+  /**
+   * Resets the post-disconnect plugin invocation count to zero.
+   *
+   * @return  The post-disconnect plugin invocation count before it was reset.
+   */
+  public static int resetPostDisconnectCount()
+  {
+    return postDisconnectCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public LDIFPluginResult doLDIFImport(LDIFImportConfig importConfig,
+                                       Entry entry)
+  {
+    ldifImportCounter.incrementAndGet();
+    return new LDIFPluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the LDIF import plugins have been called
+   * since the last reset.
+   *
+   * @return  The number of times that the LDIF import plugins have been called
+   *          since the last reset.
+   */
+  public static int getLDIFImportCount()
+  {
+    return ldifImportCounter.get();
+  }
+
+
+
+  /**
+   * Resets the LDIF import plugin invocation count to zero.
+   *
+   * @return  The LDIF import plugin invocation count before it was reset.
+   */
+  public static int resetLDIFImportCount()
+  {
+    return ldifImportCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public LDIFPluginResult doLDIFExport(LDIFExportConfig exportConfig,
+                                       Entry entry)
+  {
+    ldifExportCounter.incrementAndGet();
+    return new LDIFPluginResult();
+  }
+
+
+
+  /**
+   * Retrieves the number of times that the LDIF export plugins have been called
+   * since the last reset.
+   *
+   * @return  The number of times that the LDIF export plugins have been called
+   *          since the last reset.
+   */
+  public static int getLDIFExportCount()
+  {
+    return ldifExportCounter.get();
+  }
+
+
+
+  /**
+   * Resets the LDIF export plugin invocation count to zero.
+   *
+   * @return  The LDIF export plugin invocation count before it was reset.
+   */
+  public static int resetLDIFExportCount()
+  {
+    return ldifExportCounter.getAndSet(0);
+  }
+
+
+
+  /**
+   * Resets all of the invocation counters.  This does not impact the startup
+   * or shutdown flag.
+   */
+  public static void resetAllCounters()
+  {
+    resetPreParseCount();
+    resetPreOperationCount();
+    resetPostOperationCount();
+    resetPostResponseCount();
+    resetSearchEntryCount();
+    resetSearchReferenceCount();
+    resetIntermediateResponseCount();
+    resetPostConnectCount();
+    resetPostDisconnectCount();
+    resetLDIFImportCount();
+    resetLDIFExportCount();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public StartupPluginResult doStartup()
+  {
+    startupCalled = true;
+    return new StartupPluginResult();
+  }
+
+
+
+  /**
+   * Indicates whether the server startup plugins have been called.
+   *
+   * @return  <CODE>true</CODE> if the server startup plugins have been called,
+   *          or <CODE>false</CODE> if not.
+   */
+  public static boolean startupCalled()
+  {
+    return startupCalled;
+  }
+
+
+
+  /**
+   * Resets the flag that indicates whether the startup plugins have been
+   * called.
+   */
+  public static void resetStartupCalled()
+  {
+    startupCalled = false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
+  public void doShutdown()
+  {
+    shutdownCalled = true;
+  }
+
+
+
+  /**
+   * Indicates whether the server shutdown plugins have been called.
+   *
+   * @return  <CODE>true</CODE> if the server shutdown plugins have been called,
+   *          or <CODE>false</CODE> if not.
+   */
+  public static boolean shutdownCalled()
+  {
+    return shutdownCalled;
+  }
+
+
+
+  /**
+   * Resets the flag that indicates whether the shutdown plugins have been
+   * called.
+   */
+  public static void resetShutdownCalled()
+  {
+    shutdownCalled = false;
+  }
+}
+

--
Gitblit v1.10.0