From 47e492a9ccde3af37512cdde9cd13d3c388f4ebe Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Tue, 15 Dec 2009 21:58:47 +0000
Subject: [PATCH] Various changes:

---
 opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java                 |    6 
 opendj-sdk/sdk/src/org/opends/sdk/FutureResult.java                              |   20 
 opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java                      |  133 +-
 opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java                         |    8 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java |    4 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/BindResultFutureImpl.java             |   12 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java        |   93 +
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ExtendedResultFutureImpl.java         |   11 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java                   |  125 +-
 opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java            |  168 ++-
 opendj-sdk/sdk/src/org/opends/sdk/schema/CoreSchemaImpl.java                     |    2 
 opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java                             |  635 +++++++------
 opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java                    |   38 
 opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java                     |   18 
 opendj-sdk/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java            |  267 +++++
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/AbstractResultFutureImpl.java         |  213 ---
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java                      |    6 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java               |    4 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ResultFutureImpl.java                 |   11 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPCompare.java                     |    6 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java                      |    8 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPPasswordModify.java              |    6 
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java                         |    8 
 opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java             |  418 +++++++++
 /dev/null                                                                        |   71 -
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/CompareResultFutureImpl.java          |   12 
 opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java            |   27 
 opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java                                   |   10 
 opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SearchResultFutureImpl.java           |   43 
 opendj-sdk/sdk/src/com/sun/opends/sdk/util/FutureResultTransformer.java          |  199 ++-
 opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java                            |   90 +
 opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java                      |    7 
 opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java                |   61 
 opendj-sdk/sdk/src/org/opends/sdk/ldap/LDAPConnectionFactory.java                |    4 
 34 files changed, 1,676 insertions(+), 1,068 deletions(-)

diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/AbstractResultFutureImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/AbstractResultFutureImpl.java
index 66dc52f..0cbcf31 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/AbstractResultFutureImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/AbstractResultFutureImpl.java
@@ -29,236 +29,97 @@
 
 
 
-import java.util.concurrent.*;
-import java.util.logging.Level;
-
 import org.opends.sdk.ErrorResultException;
 import org.opends.sdk.ResultCode;
-import org.opends.sdk.ResultFuture;
+import org.opends.sdk.FutureResult;
 import org.opends.sdk.ResultHandler;
 import org.opends.sdk.requests.Requests;
-import org.opends.sdk.responses.Responses;
 import org.opends.sdk.responses.Result;
 
-import com.sun.opends.sdk.util.StaticUtils;
+import com.sun.opends.sdk.util.AbstractFutureResult;
 
 
 
 /**
  * Abstract result future implementation.
+ *
+ * @param <S>
+ *          The type of result returned by this future.
  */
-public abstract class AbstractResultFutureImpl<R extends Result>
-    implements ResultFuture<R>, Runnable
+abstract class AbstractResultFutureImpl<S extends Result> extends
+    AbstractFutureResult<S> implements FutureResult<S>
 {
   private final LDAPConnection connection;
 
-  private final ResultHandler<? super R> handler;
-
-  private final ExecutorService handlerExecutor;
-
   private final int messageID;
 
-  // Use a semaphore instead of a lock because semaphores can be
-  // released by different thread to acquirer.
-  private final Semaphore invokerLock;
-
-  private final CountDownLatch latch = new CountDownLatch(1);
-
-  private volatile boolean isCancelled = false;
-
-  private volatile R result = null;
 
 
-
+  /**
+   * Creates a new LDAP result future.
+   *
+   * @param messageID
+   *          The request message ID.
+   * @param handler
+   *          The result handler, maybe {@code null}.
+   * @param connection
+   *          The client connection.
+   */
   AbstractResultFutureImpl(int messageID,
-      ResultHandler<? super R> handler, LDAPConnection connection,
-      ExecutorService handlerExecutor)
+      ResultHandler<? super S> handler, LDAPConnection connection)
   {
+    super(handler);
     this.messageID = messageID;
-    this.handler = handler;
     this.connection = connection;
-    this.handlerExecutor = handlerExecutor;
-    if (handlerExecutor == null)
-    {
-      invokerLock = null;
-    }
-    else
-    {
-      invokerLock = new Semaphore(1);
-    }
   }
 
 
 
-  public synchronized boolean cancel(boolean b)
+  /**
+   * {@inheritDoc}
+   */
+  protected final ErrorResultException handleCancelRequest()
   {
-    if (!isDone())
-    {
-      isCancelled = true;
-      connection.abandon(Requests.newAbandonRequest(messageID));
-      latch.countDown();
-      return true;
-    }
-    else
-    {
-      return false;
-    }
+    connection.abandon(Requests.newAbandonRequest(messageID));
+    return null;
   }
 
 
 
-  public R get() throws InterruptedException, ErrorResultException
-  {
-    latch.await();
-    return get0();
-  }
-
-
-
-  public R get(long timeout, TimeUnit unit)
-      throws InterruptedException, TimeoutException,
-      ErrorResultException
-  {
-    if (!latch.await(timeout, unit))
-    {
-      throw new TimeoutException();
-    }
-    return get0();
-  }
-
-
-
-  public int getMessageID()
+  /**
+   * {@inheritDoc}
+   */
+  public final int getRequestID()
   {
     return messageID;
   }
 
 
 
-  public boolean isCancelled()
+  final void adaptErrorResult(Result result)
   {
-    return isCancelled;
+    S errorResult = newErrorResult(result.getResultCode(), result
+        .getDiagnosticMessage(), result.getCause());
+    setResultOrError(errorResult);
   }
 
 
 
-  public boolean isDone()
-  {
-    return latch.getCount() == 0;
-  }
-
-
-
-  public void run()
+  final void setResultOrError(S result)
   {
     if (result.getResultCode().isExceptional())
     {
-      ErrorResultException e = ErrorResultException.wrap(result);
-      handler.handleErrorResult(e);
+      handleErrorResult(ErrorResultException.wrap(result));
     }
     else
     {
-      handler.handleResult(result);
+      handleResult(result);
     }
   }
 
 
 
-  final void handleErrorResult(Result result)
-  {
-    R errorResult = newErrorResult(result.getResultCode(), result
-        .getDiagnosticMessage(), result.getCause());
-    handleResult(errorResult);
-  }
-
-
-
-  abstract R newErrorResult(ResultCode resultCode,
+  abstract S newErrorResult(ResultCode resultCode,
       String diagnosticMessage, Throwable cause);
 
-
-
-  final void handleResult(R result)
-  {
-    if (!isDone())
-    {
-      this.result = result;
-      if (handler != null)
-      {
-        invokeHandler(this);
-      }
-      latch.countDown();
-    }
-  }
-
-
-
-  final void invokeHandler(final Runnable runnable)
-  {
-    try
-    {
-      if (handlerExecutor == null)
-      {
-        runnable.run();
-      }
-      else
-      {
-        invokerLock.acquire();
-
-        try
-        {
-          handlerExecutor.submit(new Runnable()
-          {
-            public void run()
-            {
-              try
-              {
-                runnable.run();
-              }
-              finally
-              {
-                invokerLock.release();
-              }
-            }
-          });
-        }
-        catch (Exception e)
-        {
-          invokerLock.release();
-        }
-      }
-    }
-    catch (InterruptedException e)
-    {
-      // Thread has been interrupted so give up.
-      if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
-      {
-        StaticUtils.DEBUG_LOG.warning(String.format(
-            "Invoke thread interrupted: %s", StaticUtils
-                .getExceptionMessage(e)));
-      }
-
-      // Reset interrupt status.
-      Thread.currentThread().interrupt();
-    }
-  }
-
-
-
-  private R get0() throws ErrorResultException
-  {
-    if (isCancelled())
-    {
-      throw ErrorResultException.wrap(Responses
-          .newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED));
-    }
-    else if (result.getResultCode().isExceptional())
-    {
-      throw ErrorResultException.wrap(result);
-    }
-    else
-    {
-      return result;
-    }
-  }
 }
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/BindResultFutureImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/BindResultFutureImpl.java
index b1d61b5..37fb0d3 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/BindResultFutureImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/BindResultFutureImpl.java
@@ -29,10 +29,8 @@
 
 
 
-import java.util.concurrent.ExecutorService;
-
 import org.opends.sdk.ResultCode;
-import org.opends.sdk.ResultFuture;
+import org.opends.sdk.FutureResult;
 import org.opends.sdk.ResultHandler;
 import org.opends.sdk.requests.BindRequest;
 import org.opends.sdk.responses.BindResult;
@@ -44,9 +42,9 @@
 /**
  * Bind result future implementation.
  */
-public final class BindResultFutureImpl extends
+final class BindResultFutureImpl extends
     AbstractResultFutureImpl<BindResult> implements
-    ResultFuture<BindResult>
+    FutureResult<BindResult>
 {
   private final BindRequest request;
 
@@ -56,9 +54,9 @@
 
   BindResultFutureImpl(int messageID, BindRequest request,
       ResultHandler<? super BindResult> handler,
-      LDAPConnection connection, ExecutorService handlerExecutor)
+      LDAPConnection connection)
   {
-    super(messageID, handler, connection, handlerExecutor);
+    super(messageID, handler, connection);
     this.request = request;
   }
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/CompareResultFutureImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/CompareResultFutureImpl.java
index c56bb8b..114f775 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/CompareResultFutureImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/CompareResultFutureImpl.java
@@ -29,10 +29,8 @@
 
 
 
-import java.util.concurrent.ExecutorService;
-
 import org.opends.sdk.ResultCode;
-import org.opends.sdk.ResultFuture;
+import org.opends.sdk.FutureResult;
 import org.opends.sdk.ResultHandler;
 import org.opends.sdk.requests.CompareRequest;
 import org.opends.sdk.responses.CompareResult;
@@ -43,9 +41,9 @@
 /**
  * Compare result future implementation.
  */
-public final class CompareResultFutureImpl extends
+final class CompareResultFutureImpl extends
     AbstractResultFutureImpl<CompareResult> implements
-    ResultFuture<CompareResult>
+    FutureResult<CompareResult>
 {
   private final CompareRequest request;
 
@@ -53,9 +51,9 @@
 
   CompareResultFutureImpl(int messageID, CompareRequest request,
       ResultHandler<? super CompareResult> handler,
-      LDAPConnection connection, ExecutorService handlerExecutor)
+      LDAPConnection connection)
   {
-    super(messageID, handler, connection, handlerExecutor);
+    super(messageID, handler, connection);
     this.request = request;
   }
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ExtendedResultFutureImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ExtendedResultFutureImpl.java
index f4a59c4..55d0ead 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ExtendedResultFutureImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ExtendedResultFutureImpl.java
@@ -29,8 +29,6 @@
 
 
 
-import java.util.concurrent.ExecutorService;
-
 import org.opends.sdk.*;
 import org.opends.sdk.requests.ExtendedRequest;
 import org.opends.sdk.responses.Result;
@@ -40,18 +38,17 @@
 /**
  * Extended result future implementation.
  */
-public final class ExtendedResultFutureImpl<R extends Result> extends
-    AbstractResultFutureImpl<R> implements ResultFuture<R>
+final class ExtendedResultFutureImpl<R extends Result> extends
+    AbstractResultFutureImpl<R> implements FutureResult<R>
 {
   private final ExtendedRequest<R> request;
 
 
 
   ExtendedResultFutureImpl(int messageID, ExtendedRequest<R> request,
-      ResultHandler<? super R> handler,
-      LDAPConnection connection, ExecutorService handlerExecutor)
+      ResultHandler<? super R> handler, LDAPConnection connection)
   {
-    super(messageID, handler, connection, handlerExecutor);
+    super(messageID, handler, connection);
     this.request = request;
   }
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
index 78f1a6c..d86198a 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
@@ -92,7 +92,7 @@
           ResultFutureImpl future = (ResultFutureImpl) pendingRequest;
           if (future.getRequest() instanceof AddRequest)
           {
-            future.handleResult(result);
+            future.setResultOrError(result);
             return;
           }
         }
@@ -142,7 +142,7 @@
                     .setDiagnosticMessage(
                         "An error occurred during SASL authentication")
                     .setCause(e);
-                future.handleErrorResult(errorResult);
+                future.adaptErrorResult(errorResult);
                 return;
               }
             }
@@ -177,7 +177,7 @@
                         ResultCode.CLIENT_SIDE_ENCODING_ERROR)
                         .setCause(e);
                     connectionErrorOccurred(errorResult);
-                    future.handleErrorResult(errorResult);
+                    future.adaptErrorResult(errorResult);
                   }
                 }
               }
@@ -198,7 +198,7 @@
             }
           }
           pendingBindOrStartTLS = -1;
-          future.handleResult(result);
+          future.setResultOrError(result);
         }
         else
         {
@@ -222,7 +222,7 @@
         if (pendingRequest instanceof CompareResultFutureImpl)
         {
           CompareResultFutureImpl future = (CompareResultFutureImpl) pendingRequest;
-          future.handleResult(result);
+          future.setResultOrError(result);
         }
         else
         {
@@ -248,7 +248,7 @@
           ResultFutureImpl future = (ResultFutureImpl) pendingRequest;
           if (future.getRequest() instanceof DeleteRequest)
           {
-            future.handleResult(result);
+            future.setResultOrError(result);
             return;
           }
         }
@@ -339,7 +339,7 @@
               ResultCode.CLIENT_SIDE_DECODING_ERROR)
               .setDiagnosticMessage(de.getLocalizedMessage()).setCause(
                   de);
-          extendedFuture.handleErrorResult(errorResult);
+          extendedFuture.adaptErrorResult(errorResult);
         }
       }
       else
@@ -409,7 +409,7 @@
           ResultFutureImpl future = (ResultFutureImpl) pendingRequest;
           if (future.getRequest() instanceof ModifyDNRequest)
           {
-            future.handleResult(result);
+            future.setResultOrError(result);
             return;
           }
         }
@@ -434,7 +434,7 @@
           ResultFutureImpl future = (ResultFutureImpl) pendingRequest;
           if (future.getRequest() instanceof ModifyRequest)
           {
-            future.handleResult(result);
+            future.setResultOrError(result);
             return;
           }
         }
@@ -457,7 +457,7 @@
         if (pendingRequest instanceof SearchResultFutureImpl)
         {
           ((SearchResultFutureImpl) pendingRequest)
-              .handleResult(result);
+              .setResultOrError(result);
         }
         else
         {
@@ -666,8 +666,6 @@
 
   private final Object writeLock = new Object();
 
-
-
   /**
    * Creates a new LDAP connection.
    *
@@ -749,12 +747,12 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<Result> add(AddRequest request,
+  public FutureResult<Result> add(AddRequest request,
       ResultHandler<Result> handler)
   {
     int messageID = nextMsgID.getAndIncrement();
     ResultFutureImpl future = new ResultFutureImpl(messageID, request,
-        handler, this, connFactory.getHandlerInvokers());
+        handler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -764,12 +762,12 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(Responses.newResult(
+          future.setResultOrError(Responses.newResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "Bind or Start TLS operation in progress"));
           return future;
@@ -789,7 +787,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -828,12 +826,12 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<BindResult> bind(BindRequest request,
+  public FutureResult<BindResult> bind(BindRequest request,
       ResultHandler<? super BindResult> handler)
   {
     int messageID = nextMsgID.getAndIncrement();
     BindResultFutureImpl future = new BindResultFutureImpl(messageID,
-        request, handler, this, connFactory.getHandlerInvokers());
+        request, handler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -843,19 +841,19 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(Responses.newBindResult(
+          future.setResultOrError(Responses.newBindResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "Bind or Start TLS operation in progress"));
           return future;
         }
         if (!pendingRequests.isEmpty())
         {
-          future.handleResult(Responses.newBindResult(
+          future.setResultOrError(Responses.newBindResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "There are other operations pending on this connection"));
           return future;
@@ -886,7 +884,7 @@
                   .setDiagnosticMessage(
                       "An error occurred during SASL authentication")
                   .setCause(e);
-              future.handleErrorResult(errorResult);
+              future.adaptErrorResult(errorResult);
               return future;
             }
           }
@@ -898,7 +896,7 @@
           else
           {
             pendingRequests.remove(messageID);
-            future.handleResult(Responses.newBindResult(
+            future.setResultOrError(Responses.newBindResult(
                 ResultCode.CLIENT_SIDE_AUTH_UNKNOWN)
                 .setDiagnosticMessage("Auth type not supported"));
           }
@@ -913,7 +911,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -947,13 +945,12 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<CompareResult> compare(CompareRequest request,
+  public FutureResult<CompareResult> compare(CompareRequest request,
       ResultHandler<? super CompareResult> handler)
   {
     int messageID = nextMsgID.getAndIncrement();
     CompareResultFutureImpl future = new CompareResultFutureImpl(
-        messageID, request, handler, this, connFactory
-            .getHandlerInvokers());
+        messageID, request, handler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -963,12 +960,12 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(Responses.newCompareResult(
+          future.setResultOrError(Responses.newCompareResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "Bind or Start TLS operation in progress"));
           return future;
@@ -989,7 +986,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -1006,12 +1003,12 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<Result> delete(DeleteRequest request,
+  public FutureResult<Result> delete(DeleteRequest request,
       ResultHandler<Result> handler)
   {
     int messageID = nextMsgID.getAndIncrement();
     ResultFutureImpl future = new ResultFutureImpl(messageID, request,
-        handler, this, connFactory.getHandlerInvokers());
+        handler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -1021,12 +1018,12 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(Responses.newResult(
+          future.setResultOrError(Responses.newResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "Bind or Start TLS operation in progress"));
           return future;
@@ -1047,7 +1044,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -1064,13 +1061,12 @@
   /**
    * {@inheritDoc}
    */
-  public <R extends Result> ResultFuture<R> extendedRequest(
+  public <R extends Result> FutureResult<R> extendedRequest(
       ExtendedRequest<R> request, ResultHandler<? super R> handler)
   {
     int messageID = nextMsgID.getAndIncrement();
     ExtendedResultFutureImpl<R> future = new ExtendedResultFutureImpl<R>(
-        messageID, request, handler, this, connFactory
-            .getHandlerInvokers());
+        messageID, request, handler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -1080,12 +1076,12 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(request.getExtendedOperation()
+          future.setResultOrError(request.getExtendedOperation()
               .decodeResponse(ResultCode.OPERATIONS_ERROR, "",
                   "Bind or Start TLS operation in progress"));
           return future;
@@ -1095,14 +1091,14 @@
         {
           if (!pendingRequests.isEmpty())
           {
-            future.handleResult(request.getExtendedOperation()
+            future.setResultOrError(request.getExtendedOperation()
                 .decodeResponse(ResultCode.OPERATIONS_ERROR, "",
                     "There are pending operations on this connection"));
             return future;
           }
           if (isTLSEnabled())
           {
-            future.handleResult(request.getExtendedOperation()
+            future.setResultOrError(request.getExtendedOperation()
                 .decodeResponse(ResultCode.OPERATIONS_ERROR, "",
                     "This connection is already TLS enabled"));
           }
@@ -1125,7 +1121,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -1142,12 +1138,12 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<Result> modify(ModifyRequest request,
+  public FutureResult<Result> modify(ModifyRequest request,
       ResultHandler<Result> handler)
   {
     int messageID = nextMsgID.getAndIncrement();
     ResultFutureImpl future = new ResultFutureImpl(messageID, request,
-        handler, this, connFactory.getHandlerInvokers());
+        handler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -1157,12 +1153,12 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(Responses.newResult(
+          future.setResultOrError(Responses.newResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "Bind or Start TLS operation in progress"));
           return future;
@@ -1183,7 +1179,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -1200,12 +1196,12 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<Result> modifyDN(ModifyDNRequest request,
+  public FutureResult<Result> modifyDN(ModifyDNRequest request,
       ResultHandler<Result> handler)
   {
     int messageID = nextMsgID.getAndIncrement();
     ResultFutureImpl future = new ResultFutureImpl(messageID, request,
-        handler, this, connFactory.getHandlerInvokers());
+        handler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -1215,12 +1211,12 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(Responses.newResult(
+          future.setResultOrError(Responses.newResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "Bind or Start TLS operation in progress"));
           return future;
@@ -1241,7 +1237,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -1274,14 +1270,13 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<Result> search(SearchRequest request,
+  public FutureResult<Result> search(SearchRequest request,
       ResultHandler<Result> resultHandler,
       SearchResultHandler searchResulthandler)
   {
     int messageID = nextMsgID.getAndIncrement();
     SearchResultFutureImpl future = new SearchResultFutureImpl(
-        messageID, request, resultHandler, searchResulthandler, this,
-        connFactory.getHandlerInvokers());
+        messageID, request, resultHandler, searchResulthandler, this);
     ASN1StreamWriter asn1Writer = connFactory
         .getASN1Writer(streamWriter);
 
@@ -1291,12 +1286,12 @@
       {
         if (connectionInvalidReason != null)
         {
-          future.handleErrorResult(connectionInvalidReason);
+          future.adaptErrorResult(connectionInvalidReason);
           return future;
         }
         if (pendingBindOrStartTLS > 0)
         {
-          future.handleResult(Responses.newResult(
+          future.setResultOrError(Responses.newResult(
               ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
               "Bind or Start TLS operation in progress"));
           return future;
@@ -1317,7 +1312,7 @@
           Result errorResult = Responses.newResult(
               ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
           connectionErrorOccurred(errorResult);
-          future.handleErrorResult(errorResult);
+          future.adaptErrorResult(errorResult);
         }
       }
     }
@@ -1407,7 +1402,7 @@
               .getASN1Writer(streamWriter);
           int messageID = nextMsgID.getAndIncrement();
           AbandonRequest abandon = Requests.newAbandonRequest(future
-              .getMessageID());
+              .getRequestID());
           try
           {
             LDAPEncoder.encodeAbandonRequest(asn1Writer, messageID,
@@ -1424,7 +1419,7 @@
           }
         }
 
-        future.handleErrorResult(reason);
+        future.adaptErrorResult(reason);
       }
       pendingRequests.clear();
 
@@ -1584,14 +1579,14 @@
         }
         catch (ErrorResultException e)
         {
-          future.handleErrorResult(e.getResult());
+          future.adaptErrorResult(e.getResult());
           return;
         }
       }
       pendingBindOrStartTLS = -1;
     }
 
-    future.handleResult(decodedResponse);
+    future.setResultOrError(decodedResponse);
   }
 
 
@@ -1604,7 +1599,7 @@
         ResultCode.CLIENT_SIDE_DECODING_ERROR).setDiagnosticMessage(
         "LDAP response message did not match request");
 
-    pendingRequest.handleErrorResult(errorResult);
+    pendingRequest.adaptErrorResult(errorResult);
     connectionErrorOccurred(errorResult);
   }
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
index 42069ae..9392984 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
@@ -88,7 +88,7 @@
 
 
   private static class FailedImpl implements
-      ConnectionFuture<AsynchronousConnection>
+      FutureResult<AsynchronousConnection>
   {
     private volatile ErrorResultException exception;
 
@@ -136,12 +136,19 @@
     {
       return false;
     }
+
+
+
+    public int getRequestID()
+    {
+      return -1;
+    }
   }
 
 
 
-  private class ConnectionFutureImpl implements
-      ConnectionFuture<AsynchronousConnection>,
+  private class ResultFutureImpl implements
+      FutureResult<AsynchronousConnection>,
       com.sun.grizzly.CompletionHandler<com.sun.grizzly.Connection>,
       ResultHandler<Result>
   {
@@ -151,18 +158,18 @@
 
     private volatile Future<com.sun.grizzly.Connection> connectFuture;
 
-    private volatile ResultFuture<?> sslFuture;
+    private volatile FutureResult<?> sslFuture;
 
     private final CountDownLatch latch = new CountDownLatch(1);
 
-    private final ConnectionResultHandler<? super AsynchronousConnection> handler;
+    private final ResultHandler<? super AsynchronousConnection> handler;
 
     private boolean cancelled;
 
 
 
-    private ConnectionFutureImpl(
-        ConnectionResultHandler<? super AsynchronousConnection> handler)
+    private ResultFutureImpl(
+        ResultHandler<? super AsynchronousConnection> handler)
     {
       this.handler = handler;
     }
@@ -232,6 +239,13 @@
 
 
 
+    public int getRequestID()
+    {
+      return -1;
+    }
+
+
+
     /**
      * {@inheritDoc}
      */
@@ -267,7 +281,7 @@
           latch.countDown();
           if (handler != null)
           {
-            handler.handleConnection(this.connection);
+            handler.handleResult(this.connection);
           }
         }
         catch (CancellationException ce)
@@ -281,7 +295,7 @@
           latch.countDown();
           if (handler != null)
           {
-            handler.handleConnectionError(exception);
+            handler.handleErrorResult(exception);
           }
         }
       }
@@ -290,7 +304,7 @@
         latch.countDown();
         if (handler != null)
         {
-          handler.handleConnection(this.connection);
+          handler.handleResult(this.connection);
         }
       }
     }
@@ -307,7 +321,7 @@
       latch.countDown();
       if (handler != null)
       {
-        handler.handleConnectionError(exception);
+        handler.handleErrorResult(exception);
       }
     }
 
@@ -330,7 +344,7 @@
       latch.countDown();
       if (handler != null)
       {
-        handler.handleConnection(connection);
+        handler.handleResult(connection);
       }
     }
 
@@ -343,7 +357,7 @@
       latch.countDown();
       if (handler != null)
       {
-        handler.handleConnectionError(exception);
+        handler.handleErrorResult(exception);
       }
     }
   }
@@ -363,7 +377,6 @@
     if (TCP_NIO_TRANSPORT == null)
     {
       // Create a default transport using the Grizzly framework.
-      //
       TCP_NIO_TRANSPORT = TransportFactory.getInstance()
           .createTCPTransport();
       try
@@ -382,23 +395,7 @@
         @Override
         public void run()
         {
-          try
-          {
-            TCP_NIO_TRANSPORT.stop();
-          }
-          catch (Exception e)
-          {
-            // Ignore.
-          }
-
-          try
-          {
-            TCP_NIO_TRANSPORT.getWorkerThreadPool().shutdown();
-          }
-          catch (Exception e)
-          {
-            // Ignore.
-          }
+          ShutdownTCPNIOTransport();
         }
 
       });
@@ -408,6 +405,34 @@
 
 
 
+  private synchronized static void ShutdownTCPNIOTransport()
+  {
+    if (TCP_NIO_TRANSPORT != null)
+    {
+      try
+      {
+        TCP_NIO_TRANSPORT.stop();
+      }
+      catch (Exception e)
+      {
+        // Ignore.
+      }
+
+      // try
+      // {
+      // TCP_NIO_TRANSPORT.getWorkerThreadPool().shutdown();
+      // }
+      // catch (Exception e)
+      // {
+      // // Ignore.
+      // }
+
+      TCP_NIO_TRANSPORT = null;
+    }
+  }
+
+
+
   private final Attribute<LDAPConnection> ldapConnectionAttr;
 
   private final InetSocketAddress socketAddress;
@@ -482,10 +507,10 @@
   /**
    * {@inheritDoc}
    */
-  public ConnectionFuture<AsynchronousConnection> getAsynchronousConnection(
-      ConnectionResultHandler<? super AsynchronousConnection> handler)
+  public FutureResult<AsynchronousConnection> getAsynchronousConnection(
+      ResultHandler<? super AsynchronousConnection> handler)
   {
-    ConnectionFutureImpl future = new ConnectionFutureImpl(handler);
+    ResultFutureImpl future = new ResultFutureImpl(handler);
 
     try
     {
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ResultFutureImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ResultFutureImpl.java
index 3129167..fd62983 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ResultFutureImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ResultFutureImpl.java
@@ -29,10 +29,8 @@
 
 
 
-import java.util.concurrent.ExecutorService;
-
 import org.opends.sdk.ResultCode;
-import org.opends.sdk.ResultFuture;
+import org.opends.sdk.FutureResult;
 import org.opends.sdk.ResultHandler;
 import org.opends.sdk.requests.Request;
 import org.opends.sdk.responses.Responses;
@@ -44,17 +42,16 @@
  * Result future implementation.
  */
 public final class ResultFutureImpl extends
-    AbstractResultFutureImpl<Result> implements ResultFuture<Result>
+    AbstractResultFutureImpl<Result> implements FutureResult<Result>
 {
   private final Request request;
 
 
 
   ResultFutureImpl(int messageID, Request request,
-      ResultHandler<Result> handler, LDAPConnection connection,
-      ExecutorService handlerExecutor)
+      ResultHandler<Result> handler, LDAPConnection connection)
   {
-    super(messageID, handler, connection, handlerExecutor);
+    super(messageID, handler, connection);
     this.request = request;
   }
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SearchResultFutureImpl.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SearchResultFutureImpl.java
index 5266d71..3ea5df1 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SearchResultFutureImpl.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SearchResultFutureImpl.java
@@ -29,10 +29,8 @@
 
 
 
-import java.util.concurrent.ExecutorService;
-
 import org.opends.sdk.ResultCode;
-import org.opends.sdk.ResultFuture;
+import org.opends.sdk.FutureResult;
 import org.opends.sdk.ResultHandler;
 import org.opends.sdk.SearchResultHandler;
 import org.opends.sdk.requests.SearchRequest;
@@ -46,8 +44,8 @@
 /**
  * Search result future implementation.
  */
-public final class SearchResultFutureImpl extends
-    AbstractResultFutureImpl<Result> implements ResultFuture<Result>
+final class SearchResultFutureImpl extends
+    AbstractResultFutureImpl<Result> implements FutureResult<Result>
 {
 
   private final SearchResultHandler searchResultHandler;
@@ -58,50 +56,43 @@
 
   SearchResultFutureImpl(int messageID, SearchRequest request,
       ResultHandler<Result> resultHandler,
-      SearchResultHandler searchResultHandler,
-      LDAPConnection connection, ExecutorService handlerExecutor)
+      SearchResultHandler searchResultHandler, LDAPConnection connection)
   {
-    super(messageID, resultHandler, connection, handlerExecutor);
+    super(messageID, resultHandler, connection);
     this.request = request;
     this.searchResultHandler = searchResultHandler;
   }
 
 
 
-  void handleSearchResultEntry(
-      final SearchResultEntry entry)
+  void handleSearchResultEntry(SearchResultEntry entry)
   {
+    // FIXME: there's a potential race condition here - the future could
+    // get cancelled between the isDone() call and the handler
+    // invocation. We'd need to add support for intermediate handlers in
+    // the synchronizer.
     if (!isDone())
     {
       if (searchResultHandler != null)
       {
-        invokeHandler(new Runnable()
-        {
-          public void run()
-          {
-            searchResultHandler.handleEntry(entry);
-          }
-        });
+        searchResultHandler.handleEntry(entry);
       }
     }
   }
 
 
 
-  void handleSearchResultReference(
-      final SearchResultReference reference)
+  void handleSearchResultReference(SearchResultReference reference)
   {
+    // FIXME: there's a potential race condition here - the future could
+    // get cancelled between the isDone() call and the handler
+    // invocation. We'd need to add support for intermediate handlers in
+    // the synchronizer.
     if (!isDone())
     {
       if (searchResultHandler != null)
       {
-        invokeHandler(new Runnable()
-        {
-          public void run()
-          {
-            searchResultHandler.handleReference(reference);
-          }
-        });
+        searchResultHandler.handleReference(reference);
       }
     }
   }
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
index bb69539..9a3a7be 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
@@ -349,8 +349,8 @@
   /**
    * {@inheritDoc}
    */
-  public ConnectionFuture<? extends AsynchronousConnection> getAsynchronousConnection(
-      ConnectionResultHandler<? super AsynchronousConnection> handler)
+  public FutureResult<? extends AsynchronousConnection> getAsynchronousConnection(
+      ResultHandler<? super AsynchronousConnection> handler)
   {
     return connFactory.getAsynchronousConnection(handler);
   }
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPCompare.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPCompare.java
index d7bbee1..6b13a6c 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPCompare.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPCompare.java
@@ -69,11 +69,7 @@
   public static void main(String[] args)
   {
     int retCode = mainCompare(args, System.in, System.out, System.err);
-
-    if (retCode != 0)
-    {
-      System.exit(filterExitCode(retCode));
-    }
+    System.exit(filterExitCode(retCode));
   }
 
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java
index cf62330..f79eeec 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPModify.java
@@ -80,11 +80,7 @@
   public static void main(String[] args)
   {
     int retCode = mainModify(args, System.in, System.out, System.err);
-
-    if (retCode != 0)
-    {
-      System.exit(filterExitCode(retCode));
-    }
+    System.exit(filterExitCode(retCode));
   }
 
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPPasswordModify.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPPasswordModify.java
index ee016b4..618ec88 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPPasswordModify.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPPasswordModify.java
@@ -41,11 +41,7 @@
   public static void main(String[] args)
   {
     int retCode = mainPasswordModify(args, System.in, System.out, System.err);
-
-    if (retCode != 0)
-    {
-      System.exit(filterExitCode(retCode));
-    }
+    System.exit(filterExitCode(retCode));
   }
 
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java
index 2bc5d85..e371ad7 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/LDAPSearch.java
@@ -31,6 +31,7 @@
 
 import static com.sun.opends.sdk.messages.Messages.*;
 import static com.sun.opends.sdk.tools.ToolConstants.*;
+import static com.sun.opends.sdk.tools.Utils.*;
 
 import java.io.*;
 import java.util.ArrayList;
@@ -186,11 +187,7 @@
   {
     int retCode = mainSearch(args, false, System.in, System.out,
         System.err);
-
-    if (retCode != 0)
-    {
-      System.exit(Utils.filterExitCode(retCode));
-    }
+    System.exit(filterExitCode(retCode));
   }
 
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java
index 1aaf465..c9d9fdd 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ModRate.java
@@ -64,11 +64,7 @@
   public static void main(String[] args)
   {
     int retCode = mainModRate(args, System.in, System.out, System.err);
-
-    if (retCode != 0)
-    {
-      System.exit(filterExitCode(retCode));
-    }
+    System.exit(filterExitCode(retCode));
   }
 
 
@@ -295,7 +291,7 @@
 
 
 
-      public ResultFuture<?> performOperation(
+      public FutureResult<?> performOperation(
           AsynchronousConnection connection,
           ResultHandler<Result> handler, DataSource[] dataSources)
       {
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java
index 01dd45f..48ce379 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/PerformanceRunner.java
@@ -370,7 +370,7 @@
 
 
 
-    public abstract ResultFuture<?> performOperation(
+    public abstract FutureResult<?> performOperation(
         AsynchronousConnection connection, R handler,
         DataSource[] dataSources);
 
@@ -394,7 +394,7 @@
         }
       }
 
-      ResultFuture<?> future;
+      FutureResult<?> future;
       AsynchronousConnection connection;
       R handler;
 
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java
index f5fbdb1..eef620c 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/tools/SearchRate.java
@@ -71,11 +71,7 @@
   {
     int retCode = mainSearchRate(args, System.in, System.out,
         System.err);
-
-    if (retCode != 0)
-    {
-      System.exit(filterExitCode(retCode));
-    }
+    System.exit(filterExitCode(retCode));
   }
 
 
@@ -375,7 +371,7 @@
 
 
 
-      public ResultFuture<?> performOperation(
+      public FutureResult<?> performOperation(
           AsynchronousConnection connection,
           SearchStatsHandler handler, DataSource[] dataSources)
       {
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java
new file mode 100644
index 0000000..cca5877
--- /dev/null
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/AbstractFutureResult.java
@@ -0,0 +1,418 @@
+/*
+ * 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
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+package com.sun.opends.sdk.util;
+
+
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer;
+
+import org.opends.sdk.*;
+import org.opends.sdk.responses.Responses;
+import org.opends.sdk.responses.Result;
+
+
+
+/**
+ * This class provides a skeletal implementation of the {@code
+ * FutureResult} interface, to minimize the effort required to implement
+ * this interface.
+ * <p>
+ * This {@code FutureResult} implementation provides the following
+ * features:
+ * <ul>
+ * <li>The {@link #get} methods throw {@link ErrorResultException}s
+ * instead of the more generic {@code ExecutionException}s.
+ * <li>The {@link #get} methods never throw {@code
+ * CancellationException} since requests in this SDK can usually be
+ * cancelled via other external means (e.g. the {@code Cancel} extended
+ * operation) for which there are well defined error results. Therefore
+ * cancellation is always signalled by throwing a
+ * {@link CancelledResultException} in order to be consistent with other
+ * error results.
+ * <li>A {@link ResultHandler} can be provided to the constructor. The
+ * result handler will be invoked immediately after the result or error
+ * is received but before threads blocked on {@link #get} are released.
+ * More specifically, result handler invocation <i>happens-before</i> a
+ * call to {@link #get}. <b>NOTE:</b> a result handler which attempts to
+ * call {@link #get} will deadlock.
+ * <li>Sub-classes may choose to implement specific cancellation cleanup
+ * by implementing the {@link #handleCancelRequest} method.
+ * </ul>
+ *
+ * @param <M>
+ *          The type of result returned by this completion future.
+ */
+public abstract class AbstractFutureResult<M> implements
+    FutureResult<M>, ResultHandler<M>
+{
+  @SuppressWarnings("serial")
+  private final class Sync extends AbstractQueuedSynchronizer
+  {
+    // State value representing the initial state before a result has
+    // been received.
+    private static final int WAITING = 0;
+
+    // State value representing that a result has been received and is
+    // being processed.
+    private static final int PENDING = 1;
+
+    // State value representing that the request was cancelled.
+    private static final int CANCELLED = 2;
+
+    // State value representing that the request has failed.
+    private static final int FAIL = 3;
+
+    // State value representing that the request has succeeded.
+    private static final int SUCCESS = 4;
+
+    // These do not need to be volatile since their values are published
+    // by updating the state after they are set and reading the state
+    // immediately before they are read.
+    private ErrorResultException errorResult = null;
+
+    private M result = null;
+
+
+
+    private M get0() throws ErrorResultException
+    {
+      if (errorResult != null)
+      {
+        // State must be FAILED or CANCELLED.
+        throw errorResult;
+      }
+      else
+      {
+        // State must be SUCCESS.
+        return result;
+      }
+    }
+
+
+
+    private boolean setStatePending()
+    {
+      for (;;)
+      {
+        final int s = getState();
+        if (s != WAITING)
+        {
+          return false;
+        }
+        if (compareAndSetState(s, PENDING))
+        {
+          return true;
+        }
+      }
+    }
+
+
+
+    /**
+     * Allow all threads to acquire if future has completed.
+     */
+    protected int tryAcquireShared(int ignore)
+    {
+      return innerIsDone() ? 1 : -1;
+    }
+
+
+
+    /**
+     * Signal that the future has completed and threads waiting on get()
+     * can be released.
+     */
+    protected boolean tryReleaseShared(int finalState)
+    {
+      // Ensures that errorResult/result is published.
+      setState(finalState);
+      return true;
+    }
+
+
+
+    boolean innerCancel(boolean mayInterruptIfRunning)
+    {
+      if (!setStatePending())
+      {
+        return false;
+      }
+
+      // Perform implementation defined cancellation.
+      ErrorResultException errorResult = handleCancelRequest(mayInterruptIfRunning);
+      if (errorResult == null)
+      {
+        final Result result = Responses
+            .newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
+        errorResult = ErrorResultException.wrap(result);
+      }
+      this.errorResult = errorResult;
+
+      try
+      {
+        // Invoke error result completion handler.
+        if (handler != null)
+        {
+          handler.handleErrorResult(errorResult);
+        }
+      }
+      finally
+      {
+        releaseShared(CANCELLED); // Publishes errorResult.
+      }
+
+      return true;
+    }
+
+
+
+    M innerGet() throws ErrorResultException, InterruptedException
+    {
+      acquireSharedInterruptibly(0);
+      return get0();
+    }
+
+
+
+    M innerGet(long nanosTimeout) throws ErrorResultException,
+        TimeoutException, InterruptedException
+    {
+      if (!tryAcquireSharedNanos(0, nanosTimeout))
+      {
+        throw new TimeoutException();
+      }
+      else
+      {
+        return get0();
+      }
+    }
+
+
+
+    boolean innerIsCancelled()
+    {
+      return getState() == CANCELLED;
+    }
+
+
+
+    boolean innerIsDone()
+    {
+      return getState() > 1;
+    }
+
+
+
+    void innerSetErrorResult(ErrorResultException errorResult)
+    {
+      if (setStatePending())
+      {
+        this.errorResult = errorResult;
+
+        try
+        {
+          // Invoke error result completion handler.
+          if (handler != null)
+          {
+            handler.handleErrorResult(errorResult);
+          }
+        }
+        finally
+        {
+          releaseShared(FAIL); // Publishes errorResult.
+        }
+      }
+    }
+
+
+
+    void innerSetResult(M result)
+    {
+      if (setStatePending())
+      {
+        this.result = result;
+
+        try
+        {
+          // Invoke result completion handler.
+          if (handler != null)
+          {
+            handler.handleResult(result);
+          }
+        }
+        finally
+        {
+          releaseShared(SUCCESS); // Publishes result.
+        }
+      }
+    }
+  }
+
+
+
+  private final Sync sync = new Sync();
+
+  private final ResultHandler<? super M> handler;
+
+
+
+  /**
+   * Creates a new abstract future result with the provided result
+   * handler.
+   *
+   * @param handler
+   *          A result handler which will be forwarded the result or
+   *          error when it arrives.
+   */
+  protected AbstractFutureResult(ResultHandler<? super M> handler)
+  {
+    this.handler = handler;
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean cancel(boolean mayInterruptIfRunning)
+  {
+    return sync.innerCancel(mayInterruptIfRunning);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final M get() throws ErrorResultException,
+      InterruptedException
+  {
+    return sync.innerGet();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final M get(long timeout, TimeUnit unit)
+      throws ErrorResultException, TimeoutException,
+      InterruptedException
+  {
+    return sync.innerGet(unit.toNanos(timeout));
+  }
+
+
+
+  /**
+   * Sets the error result associated with this future. If ({@code
+   * isDone() == true}) then the error result will be ignored, otherwise
+   * the result handler will be invoked if one was provided and, on
+   * return, any threads waiting on {@link #get} will be released and
+   * the provided error result will be thrown.
+   *
+   * @param errorResult
+   *          The error result.
+   */
+  public final void handleErrorResult(ErrorResultException errorResult)
+  {
+    sync.innerSetErrorResult(errorResult);
+  }
+
+
+
+  /**
+   * Sets the result associated with this future. If ({@code isDone() ==
+   * true}) then the result will be ignored, otherwise the result
+   * handler will be invoked if one was provided and, on return, any
+   * threads waiting on {@link #get} will be released and the provided
+   * result will be returned.
+   *
+   * @param result
+   *          The result.
+   */
+  public final void handleResult(M result)
+  {
+    sync.innerSetResult(result);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean isCancelled()
+  {
+    return sync.innerIsCancelled();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean isDone()
+  {
+    return sync.innerIsDone();
+  }
+
+
+
+  /**
+   * Invoked when {@link #cancel} is called and {@code isDone() ==
+   * false} and immediately before any threads waiting on {@link #get}
+   * are released. Implementations may choose to return a custom error
+   * result if needed or return {@code null} if the following default
+   * error result is acceptable:
+   *
+   * <pre>
+   * Result result = Responses
+   *     .newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED);
+   * </pre>
+   *
+   * In addition, implementations may perform other cleanup, for
+   * example, by issuing an LDAP abandon request. The default
+   * implementation is to do nothing.
+   *
+   * @param mayInterruptIfRunning
+   *          {@code true} if the thread executing executing the
+   *          response handler should be interrupted; otherwise,
+   *          in-progress response handlers are allowed to complete.
+   * @return The custom error result, or {@code null} if the default is
+   *         acceptable.
+   */
+  protected ErrorResultException handleCancelRequest(
+      boolean mayInterruptIfRunning)
+  {
+    // Do nothing by default.
+    return null;
+  }
+
+}
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/ResultTransformer.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/FutureResultTransformer.java
similarity index 60%
rename from opendj-sdk/sdk/src/com/sun/opends/sdk/util/ResultTransformer.java
rename to opendj-sdk/sdk/src/com/sun/opends/sdk/util/FutureResultTransformer.java
index 80dc5fa..04eb558 100644
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/ResultTransformer.java
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/FutureResultTransformer.java
@@ -29,62 +29,39 @@
 
 
 
-import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 import org.opends.sdk.ErrorResultException;
-import org.opends.sdk.ResultFuture;
+import org.opends.sdk.FutureResult;
 import org.opends.sdk.ResultHandler;
 
 
 
 /**
- * A base class which can be used to transform the result of an inner
- * asynchronous request to another result type.
+ * An implementation of the {@code FutureResult} interface which
+ * transforms the result of an asynchronous operation from one type to
+ * another. The implementation ensures that the transformed is computed
+ * only once.
  *
  * @param <M>
  *          The type of the inner result.
  * @param <N>
  *          The type of the outer result.
  */
-public abstract class ResultTransformer<M, N> implements
-    ResultFuture<N>, ResultHandler<M>
+public abstract class FutureResultTransformer<M, N> implements
+    FutureResult<N>, ResultHandler<M>
 {
 
   private final ResultHandler<? super N> handler;
 
-  private volatile ResultFuture<M> future = null;
+  private volatile FutureResult<M> future = null;
 
+  // These do not need to be volatile since the future acts as a memory
+  // barrier.
+  private N transformedResult = null;
 
-
-  /**
-   * Sets the inner future for this result transformer. This must be
-   * done before this future is published.
-   *
-   * @param future
-   *          The inner future.
-   */
-  public final void setResultFuture(ResultFuture<M> future)
-  {
-    this.future = future;
-  }
-
-
-
-  /**
-   * Transforms the inner result to an outer result, possibly throwing
-   * an {@code ErrorResultException} if the transformation fails for
-   * some reason.
-   *
-   * @param result
-   *          The inner result.
-   * @return The outer result.
-   * @throws ErrorResultException
-   *           If the transformation fails for some reason.
-   */
-  protected abstract N transformResult(M result)
-      throws ErrorResultException;
+  private ErrorResultException transformedErrorResult = null;
 
 
 
@@ -95,7 +72,7 @@
    * @param handler
    *          The outer result handler.
    */
-  protected ResultTransformer(ResultHandler<? super N> handler)
+  protected FutureResultTransformer(ResultHandler<? super N> handler)
   {
     this.handler = handler;
   }
@@ -105,39 +82,6 @@
   /**
    * {@inheritDoc}
    */
-  public final void handleErrorResult(ErrorResultException error)
-  {
-    if (handler != null)
-    {
-      handler.handleErrorResult(error);
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public final void handleResult(M result)
-  {
-    if (handler != null)
-    {
-      try
-      {
-        handler.handleResult(transformResult(result));
-      }
-      catch (ErrorResultException e)
-      {
-        handler.handleErrorResult(e);
-      }
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
   public final boolean cancel(boolean mayInterruptIfRunning)
   {
     return future.cancel(mayInterruptIfRunning);
@@ -149,9 +93,12 @@
    * {@inheritDoc}
    */
   public final N get() throws ErrorResultException,
-      CancellationException, InterruptedException
+      InterruptedException
   {
-    return transformResult(future.get());
+    future.get();
+
+    // The handlers are guaranteed to have been invoked at this point.
+    return get0();
   }
 
 
@@ -161,9 +108,12 @@
    */
   public final N get(long timeout, TimeUnit unit)
       throws ErrorResultException, TimeoutException,
-      CancellationException, InterruptedException
+      InterruptedException
   {
-    return transformResult(future.get(timeout, unit));
+    future.get(timeout, unit);
+
+    // The handlers are guaranteed to have been invoked at this point.
+    return get0();
   }
 
 
@@ -171,9 +121,48 @@
   /**
    * {@inheritDoc}
    */
-  public final int getMessageID()
+  public final int getRequestID()
   {
-    return future.getMessageID();
+    return future.getRequestID();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final void handleErrorResult(ErrorResultException error)
+  {
+    transformedErrorResult = transformErrorResult(error);
+    if (handler != null)
+    {
+      handler.handleErrorResult(transformedErrorResult);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final void handleResult(M result)
+  {
+    try
+    {
+      transformedResult = transformResult(result);
+      if (handler != null)
+      {
+        handler.handleResult(transformedResult);
+      }
+    }
+    catch (final ErrorResultException e)
+    {
+      transformedErrorResult = e;
+      if (handler != null)
+      {
+        handler.handleErrorResult(transformedErrorResult);
+      }
+    }
   }
 
 
@@ -196,4 +185,64 @@
     return future.isDone();
   }
 
+
+
+  /**
+   * Sets the inner future for this result transformer. This must be
+   * done before this future is published.
+   *
+   * @param future
+   *          The inner future.
+   */
+  public final void setFutureResult(FutureResult<M> future)
+  {
+    this.future = future;
+  }
+
+
+
+  private N get0() throws ErrorResultException
+  {
+    if (transformedErrorResult != null)
+    {
+      throw transformedErrorResult;
+    }
+    else
+    {
+      return transformedResult;
+    }
+  }
+
+
+
+  /**
+   * Transforms the inner error result to an outer error result. The
+   * default implementation is to return the inner error result.
+   *
+   * @param errorResult
+   *          The inner error result.
+   * @return The outer error result.
+   */
+  protected ErrorResultException transformErrorResult(
+      ErrorResultException errorResult)
+  {
+    return errorResult;
+  }
+
+
+
+  /**
+   * Transforms the inner result to an outer result, possibly throwing
+   * an {@code ErrorResultException} if the transformation fails for
+   * some reason.
+   *
+   * @param result
+   *          The inner result.
+   * @return The outer result.
+   * @throws ErrorResultException
+   *           If the transformation fails for some reason.
+   */
+  protected abstract N transformResult(M result)
+      throws ErrorResultException;
+
 }
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java
new file mode 100644
index 0000000..de2e98d
--- /dev/null
+++ b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java
@@ -0,0 +1,267 @@
+/*
+ * 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
+ *
+ *
+ *      Copyright 2009 Sun Microsystems, Inc.
+ */
+
+package com.sun.opends.sdk.util;
+
+
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.opends.sdk.ErrorResultException;
+import org.opends.sdk.FutureResult;
+import org.opends.sdk.ResultHandler;
+
+
+
+/**
+ * An implementation of the {@code FutureResult} interface which can be
+ * used to combine a sequence of two asynchronous operations into a
+ * single future result. Implementations should override the methods
+ * {@link #chainResult} and {@link #chainErrorResult} in order to define
+ * the second asynchronous operation.
+ *
+ * @param <M>
+ *          The type of the inner result.
+ * @param <N>
+ *          The type of the outer result.
+ */
+public abstract class RecursiveFutureResult<M, N> implements
+    FutureResult<N>, ResultHandler<M>
+{
+  private final class FutureResultImpl extends AbstractFutureResult<N>
+  {
+    private FutureResultImpl(ResultHandler<? super N> handler)
+    {
+      super(handler);
+    }
+
+
+
+    public int getRequestID()
+    {
+      return innerFuture.getRequestID();
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    protected ErrorResultException handleCancelRequest(
+        boolean mayInterruptIfRunning)
+    {
+      innerFuture.cancel(mayInterruptIfRunning);
+      if (outerFuture != null)
+      {
+        outerFuture.cancel(mayInterruptIfRunning);
+      }
+      return null;
+    }
+
+  }
+
+
+
+  private final FutureResultImpl impl;
+
+  private volatile FutureResult<M> innerFuture = null;
+
+  // This does not need to be volatile since the inner future acts as a
+  // memory barrier.
+  private FutureResult<N> outerFuture = null;
+
+
+
+  /**
+   * Creates a new asynchronous result chain which will chain an outer
+   * asynchronous request once the inner asynchronous request completes.
+   *
+   * @param handler
+   *          The outer result handler.
+   */
+  protected RecursiveFutureResult(ResultHandler<? super N> handler)
+  {
+    this.impl = new FutureResultImpl(handler);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean cancel(boolean mayInterruptIfRunning)
+  {
+    return impl.cancel(mayInterruptIfRunning);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final N get() throws ErrorResultException,
+      InterruptedException
+  {
+    return impl.get();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final N get(long timeout, TimeUnit unit)
+      throws ErrorResultException, TimeoutException,
+      InterruptedException
+  {
+    return impl.get(timeout, unit);
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final int getRequestID()
+  {
+    return impl.getRequestID();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final void handleErrorResult(ErrorResultException error)
+  {
+    try
+    {
+      outerFuture = chainErrorResult(error, impl);
+    }
+    catch (final ErrorResultException e)
+    {
+      impl.handleErrorResult(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final void handleResult(M result)
+  {
+    try
+    {
+      outerFuture = chainResult(result, impl);
+    }
+    catch (final ErrorResultException e)
+    {
+      impl.handleErrorResult(e);
+    }
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean isCancelled()
+  {
+    return impl.isCancelled();
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  public final boolean isDone()
+  {
+    return impl.isDone();
+  }
+
+
+
+  /**
+   * Sets the inner future for this result chain. This must be done
+   * before this future is published.
+   *
+   * @param future
+   *          The inner future.
+   */
+  public final void setFutureResult(FutureResult<M> future)
+  {
+    this.innerFuture = future;
+  }
+
+
+
+  /**
+   * Invokes the outer request based on the error result of the inner
+   * request and returns a future representing the result of the outer
+   * request.
+   * <p>
+   * The default implementation is to terminate further processing by
+   * re-throwing the inner error result.
+   *
+   * @param innerError
+   *          The error result of the inner request.
+   * @param handler
+   *          The result handler to be used for the outer request.
+   * @return A future representing the result of the outer request.
+   * @throws ErrorResultException
+   *           If the outer request could not be invoked and processing
+   *           should terminate.
+   */
+  protected FutureResult<N> chainErrorResult(
+      ErrorResultException innerError, ResultHandler<? super N> handler)
+      throws ErrorResultException
+  {
+    throw innerError;
+  }
+
+
+
+  /**
+   * Invokes the outer request based on the result of the inner request
+   * and returns a future representing the result of the outer request.
+   *
+   * @param innerResult
+   *          The result of the inner request.
+   * @param handler
+   *          The result handler to be used for the outer request.
+   * @return A future representing the result of the outer request.
+   * @throws ErrorResultException
+   *           If the outer request could not be invoked and processing
+   *           should terminate.
+   */
+  protected abstract FutureResult<N> chainResult(M innerResult,
+      ResultHandler<? super N> handler) throws ErrorResultException;
+
+}
diff --git a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/ResultChain.java b/opendj-sdk/sdk/src/com/sun/opends/sdk/util/ResultChain.java
deleted file mode 100644
index 4c78eac..0000000
--- a/opendj-sdk/sdk/src/com/sun/opends/sdk/util/ResultChain.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * 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
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- */
-
-package com.sun.opends.sdk.util;
-
-
-
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import org.opends.sdk.ErrorResultException;
-import org.opends.sdk.ResultFuture;
-import org.opends.sdk.ResultHandler;
-
-
-
-/**
- * A base class which can be used to transform the result of an inner
- * asynchronous request to another result type.
- * <p>
- * FIXME: I don't think that this is right. There's too much locking for
- * a start.
- *
- * @param <M>
- *          The type of the inner result.
- * @param <N>
- *          The type of the outer result.
- */
-public abstract class ResultChain<M, N> implements ResultFuture<N>,
-    ResultHandler<M>
-{
-
-  private ErrorResultException errorResult;
-
-  private final ResultHandler<? super N> handler;
-
-  private final Object stateLock = new Object();
-
-  private final CountDownLatch latch = new CountDownLatch(1);
-
-  private ResultFuture<N> outerFuture = null;
-
-  private boolean cancelled = false;
-
-  private volatile ResultFuture<M> innerFuture = null;
-
-
-
-  /**
-   * Creates a new asynchronous result chain which will chain an outer
-   * asynchronous request once the inner asynchronous request completes.
-   *
-   * @param handler
-   *          The outer result handler.
-   */
-  protected ResultChain(ResultHandler<? super N> handler)
-  {
-    this.handler = handler;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean cancel(boolean mayInterruptIfRunning)
-  {
-    synchronized (stateLock)
-    {
-      if (!isDone())
-      {
-        cancelled = true;
-        innerFuture.cancel(mayInterruptIfRunning);
-        if (outerFuture != null)
-        {
-          outerFuture.cancel(mayInterruptIfRunning);
-        }
-        latch.countDown();
-        return true;
-      }
-      else
-      {
-        return false;
-      }
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public N get() throws ErrorResultException, CancellationException,
-      InterruptedException
-  {
-    latch.await();
-    return get0();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public N get(long timeout, TimeUnit unit)
-      throws ErrorResultException, TimeoutException,
-      CancellationException, InterruptedException
-  {
-    if (!latch.await(timeout, unit))
-    {
-      throw new TimeoutException();
-    }
-    return get0();
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public int getMessageID()
-  {
-    // Best effort.
-    synchronized (stateLock)
-    {
-      if (outerFuture != null)
-      {
-        return outerFuture.getMessageID();
-      }
-      else
-      {
-        return innerFuture.getMessageID();
-      }
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void handleErrorResult(ErrorResultException error)
-  {
-    synchronized (stateLock)
-    {
-      try
-      {
-        outerFuture = chainErrorResult(error, handler);
-      }
-      catch (ErrorResultException e)
-      {
-        errorResult = e;
-        if (handler != null)
-        {
-          handler.handleErrorResult(errorResult);
-        }
-        latch.countDown();
-      }
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public void handleResult(M result)
-  {
-    synchronized (stateLock)
-    {
-      try
-      {
-        outerFuture = chainResult(result, handler);
-      }
-      catch (ErrorResultException e)
-      {
-        errorResult = e;
-        if (handler != null)
-        {
-          handler.handleErrorResult(errorResult);
-        }
-        latch.countDown();
-      }
-    }
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isCancelled()
-  {
-    return isDone() && cancelled;
-  }
-
-
-
-  /**
-   * {@inheritDoc}
-   */
-  public boolean isDone()
-  {
-    return latch.getCount() == 0;
-  }
-
-
-
-  /**
-   * Sets the inner future for this result chain. This must be done
-   * before this future is published.
-   *
-   * @param future
-   *          The inner future.
-   */
-  public final void setInnerResultFuture(ResultFuture<M> future)
-  {
-    innerFuture = future;
-  }
-
-
-
-  private N get0() throws ErrorResultException, CancellationException,
-      InterruptedException
-  {
-    synchronized (stateLock)
-    {
-      if (cancelled)
-      {
-        throw new CancellationException();
-      }
-
-      if (errorResult != null)
-      {
-        throw errorResult;
-      }
-
-      return outerFuture.get();
-    }
-  }
-
-
-
-  /**
-   * Invokes the outer request based on the error result of the inner
-   * request and returns a future representing the result of the outer
-   * request.
-   * <p>
-   * The default implementation is to terminate further processing by
-   * re-throwing the inner error result.
-   *
-   * @param innerError
-   *          The error result of the inner request.
-   * @param handler
-   *          The result handler to be used for the outer request.
-   * @return A future representing the result of the outer request.
-   * @throws ErrorResultException
-   *           If the outer request could not be invoked and processing
-   *           should terminate.
-   */
-  protected ResultFuture<N> chainErrorResult(
-      ErrorResultException innerError, ResultHandler<? super N> handler)
-      throws ErrorResultException
-  {
-    throw innerError;
-  }
-
-
-
-  /**
-   * Invokes the outer request based on the result of the inner request
-   * and returns a future representing the result of the outer request.
-   *
-   * @param innerResult
-   *          The result of the inner request.
-   * @param handler
-   *          The result handler to be used for the outer request.
-   * @return A future representing the result of the outer request.
-   * @throws ErrorResultException
-   *           If the outer request could not be invoked and processing
-   *           should terminate.
-   */
-  protected abstract ResultFuture<N> chainResult(M innerResult,
-      ResultHandler<? super N> handler) throws ErrorResultException;
-
-}
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
index 905afa6..65d9b30 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
@@ -32,7 +32,6 @@
 import static com.sun.opends.sdk.messages.Messages.*;
 
 import java.util.Collection;
-import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
@@ -56,7 +55,7 @@
 {
 
   private static final class SingleEntryFuture implements
-      ResultFuture<SearchResultEntry>, ResultHandler<Result>,
+      FutureResult<SearchResultEntry>, ResultHandler<Result>,
       SearchResultHandler
   {
     private final ResultHandler<? super SearchResultEntry> handler;
@@ -67,7 +66,7 @@
 
     private volatile int entryCount = 0;
 
-    private volatile ResultFuture<Result> future = null;
+    private volatile FutureResult<Result> future = null;
 
 
 
@@ -87,7 +86,7 @@
 
 
     public SearchResultEntry get() throws ErrorResultException,
-        CancellationException, InterruptedException
+        InterruptedException
     {
       future.get();
       return get0();
@@ -97,7 +96,7 @@
 
     public SearchResultEntry get(long timeout, TimeUnit unit)
         throws ErrorResultException, TimeoutException,
-        CancellationException, InterruptedException
+        InterruptedException
     {
       future.get(timeout, unit);
       return get0();
@@ -145,9 +144,9 @@
 
 
 
-    public int getMessageID()
+    public int getRequestID()
     {
-      return future.getMessageID();
+      return future.getRequestID();
     }
 
 
@@ -214,7 +213,7 @@
 
 
 
-    private void setResultFuture(ResultFuture<Result> future)
+    private void setResultFuture(FutureResult<Result> future)
     {
       this.future = future;
     }
@@ -245,7 +244,7 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<SearchResultEntry> readEntry(DN name,
+  public FutureResult<SearchResultEntry> readEntry(DN name,
       Collection<String> attributeDescriptions,
       ResultHandler<? super SearchResultEntry> handler)
       throws UnsupportedOperationException, IllegalStateException,
@@ -262,7 +261,7 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<RootDSE> readRootDSE(
+  public FutureResult<RootDSE> readRootDSE(
       ResultHandler<RootDSE> handler)
       throws UnsupportedOperationException, IllegalStateException
   {
@@ -274,7 +273,7 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<Schema> readSchema(DN name,
+  public FutureResult<Schema> readSchema(DN name,
       ResultHandler<Schema> handler)
       throws UnsupportedOperationException, IllegalStateException
   {
@@ -286,7 +285,7 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<Schema> readSchemaForEntry(DN name,
+  public FutureResult<Schema> readSchemaForEntry(DN name,
       ResultHandler<Schema> handler)
       throws UnsupportedOperationException, IllegalStateException
   {
@@ -298,14 +297,14 @@
   /**
    * {@inheritDoc}
    */
-  public ResultFuture<SearchResultEntry> searchSingleEntry(
+  public FutureResult<SearchResultEntry> searchSingleEntry(
       SearchRequest request,
       ResultHandler<? super SearchResultEntry> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
     final SingleEntryFuture innerFuture = new SingleEntryFuture(handler);
-    final ResultFuture<Result> future = search(request, innerFuture,
+    final FutureResult<Result> future = search(request, innerFuture,
         innerFuture);
     innerFuture.setResultFuture(future);
     return innerFuture;
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
index ea77f1a..a5de76f 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
@@ -59,8 +59,8 @@
   /**
    * {@inheritDoc}
    */
-  public abstract ConnectionFuture<? extends C> getAsynchronousConnection(
-      ConnectionResultHandler<? super C> handler);
+  public abstract FutureResult<? extends C> getAsynchronousConnection(
+      ResultHandler<? super C> handler);
 
 
 
@@ -104,7 +104,7 @@
   protected final C blockingGetAsynchronousConnection()
       throws ErrorResultException
   {
-    ConnectionFuture<? extends C> future = getAsynchronousConnection(null);
+    FutureResult<? extends C> future = getAsynchronousConnection(null);
     try
     {
       return future.get();
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java
index a1ea6fb..00d2ba0 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AsynchronousConnection.java
@@ -50,10 +50,10 @@
  * <h3>Operation processing</h3>
  * <p>
  * All operations are performed asynchronously and return a
- * {@link ResultFuture} or sub-type thereof which can be used for
- * retrieving the result using the {@link ResultFuture#get} method.
+ * {@link FutureResult} or sub-type thereof which can be used for
+ * retrieving the result using the {@link FutureResult#get} method.
  * Operation failures, for whatever reason, are signalled by the
- * {@link ResultFuture#get()} method throwing an
+ * {@link FutureResult#get()} method throwing an
  * {@link ErrorResultException}.
  * <p>
  * Synchronous operations are easily simulated by immediately getting
@@ -75,9 +75,9 @@
  * Connection connection2 = ...;
  * AddRequest request = ...;
  * // Add the entry to the first server (don't block).
- * ResultFuture future1 = connection1.add(request);
+ * FutureResult future1 = connection1.add(request);
  * // Add the entry to the second server (in parallel).
- * ResultFuture future2 = connection2.add(request);
+ * FutureResult future2 = connection2.add(request);
  * // Total time = is O(1) instead of O(n).
  * future1.get();
  * future2.get();
@@ -142,7 +142,7 @@
    * abandon request.
    * <p>
    * <b>Note:</b> a more convenient approach to abandoning unfinished
-   * operations is provided via the {@link ResultFuture#cancel(boolean)}
+   * operations is provided via the {@link FutureResult#cancel(boolean)}
    * method.
    *
    * @param request
@@ -180,7 +180,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  ResultFuture<Result> add(AddRequest request,
+  FutureResult<Result> add(AddRequest request,
       ResultHandler<Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -225,7 +225,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  ResultFuture<BindResult> bind(BindRequest request,
+  FutureResult<BindResult> bind(BindRequest request,
       ResultHandler<? super BindResult> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -302,7 +302,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  ResultFuture<CompareResult> compare(CompareRequest request,
+  FutureResult<CompareResult> compare(CompareRequest request,
       ResultHandler<? super CompareResult> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -328,7 +328,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  ResultFuture<Result> delete(DeleteRequest request,
+  FutureResult<Result> delete(DeleteRequest request,
       ResultHandler<Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -356,7 +356,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  <R extends Result> ResultFuture<R> extendedRequest(
+  <R extends Result> FutureResult<R> extendedRequest(
       ExtendedRequest<R> request, ResultHandler<? super R> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -395,7 +395,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  ResultFuture<Result> modify(ModifyRequest request,
+  FutureResult<Result> modify(ModifyRequest request,
       ResultHandler<Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -421,7 +421,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  ResultFuture<Result> modifyDN(ModifyDNRequest request,
+  FutureResult<Result> modifyDN(ModifyDNRequest request,
       ResultHandler<Result> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException;
@@ -462,7 +462,7 @@
    * @throws NullPointerException
    *           If the {@code name} was {@code null}.
    */
-  ResultFuture<SearchResultEntry> readEntry(DN name,
+  FutureResult<SearchResultEntry> readEntry(DN name,
       Collection<String> attributeDescriptions,
       ResultHandler<? super SearchResultEntry> handler)
       throws UnsupportedOperationException, IllegalStateException,
@@ -488,7 +488,7 @@
    *           If this connection has already been closed, i.e. if
    *           {@code isClosed() == true}.
    */
-  ResultFuture<RootDSE> readRootDSE(ResultHandler<RootDSE> handler)
+  FutureResult<RootDSE> readRootDSE(ResultHandler<RootDSE> handler)
       throws UnsupportedOperationException, IllegalStateException;
 
 
@@ -518,7 +518,7 @@
    *           If this connection has already been closed, i.e. if
    *           {@code isClosed() == true}.
    */
-  ResultFuture<Schema> readSchema(DN name, ResultHandler<Schema> handler)
+  FutureResult<Schema> readSchema(DN name, ResultHandler<Schema> handler)
       throws UnsupportedOperationException, IllegalStateException;
 
 
@@ -551,7 +551,7 @@
    *           If this connection has already been closed, i.e. if
    *           {@code isClosed() == true}.
    */
-  ResultFuture<Schema> readSchemaForEntry(DN name,
+  FutureResult<Schema> readSchemaForEntry(DN name,
       ResultHandler<Schema> handler)
       throws UnsupportedOperationException, IllegalStateException;
 
@@ -596,7 +596,7 @@
    * @throws NullPointerException
    *           If {@code request} was {@code null}.
    */
-  ResultFuture<Result> search(SearchRequest request,
+  FutureResult<Result> search(SearchRequest request,
       ResultHandler<Result> resultHandler,
       SearchResultHandler searchResulthandler)
       throws UnsupportedOperationException, IllegalStateException,
@@ -630,7 +630,7 @@
    * @throws NullPointerException
    *           If the {@code request} was {@code null}.
    */
-  ResultFuture<SearchResultEntry> searchSingleEntry(
+  FutureResult<SearchResultEntry> searchSingleEntry(
       SearchRequest request,
       ResultHandler<? super SearchResultEntry> handler)
       throws UnsupportedOperationException, IllegalStateException,
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
index 3dbf42d..67f43d5 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
@@ -105,15 +105,15 @@
     /**
      * {@inheritDoc}
      */
-    public ConnectionFuture<AuthenticatedAsynchronousConnection> getAsynchronousConnection(
-        ConnectionResultHandler<? super AuthenticatedAsynchronousConnection> handler)
+    public FutureResult<AuthenticatedAsynchronousConnection> getAsynchronousConnection(
+        ResultHandler<? super AuthenticatedAsynchronousConnection> handler)
     {
       // TODO: bug here? if allowRebind= false then bind will never
       // happen
-      ConnectionFutureImpl future = new ConnectionFutureImpl(
+      ResultFutureImpl future = new ResultFutureImpl(
           allowRebinds ? request : null, handler);
       future.connectFuture = parentFactory
-          .getAsynchronousConnection(future);
+          .getAsynchronousConnection(future.connectionHandler);
       return future;
     }
 
@@ -279,7 +279,7 @@
 
 
 
-    public ResultFuture<Result> add(AddRequest request,
+    public FutureResult<Result> add(AddRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -303,7 +303,7 @@
      * connections. This method will always throw {@code
      * UnsupportedOperationException}.
      */
-    public ResultFuture<BindResult> bind(BindRequest request,
+    public FutureResult<BindResult> bind(BindRequest request,
         ResultHandler<? super BindResult> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -328,7 +328,7 @@
 
 
 
-    public ResultFuture<CompareResult> compare(CompareRequest request,
+    public FutureResult<CompareResult> compare(CompareRequest request,
         ResultHandler<? super CompareResult> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -338,7 +338,7 @@
 
 
 
-    public ResultFuture<Result> delete(DeleteRequest request,
+    public FutureResult<Result> delete(DeleteRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -348,7 +348,7 @@
 
 
 
-    public <R extends Result> ResultFuture<R> extendedRequest(
+    public <R extends Result> FutureResult<R> extendedRequest(
         ExtendedRequest<R> request, ResultHandler<? super R> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -358,7 +358,7 @@
 
 
 
-    public ResultFuture<Result> modify(ModifyRequest request,
+    public FutureResult<Result> modify(ModifyRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -368,7 +368,7 @@
 
 
 
-    public ResultFuture<Result> modifyDN(ModifyDNRequest request,
+    public FutureResult<Result> modifyDN(ModifyDNRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -394,7 +394,7 @@
      *           If this connection has already been closed, i.e. if
      *           {@code isClosed() == true}.
      */
-    public ResultFuture<BindResult> rebind(
+    public FutureResult<BindResult> rebind(
         ResultHandler<? super BindResult> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -450,7 +450,7 @@
 
 
 
-    public ResultFuture<Result> search(SearchRequest request,
+    public FutureResult<Result> search(SearchRequest request,
         ResultHandler<Result> resultHandler,
         SearchResultHandler searchResulthandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -475,7 +475,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<RootDSE> readRootDSE(
+    public FutureResult<RootDSE> readRootDSE(
         ResultHandler<RootDSE> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -487,7 +487,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<SearchResultEntry> readEntry(DN name,
+    public FutureResult<SearchResultEntry> readEntry(DN name,
         Collection<String> attributeDescriptions,
         ResultHandler<? super SearchResultEntry> resultHandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -502,7 +502,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<SearchResultEntry> searchSingleEntry(
+    public FutureResult<SearchResultEntry> searchSingleEntry(
         SearchRequest request,
         ResultHandler<? super SearchResultEntry> resultHandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -516,7 +516,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<Schema> readSchemaForEntry(DN name,
+    public FutureResult<Schema> readSchemaForEntry(DN name,
         ResultHandler<Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -528,7 +528,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<Schema> readSchema(DN name,
+    public FutureResult<Schema> readSchema(DN name,
         ResultHandler<Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -560,24 +560,86 @@
 
 
 
-  private static final class ConnectionFutureImpl implements
-      ConnectionFuture<AuthenticatedAsynchronousConnection>,
-      ConnectionResultHandler<AsynchronousConnection>,
-      ResultHandler<BindResult>
+  private static final class ResultFutureImpl implements
+      FutureResult<AuthenticatedAsynchronousConnection>
   {
+    private final class ConnectionResultHandler implements
+        ResultHandler<AsynchronousConnection>
+    {
+      public void handleResult(AsynchronousConnection conn)
+      {
+        connection = conn;
+        bindFuture = connection.bind(request, bindHandler);
+      }
+
+
+
+      public void handleErrorResult(ErrorResultException error)
+      {
+        exception = error;
+        latch.countDown();
+      }
+    }
+
+
+
+    private final class BindRequestResultHandler implements
+        ResultHandler<BindResult>
+    {
+      public void handleResult(BindResult result)
+      {
+        // FIXME: should make the result unmodifiable.
+        authenticatedConnection = new AuthenticatedAsynchronousConnection(
+            connection, request, result);
+        latch.countDown();
+        if (handler != null)
+        {
+          handler.handleResult(authenticatedConnection);
+        }
+      }
+
+
+
+      public void handleErrorResult(ErrorResultException error)
+      {
+        // Ensure that the connection is closed.
+        try
+        {
+          connection.close();
+        }
+        catch (Exception e)
+        {
+          // Ignore.
+        }
+
+        exception = error;
+        latch.countDown();
+        if (handler != null)
+        {
+          handler.handleErrorResult(exception);
+        }
+      }
+    }
+
+
+
+    private final ResultHandler<BindResult> bindHandler = new BindRequestResultHandler();
+
+    private final ResultHandler<AsynchronousConnection> connectionHandler = new ConnectionResultHandler();
+
     private volatile AuthenticatedAsynchronousConnection authenticatedConnection;
 
     private volatile AsynchronousConnection connection;
 
     private volatile ErrorResultException exception;
 
-    private volatile ConnectionFuture<?> connectFuture;
+    private volatile FutureResult<?> connectFuture;
 
-    private volatile ResultFuture<BindResult> bindFuture;
+    private volatile FutureResult<BindResult> bindFuture;
 
     private final CountDownLatch latch = new CountDownLatch(1);
 
-    private final ConnectionResultHandler<? super AuthenticatedAsynchronousConnection> handler;
+    private final ResultHandler<? super AuthenticatedAsynchronousConnection> handler;
 
     private boolean cancelled;
 
@@ -585,9 +647,9 @@
 
 
 
-    private ConnectionFutureImpl(
+    private ResultFutureImpl(
         BindRequest request,
-        ConnectionResultHandler<? super AuthenticatedAsynchronousConnection> handler)
+        ResultHandler<? super AuthenticatedAsynchronousConnection> handler)
     {
       this.request = request;
       this.handler = handler;
@@ -658,55 +720,11 @@
 
 
 
-    public void handleConnection(AsynchronousConnection connection)
+    public int getRequestID()
     {
-      this.connection = connection;
-      this.bindFuture = this.connection.bind(request, this);
+      return -1;
     }
 
-
-
-    public void handleConnectionError(ErrorResultException error)
-    {
-      exception = error;
-      latch.countDown();
-    }
-
-
-
-    public void handleResult(BindResult result)
-    {
-      // FIXME: should make the result unmodifiable.
-      authenticatedConnection = new AuthenticatedAsynchronousConnection(
-          connection, request, result);
-      latch.countDown();
-      if (handler != null)
-      {
-        handler.handleConnection(authenticatedConnection);
-      }
-    }
-
-
-
-    public void handleErrorResult(ErrorResultException error)
-    {
-      // Ensure that the connection is closed.
-      try
-      {
-        connection.close();
-      }
-      catch (Exception e)
-      {
-        // Ignore.
-      }
-
-      exception = error;
-      latch.countDown();
-      if (handler != null)
-      {
-        handler.handleConnectionError(exception);
-      }
-    }
   }
 
 
@@ -751,8 +769,8 @@
 
 
 
-  public ConnectionFuture<AuthenticatedAsynchronousConnection> getAsynchronousConnection(
-      ConnectionResultHandler<? super AuthenticatedAsynchronousConnection> handler)
+  public FutureResult<AuthenticatedAsynchronousConnection> getAsynchronousConnection(
+      ResultHandler<? super AuthenticatedAsynchronousConnection> handler)
   {
     return impl.getAsynchronousConnection(handler);
   }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java
index ed345b9..a9c55cb 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFactory.java
@@ -72,9 +72,9 @@
   /**
    * Initiates an asynchronous connection request to the Directory
    * Server associated with this connection factory. The returned
-   * {@code ConnectionFuture} can be used to retrieve the completed
+   * {@code FutureResult} can be used to retrieve the completed
    * asynchronous connection. Alternatively, if a {@code
-   * ConnectionResultHandler} is provided, the handler will be notified
+   * ResultHandler} is provided, the handler will be notified
    * when the connection is available and ready for use.
    *
    * @param handler
@@ -83,6 +83,6 @@
    * @return A future which can be used to retrieve the asynchronous
    *         connection.
    */
-  ConnectionFuture<? extends C> getAsynchronousConnection(
-      ConnectionResultHandler<? super C> handler);
+  FutureResult<? extends C> getAsynchronousConnection(
+      ResultHandler<? super C> handler);
 }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFuture.java b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFuture.java
deleted file mode 100644
index 517f68c..0000000
--- a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionFuture.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- */
-
-package org.opends.sdk;
-
-
-
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-
-
-/**
- * A handle which can be used to retrieve a requested {@code
- * AsynchronousConnection}.
- * <p>
- * TODO: Do we want to throw an ErrorResultException? I think we do
- * because exceptions are not limited to connection related errors. For
- * example, a transacted connection would already have a physical
- * connection; an error could occur when sending the start txn extended
- * op.
- *
- * @param <C>
- *          The type of asynchronous connection returned by this
- *          connection future.
- */
-public interface ConnectionFuture<C extends AsynchronousConnection>
-    extends Future<C>
-{
-  /**
-   * Attempts to cancel the request. This attempt will fail if the
-   * request has already completed or has already been cancelled. If
-   * successful, then cancellation results in an abandon or cancel
-   * request (if configured) being sent to the server.
-   * <p>
-   * After this method returns, subsequent calls to {@link #isDone} will
-   * always return {@code true}. Subsequent calls to
-   * {@link #isCancelled} will always return {@code true} if this method
-   * returned {@code true}.
-   *
-   * @param mayInterruptIfRunning
-   *          {@code true} if the thread executing executing the
-   *          response handler should be interrupted; otherwise,
-   *          in-progress response handlers are allowed to complete.
-   * @return {@code false} if the request could not be cancelled,
-   *         typically because it has already completed normally;
-   *         {@code true} otherwise.
-   */
-  boolean cancel(boolean mayInterruptIfRunning);
-
-
-
-  /**
-   * Waits if necessary for the connection request to complete, and then
-   * returns the asynchronous connection if the connection request
-   * succeeded. If the connection request failed (i.e. a non-successful
-   * result code was obtained) then a {@link ErrorResultException} is
-   * thrown.
-   *
-   * @return The asynchronous connection, but only if the the connection
-   *         request succeeded.
-   * @throws CancellationException
-   *           If the connection request was cancelled using a call to
-   *           {@link #cancel}.
-   * @throws ErrorResultException
-   *           If the connection request failed for some reason.
-   * @throws InterruptedException
-   *           If the current thread was interrupted while waiting.
-   */
-  C get() throws InterruptedException, ErrorResultException;
-
-
-
-  /**
-   * Waits if necessary for at most the given time for the connection
-   * request to complete, and then returns the asynchronous connection
-   * if the connection request succeeded. If the connection request
-   * failed (i.e. a non-successful result code was obtained) then a
-   * {@link ErrorResultException} is thrown.
-   *
-   * @param timeout
-   *          The maximum time to wait.
-   * @param unit
-   *          The time unit of the timeout argument.
-   * @return The asynchronous connection, but only if the the connection
-   *         request succeeded.
-   * @throws CancellationException
-   *           If the connection request was cancelled using a call to
-   *           {@link #cancel}.
-   * @throws ErrorResultException
-   *           If the connection request failed for some reason.
-   * @throws InterruptedException
-   *           If the current thread was interrupted while waiting.
-   * @throws TimeoutException
-   *           If the wait timed out.
-   */
-  C get(long timeout, TimeUnit unit) throws InterruptedException,
-      TimeoutException, ErrorResultException;
-
-
-
-  /**
-   * Returns {@code true} if the connection request was cancelled before
-   * it completed normally.
-   *
-   * @return {@code true} if the connection request was cancelled before
-   *         it completed normally, otherwise {@code false}.
-   */
-  boolean isCancelled();
-
-
-
-  /**
-   * Returns {@code true} if the connection request has completed.
-   * <p>
-   * Completion may be due to normal termination, an exception, or
-   * cancellation. In all of these cases, this method will return
-   * {@code true}.
-   *
-   * @return {@code true} if the connection request has completed,
-   *         otherwise {@code false}.
-   */
-  boolean isDone();
-}
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java
index 5bf2dff..73aad16 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java
@@ -60,7 +60,7 @@
   // FIXME: should use a better collection than this - CLQ?
   private final Stack<AsynchronousConnection> pool;
 
-  private final ConcurrentLinkedQueue<PendingConnectionFuture> pendingFutures;
+  private final ConcurrentLinkedQueue<PendingResultFuture> pendingFutures;
 
   private final Object lock = new Object();
 
@@ -94,7 +94,7 @@
 
 
 
-    public ResultFuture<Result> add(AddRequest request,
+    public FutureResult<Result> add(AddRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -108,7 +108,7 @@
 
 
 
-    public ResultFuture<BindResult> bind(BindRequest request,
+    public FutureResult<BindResult> bind(BindRequest request,
         ResultHandler<? super BindResult> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -145,7 +145,7 @@
           }
 
           // See if there waiters pending
-          PendingConnectionFuture future = pendingFutures.poll();
+          PendingResultFuture future = pendingFutures.poll();
           if (future != null)
           {
             PooledConnectionWapper pooledConnection = new PooledConnectionWapper(
@@ -195,7 +195,7 @@
 
 
 
-    public ResultFuture<CompareResult> compare(CompareRequest request,
+    public FutureResult<CompareResult> compare(CompareRequest request,
         ResultHandler<? super CompareResult> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -209,7 +209,7 @@
 
 
 
-    public ResultFuture<Result> delete(DeleteRequest request,
+    public FutureResult<Result> delete(DeleteRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -223,7 +223,7 @@
 
 
 
-    public <R extends Result> ResultFuture<R> extendedRequest(
+    public <R extends Result> FutureResult<R> extendedRequest(
         ExtendedRequest<R> request, ResultHandler<? super R> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -237,7 +237,7 @@
 
 
 
-    public ResultFuture<Result> modify(ModifyRequest request,
+    public FutureResult<Result> modify(ModifyRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -251,7 +251,7 @@
 
 
 
-    public ResultFuture<Result> modifyDN(ModifyDNRequest request,
+    public FutureResult<Result> modifyDN(ModifyDNRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -265,7 +265,7 @@
 
 
 
-    public ResultFuture<Result> search(SearchRequest request,
+    public FutureResult<Result> search(SearchRequest request,
         ResultHandler<Result> resultHandler,
         SearchResultHandler searchResulthandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -284,7 +284,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<SearchResultEntry> readEntry(DN name,
+    public FutureResult<SearchResultEntry> readEntry(DN name,
         Collection<String> attributeDescriptions,
         ResultHandler<? super SearchResultEntry> resultHandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -303,7 +303,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<SearchResultEntry> searchSingleEntry(
+    public FutureResult<SearchResultEntry> searchSingleEntry(
         SearchRequest request,
         ResultHandler<? super SearchResultEntry> resultHandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -321,7 +321,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<RootDSE> readRootDSE(
+    public FutureResult<RootDSE> readRootDSE(
         ResultHandler<RootDSE> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -337,7 +337,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<Schema> readSchemaForEntry(DN name,
+    public FutureResult<Schema> readSchemaForEntry(DN name,
         ResultHandler<Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -353,7 +353,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<Schema> readSchema(DN name,
+    public FutureResult<Schema> readSchema(DN name,
         ResultHandler<Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -438,14 +438,14 @@
 
 
 
-  private static final class CompletedConnectionFuture implements
-      ConnectionFuture<AsynchronousConnection>
+  private static final class CompletedResultFuture implements
+      FutureResult<AsynchronousConnection>
   {
     private final PooledConnectionWapper connection;
 
 
 
-    private CompletedConnectionFuture(PooledConnectionWapper connection)
+    private CompletedResultFuture(PooledConnectionWapper connection)
     {
       this.connection = connection;
     }
@@ -487,12 +487,19 @@
     {
       return true;
     }
+
+
+
+    public int getRequestID()
+    {
+      return -1;
+    }
   }
 
 
 
-  private final class PendingConnectionFuture implements
-      ConnectionFuture<AsynchronousConnection>
+  private final class PendingResultFuture implements
+      FutureResult<AsynchronousConnection>
   {
     private volatile boolean isCancelled;
 
@@ -500,14 +507,14 @@
 
     private volatile ErrorResultException err;
 
-    private final ConnectionResultHandler<? super AsynchronousConnection> handler;
+    private final ResultHandler<? super AsynchronousConnection> handler;
 
     private final CountDownLatch latch = new CountDownLatch(1);
 
 
 
-    private PendingConnectionFuture(
-        ConnectionResultHandler<? super AsynchronousConnection> handler)
+    private PendingResultFuture(
+        ResultHandler<? super AsynchronousConnection> handler)
     {
       this.handler = handler;
     }
@@ -562,12 +569,19 @@
 
 
 
+    public int getRequestID()
+    {
+      return -1;
+    }
+
+
+
     private void connection(PooledConnectionWapper connection)
     {
       this.connection = connection;
       if (handler != null)
       {
-        handler.handleConnection(connection);
+        handler.handleResult(connection);
       }
       latch.countDown();
     }
@@ -579,7 +593,7 @@
       this.err = e;
       if (handler != null)
       {
-        handler.handleConnectionError(e);
+        handler.handleErrorResult(e);
       }
       latch.countDown();
     }
@@ -603,26 +617,26 @@
     this.connectionFactory = connectionFactory;
     this.poolSize = poolSize;
     this.pool = new Stack<AsynchronousConnection>();
-    this.pendingFutures = new ConcurrentLinkedQueue<PendingConnectionFuture>();
+    this.pendingFutures = new ConcurrentLinkedQueue<PendingResultFuture>();
   }
 
 
 
-  private final class WrapConnectionResultHandler implements
-      ConnectionResultHandler<AsynchronousConnection>
+  private final class WrapResultHandler implements
+      ResultHandler<AsynchronousConnection>
   {
-    private final PendingConnectionFuture future;
+    private final PendingResultFuture future;
 
 
 
-    private WrapConnectionResultHandler(PendingConnectionFuture future)
+    private WrapResultHandler(PendingResultFuture future)
     {
       this.future = future;
     }
 
 
 
-    public void handleConnection(AsynchronousConnection connection)
+    public void handleResult(AsynchronousConnection connection)
     {
       PooledConnectionWapper pooledConnection = new PooledConnectionWapper(
           connection);
@@ -631,7 +645,7 @@
 
 
 
-    public void handleConnectionError(ErrorResultException error)
+    public void handleErrorResult(ErrorResultException error)
     {
       future.error(error);
     }
@@ -639,8 +653,8 @@
 
 
 
-  public ConnectionFuture<AsynchronousConnection> getAsynchronousConnection(
-      ConnectionResultHandler<? super AsynchronousConnection> handler)
+  public FutureResult<AsynchronousConnection> getAsynchronousConnection(
+      ResultHandler<? super AsynchronousConnection> handler)
   {
     synchronized (lock)
     {
@@ -663,19 +677,19 @@
             conn);
         if (handler != null)
         {
-          handler.handleConnection(pooledConnection);
+          handler.handleResult(pooledConnection);
         }
-        return new CompletedConnectionFuture(pooledConnection);
+        return new CompletedResultFuture(pooledConnection);
       }
 
-      PendingConnectionFuture pendingFuture = new PendingConnectionFuture(
+      PendingResultFuture pendingFuture = new PendingResultFuture(
           handler);
       // Pool was empty. Maybe a new connection if pool size is not
       // reached
       if (numConnections < poolSize)
       {
         numConnections++;
-        WrapConnectionResultHandler wrapHandler = new WrapConnectionResultHandler(
+        WrapResultHandler wrapHandler = new WrapResultHandler(
             pendingFuture);
         connectionFactory.getAsynchronousConnection(wrapHandler);
         if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE))
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionResultHandler.java b/opendj-sdk/sdk/src/org/opends/sdk/ConnectionResultHandler.java
deleted file mode 100644
index 3e9d56e..0000000
--- a/opendj-sdk/sdk/src/org/opends/sdk/ConnectionResultHandler.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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
- *
- *
- *      Copyright 2009 Sun Microsystems, Inc.
- */
-
-package org.opends.sdk;
-
-/**
- * A completion handler which is notified when an asynchronous
- * connection attempt has completed.
- * <p>
- * {@link ConnectionFactory} objects allow a connection result
- * completion handler to be specified when attempting to connect to a
- * Directory Server. The {@link #handleConnection} method is invoked
- * when the operation completes successfully. The
- * {@link #handleConnectionError} method is invoked if the operations
- * fails.
- * <p>
- * Implementations of these methods should complete in a timely manner
- * so as to avoid keeping the invoking thread from dispatching to other
- * completion handlers.
- *
- * @param <C>
- *          The type of asynchronous connection handled by this
- *          connection result handler.
- */
-public interface ConnectionResultHandler<C extends AsynchronousConnection>
-{
-  /**
-   * Invoked when the asynchronous connection has completed
-   * successfully.
-   *
-   * @param connection
-   *          The connection which can be used to interact with the
-   *          Directory Server.
-   */
-  void handleConnection(C connection);
-
-
-
-  /**
-   * Invoked when the asynchronous connection attempt has failed.
-   *
-   * @param error
-   *          The error result exception indicating why the asynchronous
-   *          connection attempt has failed.
-   */
-  void handleConnectionError(ErrorResultException error);
-}
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ResultFuture.java b/opendj-sdk/sdk/src/org/opends/sdk/FutureResult.java
similarity index 87%
rename from opendj-sdk/sdk/src/org/opends/sdk/ResultFuture.java
rename to opendj-sdk/sdk/src/org/opends/sdk/FutureResult.java
index e3a933f..3b9ff34 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ResultFuture.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/FutureResult.java
@@ -29,7 +29,6 @@
 
 
 
-import java.util.concurrent.CancellationException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -43,7 +42,7 @@
  * @param <S>
  *          The type of result returned by this future.
  */
-public interface ResultFuture<S> extends Future<S>
+public interface FutureResult<S> extends Future<S>
 {
   /**
    * Attempts to cancel the request. This attempt will fail if the
@@ -79,14 +78,10 @@
    * @throws ErrorResultException
    *           If the result code indicates that the request failed for
    *           some reason.
-   * @throws CancellationException
-   *           If the request was cancelled using a call to
-   *           {@link #cancel}.
    * @throws InterruptedException
    *           If the current thread was interrupted while waiting.
    */
-  S get() throws ErrorResultException, CancellationException,
-      InterruptedException;
+  S get() throws ErrorResultException, InterruptedException;
 
 
 
@@ -107,23 +102,20 @@
    *           some reason.
    * @throws TimeoutException
    *           If the wait timed out.
-   * @throws CancellationException
-   *           If the request was cancelled using a call to
-   *           {@link #cancel}.
    * @throws InterruptedException
    *           If the current thread was interrupted while waiting.
    */
   S get(long timeout, TimeUnit unit) throws ErrorResultException,
-      TimeoutException, CancellationException, InterruptedException;
+      TimeoutException, InterruptedException;
 
 
 
   /**
-   * Returns the message ID of the request.
+   * Returns the request ID of the request if appropriate.
    *
-   * @return The message ID.
+   * @return The request ID, or {@code -1} if there is no request ID.
    */
-  int getMessageID();
+  int getRequestID();
 
 
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
index 3fb12f3..619f880 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
@@ -158,7 +158,7 @@
 
 
 
-    public ResultFuture<Result> add(AddRequest request,
+    public FutureResult<Result> add(AddRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -168,7 +168,7 @@
 
 
 
-    public ResultFuture<BindResult> bind(BindRequest request,
+    public FutureResult<BindResult> bind(BindRequest request,
         ResultHandler<? super BindResult> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -203,7 +203,7 @@
 
 
 
-    public ResultFuture<CompareResult> compare(CompareRequest request,
+    public FutureResult<CompareResult> compare(CompareRequest request,
         ResultHandler<? super CompareResult> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -213,7 +213,7 @@
 
 
 
-    public ResultFuture<Result> delete(DeleteRequest request,
+    public FutureResult<Result> delete(DeleteRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -223,7 +223,7 @@
 
 
 
-    public <R extends Result> ResultFuture<R> extendedRequest(
+    public <R extends Result> FutureResult<R> extendedRequest(
         ExtendedRequest<R> request, ResultHandler<? super R> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -233,7 +233,7 @@
 
 
 
-    public ResultFuture<Result> modify(ModifyRequest request,
+    public FutureResult<Result> modify(ModifyRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -243,7 +243,7 @@
 
 
 
-    public ResultFuture<Result> modifyDN(ModifyDNRequest request,
+    public FutureResult<Result> modifyDN(ModifyDNRequest request,
         ResultHandler<Result> handler)
         throws UnsupportedOperationException, IllegalStateException,
         NullPointerException
@@ -253,7 +253,7 @@
 
 
 
-    public ResultFuture<Result> search(SearchRequest request,
+    public FutureResult<Result> search(SearchRequest request,
         ResultHandler<Result> resultHandler,
         SearchResultHandler searchResultHandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -268,7 +268,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<SearchResultEntry> readEntry(DN name,
+    public FutureResult<SearchResultEntry> readEntry(DN name,
         Collection<String> attributeDescriptions,
         ResultHandler<? super SearchResultEntry> resultHandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -283,7 +283,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<SearchResultEntry> searchSingleEntry(
+    public FutureResult<SearchResultEntry> searchSingleEntry(
         SearchRequest request,
         ResultHandler<? super SearchResultEntry> resultHandler)
         throws UnsupportedOperationException, IllegalStateException,
@@ -297,7 +297,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<RootDSE> readRootDSE(
+    public FutureResult<RootDSE> readRootDSE(
         ResultHandler<RootDSE> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -309,7 +309,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<Schema> readSchemaForEntry(DN name,
+    public FutureResult<Schema> readSchemaForEntry(DN name,
         ResultHandler<Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -321,7 +321,7 @@
     /**
      * {@inheritDoc}
      */
-    public ResultFuture<Schema> readSchema(DN name,
+    public FutureResult<Schema> readSchema(DN name,
         ResultHandler<Schema> handler)
         throws UnsupportedOperationException, IllegalStateException
     {
@@ -437,26 +437,26 @@
 
 
 
-  private final class ConnectionFutureImpl implements
-      ConnectionFuture<AsynchronousConnection>,
-      ConnectionResultHandler<AsynchronousConnection>
+  private final class ResultFutureImpl implements
+      FutureResult<AsynchronousConnection>,
+      ResultHandler<AsynchronousConnection>
   {
     private volatile AsynchronousConnectionImpl heartBeatConnection;
 
     private volatile ErrorResultException exception;
 
-    private volatile ConnectionFuture<?> connectFuture;
+    private volatile FutureResult<?> connectFuture;
 
     private final CountDownLatch latch = new CountDownLatch(1);
 
-    private final ConnectionResultHandler<? super AsynchronousConnectionImpl> handler;
+    private final ResultHandler<? super AsynchronousConnectionImpl> handler;
 
     private boolean cancelled;
 
 
 
-    private ConnectionFutureImpl(
-        ConnectionResultHandler<? super AsynchronousConnectionImpl> handler)
+    private ResultFutureImpl(
+        ResultHandler<? super AsynchronousConnectionImpl> handler)
     {
       this.handler = handler;
     }
@@ -524,7 +524,14 @@
 
 
 
-    public void handleConnection(AsynchronousConnection connection)
+    public int getRequestID()
+    {
+      return -1;
+    }
+
+
+
+    public void handleResult(AsynchronousConnection connection)
     {
       heartBeatConnection = new AsynchronousConnectionImpl(connection);
       synchronized (activeConnections)
@@ -534,19 +541,19 @@
       }
       if (handler != null)
       {
-        handler.handleConnection(heartBeatConnection);
+        handler.handleResult(heartBeatConnection);
       }
       latch.countDown();
     }
 
 
 
-    public void handleConnectionError(ErrorResultException error)
+    public void handleErrorResult(ErrorResultException error)
     {
       exception = error;
       if (handler != null)
       {
-        handler.handleConnectionError(error);
+        handler.handleErrorResult(error);
       }
       latch.countDown();
     }
@@ -554,10 +561,10 @@
 
 
 
-  public ConnectionFuture<AsynchronousConnection> getAsynchronousConnection(
-      ConnectionResultHandler<? super AsynchronousConnection> handler)
+  public FutureResult<AsynchronousConnection> getAsynchronousConnection(
+      ResultHandler<? super AsynchronousConnection> handler)
   {
-    ConnectionFutureImpl future = new ConnectionFutureImpl(handler);
+    ResultFutureImpl future = new ResultFutureImpl(handler);
     future.connectFuture = parentFactory
         .getAsynchronousConnection(future);
     return future;
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java b/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java
index 1a32f78..613a8b9 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/RootDSE.java
@@ -156,14 +156,14 @@
    * @throws NullPointerException
    *           If the {@code connection} was {@code null}.
    */
-  public static ResultFuture<RootDSE> readRootDSE(
+  public static FutureResult<RootDSE> readRootDSE(
       AsynchronousConnection connection,
       ResultHandler<RootDSE> handler)
       throws UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    final ResultTransformer<SearchResultEntry, RootDSE> future =
-      new ResultTransformer<SearchResultEntry, RootDSE>(handler)
+    final FutureResultTransformer<SearchResultEntry, RootDSE> future =
+      new FutureResultTransformer<SearchResultEntry, RootDSE>(handler)
     {
 
       protected RootDSE transformResult(SearchResultEntry result)
@@ -174,9 +174,9 @@
 
     };
 
-    ResultFuture<SearchResultEntry> innerFuture = connection
+    FutureResult<SearchResultEntry> innerFuture = connection
         .searchSingleEntry(SEARCH_REQUEST, future);
-    future.setResultFuture(innerFuture);
+    future.setFutureResult(innerFuture);
     return future;
   }
 
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java b/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java
index 87caee7..29645f7 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java
@@ -74,7 +74,7 @@
       InterruptedException, UnsupportedOperationException,
       IllegalStateException, NullPointerException
   {
-    ResultFuture<Result> future = connection.add(request, null);
+    FutureResult<Result> future = connection.add(request, null);
     try
     {
       return future.get();
@@ -102,7 +102,7 @@
       UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    ResultFuture<BindResult> future = connection.bind(request, null);
+    FutureResult<BindResult> future = connection.bind(request, null);
     try
     {
       return future.get();
@@ -136,7 +136,7 @@
       UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    ResultFuture<CompareResult> future = connection.compare(request,
+    FutureResult<CompareResult> future = connection.compare(request,
         null);
     try
     {
@@ -156,7 +156,7 @@
       UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    ResultFuture<Result> future = connection.delete(request, null);
+    FutureResult<Result> future = connection.delete(request, null);
     try
     {
       return future.get();
@@ -175,7 +175,7 @@
       UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    ResultFuture<R> future = connection.extendedRequest(request, null);
+    FutureResult<R> future = connection.extendedRequest(request, null);
     try
     {
       return future.get();
@@ -194,7 +194,7 @@
       UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    ResultFuture<Result> future = connection.modify(request, null);
+    FutureResult<Result> future = connection.modify(request, null);
     try
     {
       return future.get();
@@ -213,7 +213,7 @@
       UnsupportedOperationException, IllegalStateException,
       NullPointerException
   {
-    ResultFuture<Result> future = connection.modifyDN(request, null);
+    FutureResult<Result> future = connection.modifyDN(request, null);
     try
     {
       return future.get();
@@ -243,7 +243,7 @@
       InterruptedException, UnsupportedOperationException,
       IllegalStateException, NullPointerException
   {
-    ResultFuture<Result> future = connection.search(request, null,
+    FutureResult<Result> future = connection.search(request, null,
         handler);
     try
     {
@@ -275,7 +275,7 @@
       throws ErrorResultException, InterruptedException,
       UnsupportedOperationException, IllegalStateException
   {
-    ResultFuture<Schema> future = connection.readSchemaForEntry(name,
+    FutureResult<Schema> future = connection.readSchemaForEntry(name,
         null);
     try
     {
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/ldap/LDAPConnectionFactory.java b/opendj-sdk/sdk/src/org/opends/sdk/ldap/LDAPConnectionFactory.java
index 7af69cb..3a66ba8 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/ldap/LDAPConnectionFactory.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/ldap/LDAPConnectionFactory.java
@@ -114,8 +114,8 @@
 
 
 
-  public ConnectionFuture<AsynchronousConnection> getAsynchronousConnection(
-      ConnectionResultHandler<? super AsynchronousConnection> handler)
+  public FutureResult<AsynchronousConnection> getAsynchronousConnection(
+      ResultHandler<? super AsynchronousConnection> handler)
   {
     return impl.getAsynchronousConnection(handler);
   }
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/schema/CoreSchemaImpl.java b/opendj-sdk/sdk/src/org/opends/sdk/schema/CoreSchemaImpl.java
index 1140a86..8f39528 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/schema/CoreSchemaImpl.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/schema/CoreSchemaImpl.java
@@ -81,7 +81,7 @@
 
   static
   {
-    final SchemaBuilder builder = new SchemaBuilder();
+    final SchemaBuilder builder = new SchemaBuilder("Core Schema");
     defaultSyntaxes(builder);
     defaultMatchingRules(builder);
     defaultAttributeTypes(builder);
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java b/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java
index 37b665f..fd6722e 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/schema/Schema.java
@@ -42,8 +42,8 @@
 import org.opends.sdk.responses.Result;
 import org.opends.sdk.responses.SearchResultEntry;
 
-import com.sun.opends.sdk.util.ResultChain;
-import com.sun.opends.sdk.util.ResultTransformer;
+import com.sun.opends.sdk.util.FutureResultTransformer;
+import com.sun.opends.sdk.util.RecursiveFutureResult;
 import com.sun.opends.sdk.util.StaticUtils;
 
 
@@ -281,6 +281,16 @@
 
 
 
+    /**
+     * {@inheritDoc}
+     */
+    public String getSchemaName()
+    {
+      return "Empty Schema";
+    }
+
+
+
     public Syntax getSyntax(String numericOID)
     {
       // Fake up a syntax substituted by the default syntax.
@@ -483,6 +493,10 @@
 
 
 
+    String getSchemaName();
+
+
+
     Syntax getSyntax(String numericOID)
         throws UnknownSchemaElementException;
 
@@ -750,6 +764,13 @@
 
 
 
+    public String getSchemaName()
+    {
+      return strictImpl.getSchemaName();
+    }
+
+
+
     public Syntax getSyntax(String numericOID)
     {
       if (!strictImpl.hasSyntax(numericOID))
@@ -883,9 +904,12 @@
 
     private final List<LocalizableMessage> warnings;
 
+    private final String schemaName;
 
 
-    private StrictImpl(Map<String, Syntax> numericOID2Syntaxes,
+
+    private StrictImpl(String schemaName,
+        Map<String, Syntax> numericOID2Syntaxes,
         Map<String, MatchingRule> numericOID2MatchingRules,
         Map<String, MatchingRuleUse> numericOID2MatchingRuleUses,
         Map<String, AttributeType> numericOID2AttributeTypes,
@@ -904,6 +928,7 @@
         Map<String, List<DITStructureRule>> nameForm2StructureRules,
         SchemaCompatOptions options, List<LocalizableMessage> warnings)
     {
+      this.schemaName = schemaName;
       this.numericOID2Syntaxes = Collections
           .unmodifiableMap(numericOID2Syntaxes);
       this.numericOID2MatchingRules = Collections
@@ -1320,6 +1345,13 @@
 
 
 
+    public String getSchemaName()
+    {
+      return schemaName;
+    }
+
+
+
     public Syntax getSyntax(String numericOID)
         throws UnknownSchemaElementException
     {
@@ -1502,276 +1534,6 @@
 
 
   /**
-   * Reads the schema from the Directory Server contained in the named
-   * subschema sub-entry using the provided connection.
-   * <p>
-   * If the requested schema is not returned by the Directory Server
-   * then the request will fail with an {@link EntryNotFoundException}.
-   * More specifically, this method will never return {@code null}.
-   *
-   * @param connection
-   *          A connection to the Directory Server whose schema is to be
-   *          read.
-   * @param name
-   *          The distinguished name of the subschema sub-entry.
-   * @return The schema from the Directory Server.
-   * @throws ErrorResultException
-   *           If the result code indicates that the request failed for
-   *           some reason.
-   * @throws InterruptedException
-   *           If the current thread was interrupted while waiting.
-   * @throws UnsupportedOperationException
-   *           If the connection does not support search operations.
-   * @throws IllegalStateException
-   *           If the connection has already been closed, i.e. if
-   *           {@code isClosed() == true}.
-   * @throws NullPointerException
-   *           If the {@code connection} or {@code name} was {@code
-   *           null}.
-   */
-  public static Schema readSchema(Connection connection, DN name)
-      throws ErrorResultException, InterruptedException,
-      UnsupportedOperationException, IllegalStateException,
-      NullPointerException
-  {
-    final SearchRequest request = getReadSchemaSearchRequest(name);
-    final Entry entry = connection.searchSingleEntry(request);
-    return valueOf(entry);
-  }
-
-
-
-  /**
-   * Reads the schema from the Directory Server which applies to the
-   * named entry using the provided connection.
-   * <p>
-   * If the requested entry or its associated schema are not returned by
-   * the Directory Server then the request will fail with an
-   * {@link EntryNotFoundException}. More specifically, this method will
-   * never return {@code null}.
-   * <p>
-   * A typical implementation will first read the {@code
-   * subschemaSubentry} attribute of the entry in order to locate the
-   * schema. However, implementations may choose to perform other
-   * optimizations, such as caching.
-   *
-   * @param connection
-   *          A connection to the Directory Server whose schema is to be
-   *          read.
-   * @param name
-   *          The distinguished name of the entry whose schema is to be
-   *          located.
-   * @return The schema from the Directory Server which applies to the
-   *         named entry.
-   * @throws ErrorResultException
-   *           If the result code indicates that the request failed for
-   *           some reason.
-   * @throws InterruptedException
-   *           If the current thread was interrupted while waiting.
-   * @throws UnsupportedOperationException
-   *           If the connection does not support search operations.
-   * @throws IllegalStateException
-   *           If the connection has already been closed, i.e. if
-   *           {@code isClosed() == true}.
-   * @throws NullPointerException
-   *           If the {@code connection} or {@code name} was {@code
-   *           null}.
-   */
-  public static Schema readSchemaForEntry(Connection connection, DN name)
-      throws ErrorResultException, InterruptedException,
-      UnsupportedOperationException, IllegalStateException,
-      NullPointerException
-  {
-    final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
-    final Entry entry = connection.searchSingleEntry(request);
-    final DN subschemaDN = getSubschemaSubentryDN(name, entry);
-
-    return readSchema(connection, subschemaDN);
-  }
-
-
-
-  private static DN getSubschemaSubentryDN(DN name, final Entry entry)
-      throws ErrorResultException
-  {
-    final Attribute subentryAttr = entry
-        .getAttribute(ATTR_SUBSCHEMA_SUBENTRY);
-
-    if (subentryAttr == null || subentryAttr.isEmpty())
-    {
-      // Did not get the subschema sub-entry attribute.
-      Result result = Responses.newResult(
-          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED)
-          .setDiagnosticMessage(
-              ERR_NO_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString())
-                  .toString());
-      throw ErrorResultException.wrap(result);
-    }
-
-    String dnString = subentryAttr.iterator().next().toString();
-    DN subschemaDN;
-    try
-    {
-      subschemaDN = DN.valueOf(dnString);
-    }
-    catch (LocalizedIllegalArgumentException e)
-    {
-      Result result = Responses.newResult(
-          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED)
-          .setDiagnosticMessage(
-              ERR_INVALID_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString(),
-                  dnString, e.getMessageObject()).toString());
-      throw ErrorResultException.wrap(result);
-    }
-    return subschemaDN;
-  }
-
-
-
-  /**
-   * Reads the schema from the Directory Server contained in the named
-   * subschema sub-entry.
-   * <p>
-   * If the requested schema is not returned by the Directory Server
-   * then the request will fail with an {@link EntryNotFoundException}.
-   * More specifically, the returned future will never return {@code
-   * null}.
-   * <p>
-   * Implementations may choose to perform optimizations such as
-   * caching.
-   *
-   * @param connection
-   *          A connection to the Directory Server whose schema is to be
-   *          read.
-   * @param name
-   *          The distinguished name of the subschema sub-entry.
-   * @param handler
-   *          A result handler which can be used to asynchronously
-   *          process the operation result when it is received, may be
-   *          {@code null}.
-   * @return A future representing the result of the operation.
-   * @throws UnsupportedOperationException
-   *           If this connection does not support search operations.
-   * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if
-   *           {@code isClosed() == true}.
-   * @throws NullPointerException
-   *           If the {@code connection} or {@code name} was {@code
-   *           null}.
-   */
-  public static ResultFuture<Schema> readSchema(
-      AsynchronousConnection connection, DN name,
-      ResultHandler<? super Schema> handler)
-      throws UnsupportedOperationException, IllegalStateException,
-      NullPointerException
-  {
-    final SearchRequest request = getReadSchemaSearchRequest(name);
-
-    final ResultTransformer<SearchResultEntry, Schema> future = new ResultTransformer<SearchResultEntry, Schema>(
-        handler)
-    {
-
-      protected Schema transformResult(SearchResultEntry result)
-          throws ErrorResultException
-      {
-        return valueOf(result);
-      }
-
-    };
-
-    ResultFuture<SearchResultEntry> innerFuture = connection
-        .searchSingleEntry(request, future);
-    future.setResultFuture(innerFuture);
-    return future;
-  }
-
-
-
-  /**
-   * Reads the schema from the Directory Server which applies to the
-   * named entry.
-   * <p>
-   * If the requested entry or its associated schema are not returned by
-   * the Directory Server then the request will fail with an
-   * {@link EntryNotFoundException}. More specifically, the returned
-   * future will never return {@code null}.
-   * <p>
-   * A typical implementation will first read the {@code
-   * subschemaSubentry} attribute of the entry in order to locate the
-   * schema. However, implementations may choose to perform other
-   * optimizations, such as caching.
-   *
-   * @param connection
-   *          A connection to the Directory Server whose schema is to be
-   *          read.
-   * @param name
-   *          The distinguished name of the entry whose schema is to be
-   *          located.
-   * @param handler
-   *          A result handler which can be used to asynchronously
-   *          process the operation result when it is received, may be
-   *          {@code null}.
-   * @return A future representing the result of the operation.
-   * @throws UnsupportedOperationException
-   *           If this connection does not support search operations.
-   * @throws IllegalStateException
-   *           If this connection has already been closed, i.e. if
-   *           {@code isClosed() == true}.
-   * @throws NullPointerException
-   *           If the {@code connection} or {@code name} was {@code
-   *           null}.
-   */
-  public static ResultFuture<Schema> readSchemaForEntry(
-      final AsynchronousConnection connection, final DN name,
-      ResultHandler<Schema> handler)
-      throws UnsupportedOperationException, IllegalStateException,
-      NullPointerException
-  {
-    final ResultChain<SearchResultEntry, Schema> future = new ResultChain<SearchResultEntry, Schema>(
-        handler)
-    {
-
-      protected ResultFuture<Schema> chainResult(
-          SearchResultEntry innerResult,
-          ResultHandler<? super Schema> handler)
-          throws ErrorResultException
-      {
-        final DN subschemaDN = getSubschemaSubentryDN(name, innerResult);
-        return readSchema(connection, subschemaDN, handler);
-      }
-
-    };
-
-    final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
-    ResultFuture<SearchResultEntry> innerFuture = connection
-        .searchSingleEntry(request, future);
-    future.setInnerResultFuture(innerFuture);
-    return future;
-  }
-
-
-
-  // Constructs a search request for retrieving the named subschema
-  // sub-entry.
-  private static SearchRequest getReadSchemaSearchRequest(DN dn)
-  {
-    return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
-        SUBSCHEMA_FILTER, SUBSCHEMA_ATTRS);
-  }
-
-
-
-  // Constructs a search request for retrieving the subschemaSubentry
-  // attribute from the named entry.
-  private static SearchRequest getReadSchemaForEntrySearchRequest(DN dn)
-  {
-    return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
-        SUBSCHEMA_FILTER, SUBSCHEMA_SUBENTRY_ATTRS);
-  }
-
-
-
-  /**
    * Returns the core schema. The core schema is non-strict and contains
    * the following standard LDAP schema elements:
    * <ul>
@@ -1831,6 +1593,234 @@
 
 
   /**
+   * Reads the schema from the Directory Server contained in the named
+   * subschema sub-entry.
+   * <p>
+   * If the requested schema is not returned by the Directory Server
+   * then the request will fail with an {@link EntryNotFoundException}.
+   * More specifically, the returned future will never return {@code
+   * null}.
+   * <p>
+   * Implementations may choose to perform optimizations such as
+   * caching.
+   *
+   * @param connection
+   *          A connection to the Directory Server whose schema is to be
+   *          read.
+   * @param name
+   *          The distinguished name of the subschema sub-entry.
+   * @param handler
+   *          A result handler which can be used to asynchronously
+   *          process the operation result when it is received, may be
+   *          {@code null}.
+   * @return A future representing the result of the operation.
+   * @throws UnsupportedOperationException
+   *           If this connection does not support search operations.
+   * @throws IllegalStateException
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
+   * @throws NullPointerException
+   *           If the {@code connection} or {@code name} was {@code
+   *           null}.
+   */
+  public static FutureResult<Schema> readSchema(
+      AsynchronousConnection connection, DN name,
+      ResultHandler<? super Schema> handler)
+      throws UnsupportedOperationException, IllegalStateException,
+      NullPointerException
+  {
+    final SearchRequest request = getReadSchemaSearchRequest(name);
+
+    final FutureResultTransformer<SearchResultEntry, Schema> future = new FutureResultTransformer<SearchResultEntry, Schema>(
+        handler)
+    {
+
+      protected Schema transformResult(SearchResultEntry result)
+          throws ErrorResultException
+      {
+        return valueOf(result);
+      }
+
+    };
+
+    final FutureResult<SearchResultEntry> innerFuture = connection
+        .searchSingleEntry(request, future);
+    future.setFutureResult(innerFuture);
+    return future;
+  }
+
+
+
+  /**
+   * Reads the schema from the Directory Server contained in the named
+   * subschema sub-entry using the provided connection.
+   * <p>
+   * If the requested schema is not returned by the Directory Server
+   * then the request will fail with an {@link EntryNotFoundException}.
+   * More specifically, this method will never return {@code null}.
+   *
+   * @param connection
+   *          A connection to the Directory Server whose schema is to be
+   *          read.
+   * @param name
+   *          The distinguished name of the subschema sub-entry.
+   * @return The schema from the Directory Server.
+   * @throws ErrorResultException
+   *           If the result code indicates that the request failed for
+   *           some reason.
+   * @throws InterruptedException
+   *           If the current thread was interrupted while waiting.
+   * @throws UnsupportedOperationException
+   *           If the connection does not support search operations.
+   * @throws IllegalStateException
+   *           If the connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
+   * @throws NullPointerException
+   *           If the {@code connection} or {@code name} was {@code
+   *           null}.
+   */
+  public static Schema readSchema(Connection connection, DN name)
+      throws ErrorResultException, InterruptedException,
+      UnsupportedOperationException, IllegalStateException,
+      NullPointerException
+  {
+    final SearchRequest request = getReadSchemaSearchRequest(name);
+    final Entry entry = connection.searchSingleEntry(request);
+    return valueOf(entry);
+  }
+
+
+
+  /**
+   * Reads the schema from the Directory Server which applies to the
+   * named entry.
+   * <p>
+   * If the requested entry or its associated schema are not returned by
+   * the Directory Server then the request will fail with an
+   * {@link EntryNotFoundException}. More specifically, the returned
+   * future will never return {@code null}.
+   * <p>
+   * A typical implementation will first read the {@code
+   * subschemaSubentry} attribute of the entry in order to locate the
+   * schema. However, implementations may choose to perform other
+   * optimizations, such as caching.
+   *
+   * @param connection
+   *          A connection to the Directory Server whose schema is to be
+   *          read.
+   * @param name
+   *          The distinguished name of the entry whose schema is to be
+   *          located.
+   * @param handler
+   *          A result handler which can be used to asynchronously
+   *          process the operation result when it is received, may be
+   *          {@code null}.
+   * @return A future representing the result of the operation.
+   * @throws UnsupportedOperationException
+   *           If this connection does not support search operations.
+   * @throws IllegalStateException
+   *           If this connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
+   * @throws NullPointerException
+   *           If the {@code connection} or {@code name} was {@code
+   *           null}.
+   */
+  public static FutureResult<Schema> readSchemaForEntry(
+      final AsynchronousConnection connection, final DN name,
+      ResultHandler<Schema> handler)
+      throws UnsupportedOperationException, IllegalStateException,
+      NullPointerException
+  {
+    final RecursiveFutureResult<SearchResultEntry, Schema> future = new RecursiveFutureResult<SearchResultEntry, Schema>(
+        handler)
+    {
+
+      protected FutureResult<Schema> chainResult(
+          SearchResultEntry innerResult,
+          ResultHandler<? super Schema> handler)
+          throws ErrorResultException
+      {
+        final DN subschemaDN = getSubschemaSubentryDN(name, innerResult);
+        return readSchema(connection, subschemaDN, handler);
+      }
+
+    };
+
+    final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
+    final FutureResult<SearchResultEntry> innerFuture = connection
+        .searchSingleEntry(request, future);
+    future.setFutureResult(innerFuture);
+    return future;
+  }
+
+
+
+  /**
+   * Reads the schema from the Directory Server which applies to the
+   * named entry using the provided connection.
+   * <p>
+   * If the requested entry or its associated schema are not returned by
+   * the Directory Server then the request will fail with an
+   * {@link EntryNotFoundException}. More specifically, this method will
+   * never return {@code null}.
+   * <p>
+   * A typical implementation will first read the {@code
+   * subschemaSubentry} attribute of the entry in order to locate the
+   * schema. However, implementations may choose to perform other
+   * optimizations, such as caching.
+   *
+   * @param connection
+   *          A connection to the Directory Server whose schema is to be
+   *          read.
+   * @param name
+   *          The distinguished name of the entry whose schema is to be
+   *          located.
+   * @return The schema from the Directory Server which applies to the
+   *         named entry.
+   * @throws ErrorResultException
+   *           If the result code indicates that the request failed for
+   *           some reason.
+   * @throws InterruptedException
+   *           If the current thread was interrupted while waiting.
+   * @throws UnsupportedOperationException
+   *           If the connection does not support search operations.
+   * @throws IllegalStateException
+   *           If the connection has already been closed, i.e. if
+   *           {@code isClosed() == true}.
+   * @throws NullPointerException
+   *           If the {@code connection} or {@code name} was {@code
+   *           null}.
+   */
+  public static Schema readSchemaForEntry(Connection connection, DN name)
+      throws ErrorResultException, InterruptedException,
+      UnsupportedOperationException, IllegalStateException,
+      NullPointerException
+  {
+    final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
+    final Entry entry = connection.searchSingleEntry(request);
+    final DN subschemaDN = getSubschemaSubentryDN(name, entry);
+
+    return readSchema(connection, subschemaDN);
+  }
+
+
+
+  /**
+   * Sets the default schema which should be used by this application.
+   * The default schema is initially set to the core schema.
+   *
+   * @param schema
+   *          The default schema which should be used by this
+   *          application.
+   */
+  public static void setDefaultSchema(Schema schema)
+  {
+    DEFAULT_SCHEMA = schema;
+  }
+
+
+
+  /**
    * Parses the provided entry as a subschema subentry. Any problems
    * encountered while parsing the entry can be retrieved using the
    * returned schema's {@link #getWarnings()} method.
@@ -1841,7 +1831,8 @@
    */
   public static Schema valueOf(Entry entry)
   {
-    final SchemaBuilder builder = new SchemaBuilder();
+    final SchemaBuilder builder = new SchemaBuilder(entry.getName()
+        .toString());
 
     Attribute attr = entry.getAttribute(ATTR_LDAP_SYNTAXES);
     if (attr != null)
@@ -1976,17 +1967,59 @@
 
 
 
-  /**
-   * Sets the default schema which should be used by this application.
-   * The default schema is initially set to the core schema.
-   *
-   * @param schema
-   *          The default schema which should be used by this
-   *          application.
-   */
-  public static void setDefaultSchema(Schema schema)
+  // Constructs a search request for retrieving the subschemaSubentry
+  // attribute from the named entry.
+  private static SearchRequest getReadSchemaForEntrySearchRequest(DN dn)
   {
-    DEFAULT_SCHEMA = schema;
+    return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
+        Filter.getObjectClassPresentFilter(), SUBSCHEMA_SUBENTRY_ATTRS);
+  }
+
+
+
+  // Constructs a search request for retrieving the named subschema
+  // sub-entry.
+  private static SearchRequest getReadSchemaSearchRequest(DN dn)
+  {
+    return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
+        SUBSCHEMA_FILTER, SUBSCHEMA_ATTRS);
+  }
+
+
+
+  private static DN getSubschemaSubentryDN(DN name, final Entry entry)
+      throws ErrorResultException
+  {
+    final Attribute subentryAttr = entry
+        .getAttribute(ATTR_SUBSCHEMA_SUBENTRY);
+
+    if (subentryAttr == null || subentryAttr.isEmpty())
+    {
+      // Did not get the subschema sub-entry attribute.
+      final Result result = Responses.newResult(
+          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED)
+          .setDiagnosticMessage(
+              ERR_NO_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString())
+                  .toString());
+      throw ErrorResultException.wrap(result);
+    }
+
+    final String dnString = subentryAttr.iterator().next().toString();
+    DN subschemaDN;
+    try
+    {
+      subschemaDN = DN.valueOf(dnString);
+    }
+    catch (final LocalizedIllegalArgumentException e)
+    {
+      final Result result = Responses.newResult(
+          ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED)
+          .setDiagnosticMessage(
+              ERR_INVALID_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString(),
+                  dnString, e.getMessageObject()).toString());
+      throw ErrorResultException.wrap(result);
+    }
+    return subschemaDN;
   }
 
 
@@ -2009,7 +2042,14 @@
 
 
 
-  Schema(Map<String, Syntax> numericOID2Syntaxes,
+  private Schema(Impl impl)
+  {
+    this.impl = impl;
+  }
+
+
+
+  Schema(String schemaName, Map<String, Syntax> numericOID2Syntaxes,
       Map<String, MatchingRule> numericOID2MatchingRules,
       Map<String, MatchingRuleUse> numericOID2MatchingRuleUses,
       Map<String, AttributeType> numericOID2AttributeTypes,
@@ -2028,7 +2068,7 @@
       Map<String, List<DITStructureRule>> nameForm2StructureRules,
       SchemaCompatOptions options, List<LocalizableMessage> warnings)
   {
-    impl = new StrictImpl(numericOID2Syntaxes,
+    impl = new StrictImpl(schemaName, numericOID2Syntaxes,
         numericOID2MatchingRules, numericOID2MatchingRuleUses,
         numericOID2AttributeTypes, numericOID2ObjectClasses,
         numericOID2NameForms, numericOID2ContentRules,
@@ -2040,13 +2080,6 @@
 
 
 
-  private Schema(Impl impl)
-  {
-    this.impl = impl;
-  }
-
-
-
   /**
    * Returns the attribute type with the specified name or numeric OID.
    *
@@ -2441,6 +2474,22 @@
 
 
   /**
+   * Returns the user-friendly name of this schema which may be used for
+   * debugging purposes. The format of the schema name is not defined
+   * but should contain the distinguished name of the subschema
+   * sub-entry for those schemas retrieved from a Directory Server.
+   *
+   * @return The user-friendly name of this schema which may be used for
+   *         debugging purposes.
+   */
+  public String getSchemaName()
+  {
+    return impl.getSchemaName();
+  }
+
+
+
+  /**
    * Returns the syntax with the specified numeric OID.
    *
    * @param numericOID
diff --git a/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java b/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java
index 8516737..cd54d12 100644
--- a/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java
+++ b/opendj-sdk/sdk/src/org/opends/sdk/schema/SchemaBuilder.java
@@ -33,13 +33,16 @@
 import static org.opends.sdk.schema.SchemaConstants.*;
 
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Pattern;
 
 import org.opends.sdk.DecodeException;
-import org.opends.sdk.LocalizedIllegalArgumentException;
 import org.opends.sdk.LocalizableMessage;
+import org.opends.sdk.LocalizedIllegalArgumentException;
 
-import com.sun.opends.sdk.util.*;
+import com.sun.opends.sdk.util.StaticUtils;
+import com.sun.opends.sdk.util.SubstringReader;
+import com.sun.opends.sdk.util.Validator;
 
 
 
@@ -90,6 +93,10 @@
 
   private Schema schema;
 
+  // A unique ID which can be used to uniquely identify schemas
+  // constructed without a name.
+  private final AtomicInteger nextSchemaID = new AtomicInteger();
+
 
 
   /**
@@ -98,7 +105,7 @@
    */
   public SchemaBuilder()
   {
-    initBuilder();
+    initBuilder(null);
   }
 
 
@@ -106,7 +113,7 @@
   /**
    * Creates a new schema builder containing all of the schema elements
    * from the provided schema and its compatibility options.
-   *
+   * 
    * @param schema
    *          The initial contents of the schema builder.
    * @throws NullPointerException
@@ -114,8 +121,7 @@
    */
   public SchemaBuilder(Schema schema) throws NullPointerException
   {
-    Validator.ensureNotNull(schema);
-    initBuilder();
+    initBuilder(schema.getSchemaName());
     setSchemaCompatOptions(schema.getSchemaCompatOptions());
     addSchema(schema, true);
   }
@@ -123,8 +129,23 @@
 
 
   /**
+   * Creates a new schema builder with no schema elements and default
+   * compatibility options.
+   * 
+   * @param schemaName
+   *          The user-friendly name of this schema which may be used
+   *          for debugging purposes.
+   */
+  public SchemaBuilder(String schemaName)
+  {
+    initBuilder(schemaName);
+  }
+
+
+
+  /**
    * Adds the provided attribute type definition to this schema builder.
-   *
+   * 
    * @param definition
    *          The attribute type definition.
    * @param overwrite
@@ -386,7 +407,7 @@
 
   /**
    * Adds the provided attribute type definition to this schema builder.
-   *
+   * 
    * @param oid
    *          The OID of the attribute type definition.
    * @param names
@@ -465,7 +486,7 @@
   /**
    * Adds the provided DIT content rule definition to this schema
    * builder.
-   *
+   * 
    * @param definition
    *          The DIT content rule definition.
    * @param overwrite
@@ -622,7 +643,7 @@
   /**
    * Adds the provided DIT content rule definition to this schema
    * builder.
-   *
+   * 
    * @param structuralClass
    *          The name of the structural object class to which the DIT
    *          content rule applies.
@@ -677,7 +698,7 @@
   /**
    * Adds the provided DIT structure rule definition to this schema
    * builder.
-   *
+   * 
    * @param ruleID
    *          The rule identifier of the DIT structure rule.
    * @param names
@@ -721,7 +742,7 @@
   /**
    * Adds the provided DIT structure rule definition to this schema
    * builder.
-   *
+   * 
    * @param definition
    *          The DIT structure rule definition.
    * @param overwrite
@@ -874,7 +895,7 @@
   /**
    * Adds the provided enumeration syntax definition to this schema
    * builder.
-   *
+   * 
    * @param oid
    *          The OID of the enumeration syntax definition.
    * @param description
@@ -923,7 +944,7 @@
 
   /**
    * Adds the provided matching rule definition to this schema builder.
-   *
+   * 
    * @param definition
    *          The matching rule definition.
    * @param overwrite
@@ -1070,7 +1091,7 @@
 
   /**
    * Adds the provided matching rule definition to this schema builder.
-   *
+   * 
    * @param oid
    *          The OID of the matching rule definition.
    * @param names
@@ -1114,7 +1135,7 @@
   /**
    * Adds the provided matching rule use definition to this schema
    * builder.
-   *
+   * 
    * @param definition
    *          The matching rule use definition.
    * @param overwrite
@@ -1263,7 +1284,7 @@
   /**
    * Adds the provided matching rule use definition to this schema
    * builder.
-   *
+   * 
    * @param oid
    *          The OID of the matching rule use definition.
    * @param names
@@ -1303,7 +1324,7 @@
 
   /**
    * Adds the provided name form definition to this schema builder.
-   *
+   * 
    * @param definition
    *          The name form definition.
    * @param overwrite
@@ -1468,7 +1489,7 @@
 
   /**
    * Adds the provided name form definition to this schema builder.
-   *
+   * 
    * @param oid
    *          The OID of the name form definition.
    * @param names
@@ -1514,7 +1535,7 @@
 
   /**
    * Adds the provided object class definition to this schema builder.
-   *
+   * 
    * @param definition
    *          The object class definition.
    * @param overwrite
@@ -1699,7 +1720,7 @@
 
   /**
    * Adds the provided object class definition to this schema builder.
-   *
+   * 
    * @param oid
    *          The OID of the object class definition.
    * @param names
@@ -1762,7 +1783,7 @@
 
   /**
    * Adds the provided pattern syntax definition to this schema builder.
-   *
+   * 
    * @param oid
    *          The OID of the pattern syntax definition.
    * @param description
@@ -1795,7 +1816,7 @@
   /**
    * Adds all of the schema elements in the provided schema to this
    * schema builder.
-   *
+   * 
    * @param schema
    *          The schema to be copied into this schema builder.
    * @param overwrite
@@ -1862,7 +1883,7 @@
   /**
    * Adds the provided substitution syntax definition to this schema
    * builder.
-   *
+   * 
    * @param oid
    *          The OID of the substitution syntax definition.
    * @param description
@@ -1894,7 +1915,7 @@
 
   /**
    * Adds the provided syntax definition to this schema builder.
-   *
+   * 
    * @param definition
    *          The syntax definition.
    * @param overwrite
@@ -2034,7 +2055,7 @@
 
   /**
    * Adds the provided syntax definition to this schema builder.
-   *
+   * 
    * @param oid
    *          The OID of the syntax definition.
    * @param description
@@ -2068,7 +2089,7 @@
 
   /**
    * Removes the named attribute type from this schema builder.
-   *
+   * 
    * @param name
    *          The name or OID of the attribute type to be removed.
    * @return {@code true} if the attribute type was found.
@@ -2087,7 +2108,7 @@
 
   /**
    * Removes the named DIT content rule from this schema builder.
-   *
+   * 
    * @param name
    *          The name or OID of the DIT content rule to be removed.
    * @return {@code true} if the DIT content rule was found.
@@ -2106,7 +2127,7 @@
 
   /**
    * Removes the specified DIT structure rule from this schema builder.
-   *
+   * 
    * @param ruleID
    *          The ID of the DIT structure rule to be removed.
    * @return {@code true} if the DIT structure rule was found.
@@ -2125,7 +2146,7 @@
 
   /**
    * Removes the named matching rule from this schema builder.
-   *
+   * 
    * @param name
    *          The name or OID of the matching rule to be removed.
    * @return {@code true} if the matching rule was found.
@@ -2144,7 +2165,7 @@
 
   /**
    * Removes the named matching rule use from this schema builder.
-   *
+   * 
    * @param name
    *          The name or OID of the matching rule use to be removed.
    * @return {@code true} if the matching rule use was found.
@@ -2163,7 +2184,7 @@
 
   /**
    * Removes the named name form from this schema builder.
-   *
+   * 
    * @param name
    *          The name or OID of the name form to be removed.
    * @return {@code true} if the name form was found.
@@ -2182,7 +2203,7 @@
 
   /**
    * Removes the named object class from this schema builder.
-   *
+   * 
    * @param name
    *          The name or OID of the object class to be removed.
    * @return {@code true} if the object class was found.
@@ -2201,7 +2222,7 @@
 
   /**
    * Removes the named syntax from this schema builder.
-   *
+   * 
    * @param numericOID
    *          The name of the syntax to be removed.
    * @return {@code true} if the syntax was found.
@@ -2223,7 +2244,7 @@
    * schema builder maintains its own set of compatibility options, so
    * subsequent changes to the provided set of options will not impact
    * this schema builder.
-   *
+   * 
    * @param options
    *          The set of schema compatibility options that this schema
    *          builder should use.
@@ -2248,7 +2269,7 @@
    * <p>
    * When this method returns this schema builder is empty and contains
    * a default set of compatibility options.
-   *
+   * 
    * @return A {@code Schema} containing all of the schema elements
    *         contained in this schema builder as well as the same set of
    *         schema compatibility options
@@ -2257,19 +2278,12 @@
   {
     validate();
     final Schema builtSchema = schema;
-    initBuilder();
+    initBuilder(null);
     return builtSchema;
   }
 
 
 
-  void addWarning(LocalizableMessage warning)
-  {
-    warnings.add(warning);
-  }
-
-
-
   private synchronized void addAttributeType(AttributeType attribute,
       boolean overwrite) throws ConflictingSchemaElementException
   {
@@ -2589,7 +2603,7 @@
 
 
 
-  private void initBuilder()
+  private void initBuilder(String schemaName)
   {
     numericOID2Syntaxes = new HashMap<String, Syntax>();
     numericOID2MatchingRules = new HashMap<String, MatchingRule>();
@@ -2613,14 +2627,20 @@
     options = SchemaCompatOptions.defaultOptions();
     warnings = new LinkedList<LocalizableMessage>();
 
-    schema = new Schema(numericOID2Syntaxes, numericOID2MatchingRules,
-        numericOID2MatchingRuleUses, numericOID2AttributeTypes,
-        numericOID2ObjectClasses, numericOID2NameForms,
-        numericOID2ContentRules, id2StructureRules, name2MatchingRules,
-        name2MatchingRuleUses, name2AttributeTypes, name2ObjectClasses,
-        name2NameForms, name2ContentRules, name2StructureRules,
-        objectClass2NameForms, nameForm2StructureRules, options,
-        warnings);
+    if (schemaName == null)
+    {
+      schemaName = String.format("Schema#%d", nextSchemaID
+          .getAndIncrement());
+    }
+
+    schema = new Schema(schemaName, numericOID2Syntaxes,
+        numericOID2MatchingRules, numericOID2MatchingRuleUses,
+        numericOID2AttributeTypes, numericOID2ObjectClasses,
+        numericOID2NameForms, numericOID2ContentRules,
+        id2StructureRules, name2MatchingRules, name2MatchingRuleUses,
+        name2AttributeTypes, name2ObjectClasses, name2NameForms,
+        name2ContentRules, name2StructureRules, objectClass2NameForms,
+        nameForm2StructureRules, options, warnings);
   }
 
 
@@ -2973,4 +2993,11 @@
     }
 
   }
+
+
+
+  void addWarning(LocalizableMessage warning)
+  {
+    warnings.add(warning);
+  }
 }

--
Gitblit v1.10.0