From 8be8fb738c2abe5b5e85c96f3dd6d29a3f9784fb Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Fri, 13 Oct 2006 02:58:54 +0000
Subject: [PATCH] Add additional test cases for add, delete, and modify operations.

---
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java |  122 +++++++
 opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java                                 |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/AbandonOperation.java                                |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java                                   |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/Operation.java                                       |   16 
 opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java                                 |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java                                    |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/CompareOperation.java                                |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/ExtendedOperation.java                               |   15 
 opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java                               |   15 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java |  340 ++++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/core/UnbindOperation.java                                 |   15 
 opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AddOperationTestCase.java    |  358 +++++++++++++++++++++
 opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java                                 |   15 
 14 files changed, 986 insertions(+), 0 deletions(-)

diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/AbandonOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/AbandonOperation.java
index 0471a6f..8e6aff3 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/AbandonOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/AbandonOperation.java
@@ -417,6 +417,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    // Abandon operations cannot be canceled.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
index fd3807b..a3b2cad 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/AddOperation.java
@@ -2466,6 +2466,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    this.cancelRequest = cancelRequest;
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java
index d964244..807c88b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/BindOperation.java
@@ -2299,6 +2299,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    // Bind operations cannot be canceled.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/CompareOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/CompareOperation.java
index 287b24f..ab312d8 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/CompareOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/CompareOperation.java
@@ -1153,6 +1153,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    this.cancelRequest = cancelRequest;
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java
index 8c93089..230ef40 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/DeleteOperation.java
@@ -1318,6 +1318,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    this.cancelRequest = cancelRequest;
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/ExtendedOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/ExtendedOperation.java
index 458ceb9..77eb4f7 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/ExtendedOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/ExtendedOperation.java
@@ -771,6 +771,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    this.cancelRequest = cancelRequest;
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java
index caf5af8..7c275e4 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyDNOperation.java
@@ -2116,6 +2116,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    this.cancelRequest = cancelRequest;
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
index 1956a1b..7c5669c 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -2808,6 +2808,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    this.cancelRequest = cancelRequest;
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/Operation.java b/opendj-sdk/opends/src/server/org/opends/server/core/Operation.java
index fc49f53..0d85d66 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/Operation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/Operation.java
@@ -928,6 +928,22 @@
 
 
   /**
+   * Sets the cancel request for this operation, if applicable.  This should
+   * only be used for testing purposes (e.g., for ensuring a cancel request is
+   * submitted before processing begins on an operation, or to allow for
+   * cancelling an internal operation).
+   *
+   * @param  cancelRequest  The cancel request to set for this operation.
+   *
+   * @return  <CODE>true</CODE> if the cancel request was set, or
+   *          <CODE>false</CODE> if it was not for some reason (e.g., the
+   *          specified operation cannot be cancelled).
+   */
+  abstract boolean setCancelRequest(CancelRequest cancelRequest);
+
+
+
+  /**
    * Retrieves the cancel request that has been issued for this operation, if
    * there is one.  This method should not be called by post-operation or
    * post-response plugins.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
index d0feac0..5fdaccf 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/SearchOperation.java
@@ -2179,6 +2179,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    this.cancelRequest = cancelRequest;
+    return true;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/src/server/org/opends/server/core/UnbindOperation.java b/opendj-sdk/opends/src/server/org/opends/server/core/UnbindOperation.java
index 593995f..9a2f096 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/core/UnbindOperation.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/core/UnbindOperation.java
@@ -306,6 +306,21 @@
    * {@inheritDoc}
    */
   @Override()
+  boolean setCancelRequest(CancelRequest cancelRequest)
+  {
+    assert debugEnter(CLASS_NAME, "setCancelRequest",
+                      String.valueOf(cancelRequest));
+
+    // Unbind operations cannot be canceled.
+    return false;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override()
   public final void toString(StringBuilder buffer)
   {
     assert debugEnter(CLASS_NAME, "toString", "java.lang.StringBuilder");
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AddOperationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AddOperationTestCase.java
index 415d0ab..4f0e9ca 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AddOperationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/AddOperationTestCase.java
@@ -31,13 +31,16 @@
 import java.net.Socket;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.locks.Lock;
 
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import org.opends.server.TestCaseUtils;
 import org.opends.server.api.Backend;
+import org.opends.server.plugins.DisconnectClientPlugin;
 import org.opends.server.plugins.UpdatePreOpPlugin;
+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;
@@ -53,15 +56,18 @@
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteString;
+import org.opends.server.types.CancelRequest;
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
+import org.opends.server.types.LockManager;
 import org.opends.server.types.ObjectClass;
 import org.opends.server.types.ResultCode;
 import org.opends.server.types.WritabilityMode;
 
 import static org.testng.Assert.*;
 
+import static org.opends.server.protocols.ldap.LDAPConstants.*;
 import static org.opends.server.util.ServerConstants.*;
 
 
@@ -1885,5 +1891,357 @@
     assertEquals(changeListener.getAddCount(), 0);
     DirectoryServer.deregisterChangeNotificationListener(changeListener);
   }
+
+
+
+  /**
+   * Tests an add operation that gets canceled before startup.
+   *
+   * @throws  Exception  If an unexpected probem occurs.
+   */
+  @Test()
+  public void testCancelBeforeStartup()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Entry entry = TestCaseUtils.makeEntry(
+         "dn: ou=People,o=test",
+         "objectClass: top",
+         "objectClass: organizationalUnit",
+         "ou: People");
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    AddOperation addOperation =
+         new AddOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                          null, entry.getDN(), entry.getObjectClasses(),
+                          entry.getUserAttributes(),
+                          entry.getOperationalAttributes());
+
+    CancelRequest cancelRequest = new CancelRequest(false,
+                                                    "testCancelBeforeStartup");
+    addOperation.setCancelRequest(cancelRequest);
+    addOperation.run();
+    assertEquals(addOperation.getResultCode(), ResultCode.CANCELED);
+  }
+
+
+
+  /**
+   * Tests an add operation in which the server cannot obtain a lock on the
+   * target entry because there is already a read lock held on it.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(groups = { "slow" })
+  public void testCannotLockEntry()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Lock entryLock = LockManager.lockRead(DN.decode("ou=People,o=test"));
+
+    try
+    {
+      Entry entry = TestCaseUtils.makeEntry(
+           "dn: ou=People,o=test",
+           "objectClass: top",
+           "objectClass: organizationalUnit",
+           "ou: People");
+
+      InternalClientConnection conn =
+           InternalClientConnection.getRootConnection();
+
+      AddOperation addOperation =
+           conn.processAdd(entry.getDN(), entry.getObjectClasses(),
+                           entry.getUserAttributes(),
+                           entry.getOperationalAttributes());
+      assertFalse(addOperation.getResultCode() == ResultCode.SUCCESS);
+    }
+    finally
+    {
+      LockManager.unlock(DN.decode("ou=People,o=test"), entryLock);
+    }
+  }
+
+
+
+  /**
+   * Tests an add operation that should be disconnected in a pre-parse plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPreParseAdd()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<LDAPAttribute> attrs = new ArrayList<LDAPAttribute>();
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("top"));
+    values.add(new ASN1OctetString("organizationalUnit"));
+    attrs.add(new LDAPAttribute("objectClass", values));
+
+    values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("People"));
+    attrs.add(new LDAPAttribute("ou", values));
+
+    AddRequestProtocolOp addRequest =
+         new AddRequestProtocolOp(new ASN1OctetString("ou=People,o=test"),
+                                  attrs);
+    message = new LDAPMessage(2, addRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList("PreParse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an add operation that should be disconnected in a pre-operation
+   * plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPreOperationAdd()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<LDAPAttribute> attrs = new ArrayList<LDAPAttribute>();
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("top"));
+    values.add(new ASN1OctetString("organizationalUnit"));
+    attrs.add(new LDAPAttribute("objectClass", values));
+
+    values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("People"));
+    attrs.add(new LDAPAttribute("ou", values));
+
+    AddRequestProtocolOp addRequest =
+         new AddRequestProtocolOp(new ASN1OctetString("ou=People,o=test"),
+                                  attrs);
+    message = new LDAPMessage(2, addRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PreOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an add operation that should be disconnected in a post-operation
+   * plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPostOperationAdd()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<LDAPAttribute> attrs = new ArrayList<LDAPAttribute>();
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("top"));
+    values.add(new ASN1OctetString("organizationalUnit"));
+    attrs.add(new LDAPAttribute("objectClass", values));
+
+    values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("People"));
+    attrs.add(new LDAPAttribute("ou", values));
+
+    AddRequestProtocolOp addRequest =
+         new AddRequestProtocolOp(new ASN1OctetString("ou=People,o=test"),
+                                  attrs);
+    message = new LDAPMessage(2, addRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests an add operation that should be disconnected in a post-response
+   * plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPostResponseAdd()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<LDAPAttribute> attrs = new ArrayList<LDAPAttribute>();
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("top"));
+    values.add(new ASN1OctetString("organizationalUnit"));
+    attrs.add(new LDAPAttribute("objectClass", values));
+
+    values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("People"));
+    attrs.add(new LDAPAttribute("ou", values));
+
+    AddRequestProtocolOp addRequest =
+         new AddRequestProtocolOp(new ASN1OctetString("ou=People,o=test"),
+                                  attrs);
+    message = new LDAPMessage(2, addRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostResponse"));
+    w.writeElement(message.encode());
+
+responseLoop:
+    while (true)
+    {
+      ASN1Element element = r.readElement();
+      if (element == null)
+      {
+        // The connection has been closed.
+        break responseLoop;
+      }
+
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      switch (message.getProtocolOpType())
+      {
+        case OP_TYPE_ADD_RESPONSE:
+          // This was expected.  The disconnect didn't happen until after the
+          // response was sent.
+          break;
+        case OP_TYPE_EXTENDED_RESPONSE:
+          // The server is notifying us that it will be closing the connection.
+          break responseLoop;
+        default:
+          // This is a problem.  It's an unexpected response.
+          try
+          {
+            s.close();
+          } catch (Exception e) {}
+
+          throw new Exception("Unexpected response message " + message +
+                              " encountered in " +
+                              "testDisconnectInPostResponseAdd");
+      }
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
 }
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java
index c997aa1..343ed1a 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java
@@ -30,6 +30,7 @@
 
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.concurrent.locks.Lock;
 
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
@@ -50,9 +51,11 @@
 import org.opends.server.protocols.ldap.LDAPMessage;
 import org.opends.server.tools.LDAPDelete;
 import org.opends.server.types.ByteString;
+import org.opends.server.types.CancelRequest;
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
+import org.opends.server.types.LockManager;
 import org.opends.server.types.ResultCode;
 import org.opends.server.types.WritabilityMode;
 
@@ -767,6 +770,64 @@
 
 
   /**
+   * Tests a delete operation that gets canceled before startup.
+   *
+   * @throws  Exception  If an unexpected probem occurs.
+   */
+  @Test()
+  public void testCancelBeforeStartup()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    DeleteOperation deleteOperation =
+         new DeleteOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                             null, new ASN1OctetString("o=test"));
+
+    CancelRequest cancelRequest = new CancelRequest(false,
+                                                    "testCancelBeforeStartup");
+    deleteOperation.setCancelRequest(cancelRequest);
+    deleteOperation.run();
+    assertEquals(deleteOperation.getResultCode(), ResultCode.CANCELED);
+  }
+
+
+
+  /**
+   * Tests a delete operation in which the server cannot obtain a lock on the
+   * target entry because there is already a read lock held on it.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(groups = { "slow" })
+  public void testCannotLockEntry()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Lock entryLock = LockManager.lockRead(DN.decode("o=test"));
+
+    try
+    {
+      InternalClientConnection conn =
+           InternalClientConnection.getRootConnection();
+
+      DeleteOperation deleteOperation =
+           conn.processDelete(new ASN1OctetString("o=test"));
+      assertFalse(deleteOperation.getResultCode() == ResultCode.SUCCESS);
+    }
+    finally
+    {
+      LockManager.unlock(DN.decode("o=test"), entryLock);
+    }
+  }
+
+
+
+  /**
    * Tests a delete operation that should be disconnected in a pre-parse plugin.
    *
    * @throws  Exception  If an unexpected problem occurs.
@@ -997,5 +1058,66 @@
       s.close();
     } catch (Exception e) {}
   }
+
+
+
+  /**
+   * Tests to ensure that any registered notification listeners are invoked for
+   * a successful delete operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testSuccessWithNotificationListener()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestChangeNotificationListener changeListener =
+         new TestChangeNotificationListener();
+    DirectoryServer.registerChangeNotificationListener(changeListener);
+    assertEquals(changeListener.getAddCount(), 0);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    DeleteOperation deleteOperation =
+         conn.processDelete(new ASN1OctetString("o=test"));
+    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
+    retrieveCompletedOperationElements(deleteOperation);
+
+    assertEquals(changeListener.getDeleteCount(), 1);
+    DirectoryServer.deregisterChangeNotificationListener(changeListener);
+  }
+
+
+
+  /**
+   * Tests to ensure that any registered notification listeners are not invoked
+   * for a failed delete operation.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testFailureWithNotificationListener()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    TestChangeNotificationListener changeListener =
+         new TestChangeNotificationListener();
+    DirectoryServer.registerChangeNotificationListener(changeListener);
+    assertEquals(changeListener.getAddCount(), 0);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    DeleteOperation deleteOperation =
+         conn.processDelete(new ASN1OctetString("cn=nonexistent,o=test"));
+    assertFalse(deleteOperation.getResultCode() == ResultCode.SUCCESS);
+
+    assertEquals(changeListener.getDeleteCount(), 0);
+    DirectoryServer.deregisterChangeNotificationListener(changeListener);
+  }
 }
 
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
index dc15f84..f43db15 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
@@ -31,13 +31,16 @@
 import java.net.Socket;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.locks.Lock;
 
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import org.opends.server.TestCaseUtils;
 import org.opends.server.api.Backend;
+import org.opends.server.plugins.DisconnectClientPlugin;
 import org.opends.server.plugins.UpdatePreOpPlugin;
+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;
@@ -54,9 +57,11 @@
 import org.opends.server.types.AttributeType;
 import org.opends.server.types.AttributeValue;
 import org.opends.server.types.ByteString;
+import org.opends.server.types.CancelRequest;
 import org.opends.server.types.Control;
 import org.opends.server.types.DN;
 import org.opends.server.types.Entry;
+import org.opends.server.types.LockManager;
 import org.opends.server.types.Modification;
 import org.opends.server.types.ModificationType;
 import org.opends.server.types.ObjectClass;
@@ -65,6 +70,7 @@
 
 import static org.testng.Assert.*;
 
+import static org.opends.server.protocols.ldap.LDAPConstants.*;
 import static org.opends.server.util.ServerConstants.*;
 
 
@@ -3643,5 +3649,339 @@
     assertEquals(changeListener.getModifyCount(), 0);
     DirectoryServer.deregisterChangeNotificationListener(changeListener);
   }
+
+
+
+  /**
+   * Tests a modify operation that gets canceled before startup.
+   *
+   * @throws  Exception  If an unexpected probem occurs.
+   */
+  @Test()
+  public void testCancelBeforeStartup()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    InternalClientConnection conn =
+         InternalClientConnection.getRootConnection();
+
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("foo"));
+    LDAPAttribute attr = new LDAPAttribute("description", values);
+
+    ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+    mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+    ModifyOperation modifyOperation =
+         new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
+                             null, new ASN1OctetString("o=test"), mods);
+
+    CancelRequest cancelRequest = new CancelRequest(false,
+                                                    "testCancelBeforeStartup");
+    modifyOperation.setCancelRequest(cancelRequest);
+    modifyOperation.run();
+    assertEquals(modifyOperation.getResultCode(), ResultCode.CANCELED);
+  }
+
+
+
+  /**
+   * Tests a modify operation in which the server cannot obtain a lock on the
+   * target entry because there is already a read lock held on it.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test(groups = { "slow" })
+  public void testCannotLockEntry()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Lock entryLock = LockManager.lockRead(DN.decode("o=test"));
+
+    try
+    {
+      InternalClientConnection conn =
+           InternalClientConnection.getRootConnection();
+
+      ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+      values.add(new ASN1OctetString("foo"));
+      LDAPAttribute attr = new LDAPAttribute("description", values);
+
+      ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+      mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+      ModifyOperation modifyOperation =
+           conn.processModify(new ASN1OctetString("o=test"), mods);
+      assertFalse(modifyOperation.getResultCode() == ResultCode.SUCCESS);
+    }
+    finally
+    {
+      LockManager.unlock(DN.decode("o=test"), entryLock);
+    }
+  }
+
+
+
+  /**
+   * Tests a modify operation that should be disconnected in a pre-parse plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPreParseModify()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("foo"));
+    LDAPAttribute attr = new LDAPAttribute("description", values);
+
+    ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+    mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+    ModifyRequestProtocolOp modifyRequest =
+         new ModifyRequestProtocolOp(new ASN1OctetString("o=test"), mods);
+    message = new LDAPMessage(2, modifyRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList("PreParse"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a modify operation that should be disconnected in a pre-operation
+   * plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPreOperationModify()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("foo"));
+    LDAPAttribute attr = new LDAPAttribute("description", values);
+
+    ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+    mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+    ModifyRequestProtocolOp modifyRequest =
+         new ModifyRequestProtocolOp(new ASN1OctetString("o=test"), mods);
+    message = new LDAPMessage(2, modifyRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PreOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a modify operation that should be disconnected in a post-operation
+   * plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPostOperationModify()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("foo"));
+    LDAPAttribute attr = new LDAPAttribute("description", values);
+
+    ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+    mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+    ModifyRequestProtocolOp modifyRequest =
+         new ModifyRequestProtocolOp(new ASN1OctetString("o=test"), mods);
+    message = new LDAPMessage(2, modifyRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostOperation"));
+    w.writeElement(message.encode());
+
+    ASN1Element element = r.readElement();
+    if (element != null)
+    {
+      // If we got an element back, then it must be a notice of disconnect
+      // unsolicited notification.
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      assertEquals(message.getProtocolOpType(), OP_TYPE_EXTENDED_RESPONSE);
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
+
+
+
+  /**
+   * Tests a modify operation that should be disconnected in a post-response
+   * plugin.
+   *
+   * @throws  Exception  If an unexpected problem occurs.
+   */
+  @Test()
+  public void testDisconnectInPostResponseModify()
+         throws Exception
+  {
+    TestCaseUtils.initializeTestBackend(true);
+
+    Socket s = new Socket("127.0.0.1", (int) TestCaseUtils.getServerLdapPort());
+    ASN1Reader r = new ASN1Reader(s);
+    ASN1Writer w = new ASN1Writer(s);
+    r.setIOTimeout(5000);
+
+    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(), 0);
+
+
+    ArrayList<ASN1OctetString> values = new ArrayList<ASN1OctetString>();
+    values.add(new ASN1OctetString("foo"));
+    LDAPAttribute attr = new LDAPAttribute("description", values);
+
+    ArrayList<LDAPModification> mods = new ArrayList<LDAPModification>();
+    mods.add(new LDAPModification(ModificationType.REPLACE, attr));
+
+    ModifyRequestProtocolOp modifyRequest =
+         new ModifyRequestProtocolOp(new ASN1OctetString("o=test"), mods);
+    message = new LDAPMessage(2, modifyRequest,
+         DisconnectClientPlugin.createDisconnectLDAPControlList(
+              "PostResponse"));
+    w.writeElement(message.encode());
+
+responseLoop:
+    while (true)
+    {
+      ASN1Element element = r.readElement();
+      if (element == null)
+      {
+        // The connection has been closed.
+        break responseLoop;
+      }
+
+      message = LDAPMessage.decode(element.decodeAsSequence());
+      switch (message.getProtocolOpType())
+      {
+        case OP_TYPE_MODIFY_RESPONSE:
+          // This was expected.  The disconnect didn't happen until after the
+          // response was sent.
+          break;
+        case OP_TYPE_EXTENDED_RESPONSE:
+          // The server is notifying us that it will be closing the connection.
+          break responseLoop;
+        default:
+          // This is a problem.  It's an unexpected response.
+          try
+          {
+            s.close();
+          } catch (Exception e) {}
+
+          throw new Exception("Unexpected response message " + message +
+                              " encountered in " +
+                              "testDisconnectInPostResponseModify");
+      }
+    }
+
+    try
+    {
+      s.close();
+    } catch (Exception e) {}
+  }
 }
 

--
Gitblit v1.10.0