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

Ludovic Poitou
14.52.2010 b8efcc05f7380c48df26fe0291991020dcf8be90
opendj-sdk/sdk/build.xml
@@ -363,7 +363,7 @@
        <mkdir dir="${classes.dir}" />
        <mkdir dir="${build.lib.dir}" />
        <javac srcdir="${src.dir}:${srcgen.dir}"
        <javac srcdir="${src.dir}:${srcgen.dir}:${examples.dir}"
               destdir="${classes.dir}"
               debug="on"
               debuglevel="${build.debuglevel}"
@@ -408,11 +408,11 @@
        <jar jarfile="${pdir}/lib/${SHORT_NAME}.jar"
             basedir="${classes.dir}"
             excludes="**/*_fr.properties,**/*_ja.properties,**/*_de.properties,**/*_es.properties,**/*_zh_TW.properties,**/*_zh_CN.properties,**/*_ko.properties"
             excludes="**/examples/**,**/*_fr.properties,**/*_ja.properties,**/*_de.properties,**/*_es.properties,**/*_zh_TW.properties,**/*_zh_CN.properties,**/*_ko.properties"
             compress="true"
             index="true" />
        <jar jarfile="${pdir}/lib/${SHORT_NAME}_fr.jar"
      <jar jarfile="${pdir}/lib/${SHORT_NAME}_fr.jar"
             basedir="${classes.dir}"
             includes="**/*_fr.properties"
             compress="true"
@@ -453,6 +453,29 @@
             includes="**/*_zh_TW.properties"
             compress="true"
             index="true" />
      <mkdir dir="${build.dir}/examples"/>
      <!-- Create examples.zip -->
      <jar jarfile="${build.dir}/examples/examples.jar"
                           basedir="${classes.dir}"
                           includes="**/examples/**"
                           compress="true"
                           index="true" />
      <zip destfile="${build.dir}/examples/src.zip">
            <zipfileset dir="${examples.dir}" filemode="644" dirmode="755" />
      </zip>
        <copy todir="${build.dir}/examples">
            <fileset file="${resource.dir}/example-2000.ldif.zip" />
        </copy>
      <zip destfile="${pdir}/examples.zip">
          <zipfileset dir="${build.dir}/examples"
                      filemode="644"
                      dirmode="700" />
        </zip>
        <copy todir="${pdir}/lib">
            <fileset file="${lib.dir}/*.jar" />
@@ -510,13 +533,15 @@
        <mkdir dir="${javadoc.dir}" />
        <javadoc access="protected"
                 windowtitle="${PRODUCT_NAME} API Documentation"
                 windowtitle="${PRODUCT_NAME} Documentation"
              doctitle="${PRODUCT_NAME} Documentation"
                 maxmemory="${MEM}"
                 classpath="${lib.dir}/grizzly.jar"
                 destdir="${javadoc.dir}"
                 packagenames="org.opends.sdk.*"
                 source="1.5"
                 sourcepath="src:src-generated">
                 sourcepath="src:src-generated"
                 overview="src/overview.html">
            <link href="http://java.sun.com/javase/6/docs/api/" />
        </javadoc>
    </target>
@@ -1159,6 +1184,7 @@
            <zipfileset dir="${srcgen.dir}" filemode="644" dirmode="755" />
        </zip>
    </target>
    <!-- The build target that should be used for nightly builds. -->
    <target name="nightly"
       depends="nightlybuild,nightlytests"
opendj-sdk/sdk/examples/org/opends/sdk/examples/client/asyncsearch/Main.java
New file
@@ -0,0 +1,322 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.examples.client.asyncsearch;
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import org.opends.sdk.*;
import org.opends.sdk.ldif.LDIFEntryWriter;
import org.opends.sdk.requests.BindRequest;
import org.opends.sdk.requests.Requests;
import org.opends.sdk.requests.SearchRequest;
import org.opends.sdk.responses.BindResult;
import org.opends.sdk.responses.Result;
import org.opends.sdk.responses.SearchResultEntry;
import org.opends.sdk.responses.SearchResultReference;
/**
 * An example client application which searches a Directory Server using the
 * asynchronous APIs. This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password>
 *      &lt;baseDN> &lt;scope> &lt;filter> [&lt;attibute> &lt;attribute> ...]
 * </pre>
 */
public final class Main
{
  private static final class BindResultHandlerImpl implements
      ResultHandler<BindResult>
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      System.err.println(error.getMessage());
      resultCode = error.getResult().getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleResult(final BindResult result)
    {
      // Bind succeeded: initiate search.
      final SearchRequest request = Requests.newSearchRequest(baseDN, scope,
          filter, attributes);
      connection.search(request, new SearchResultHandlerImpl());
    }
  }
  private static final class ConnectResultHandlerImpl implements
      ResultHandler<AsynchronousConnection>
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      System.err.println(error.getMessage());
      resultCode = error.getResult().getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleResult(final AsynchronousConnection connection)
    {
      // Connect succeeded: save connection and initiate bind.
      Main.connection = connection;
      final BindRequest request = Requests.newSimpleBindRequest(userName,
          password);
      connection.bind(request, new BindResultHandlerImpl());
    }
  }
  private static final class SearchResultHandlerImpl implements
      SearchResultHandler
  {
    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized boolean handleEntry(final SearchResultEntry entry)
    {
      try
      {
        WRITER.writeComment("Search result entry: "
            + entry.getName().toString());
        WRITER.writeEntry(entry);
      }
      catch (final IOException e)
      {
        System.err.println(e.getMessage());
        resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
        COMPLETION_LATCH.countDown();
        return false;
      }
      return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      System.err.println(error.getMessage());
      resultCode = error.getResult().getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized boolean handleReference(
        final SearchResultReference reference)
    {
      try
      {
        WRITER.writeComment("Search result reference: "
            + reference.getURIs().toString());
      }
      catch (final IOException e)
      {
        System.err.println(e.getMessage());
        resultCode = ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue();
        COMPLETION_LATCH.countDown();
        return false;
      }
      return true;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleResult(final Result result)
    {
      resultCode = result.getResultCode().intValue();
      COMPLETION_LATCH.countDown();
    }
  }
  private static final CountDownLatch COMPLETION_LATCH = new CountDownLatch(1);
  private static final LDIFEntryWriter WRITER = new LDIFEntryWriter(System.out);
  private static String userName;
  private static String password;
  private static String baseDN;
  private static SearchScope scope;
  private static String filter;
  private static String[] attributes;
  private static AsynchronousConnection connection = null;
  private static int resultCode = 0;
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: host, port, username, password, base
   *          DN, scope, filter, and zero or more attributes to be retrieved.
   */
  public static void main(final String[] args)
  {
    if (args.length < 7)
    {
      System.err.println("Usage: host port username password baseDN scope "
          + "filter [attribute ...]");
      System.exit(1);
    }
    // Parse command line arguments.
    final String hostName = args[0];
    final int port = Integer.parseInt(args[1]);
    userName = args[2];
    password = args[3];
    baseDN = args[4];
    final String scopeString = args[5];
    filter = args[6];
    if (args.length > 7)
    {
      attributes = Arrays.copyOfRange(args, 7, args.length);
    }
    else
    {
      attributes = new String[0];
    }
    if (scopeString.equalsIgnoreCase("base"))
    {
      scope = SearchScope.BASE_OBJECT;
    }
    else if (scopeString.equalsIgnoreCase("one"))
    {
      scope = SearchScope.SINGLE_LEVEL;
    }
    else if (scopeString.equalsIgnoreCase("sub"))
    {
      scope = SearchScope.WHOLE_SUBTREE;
    }
    else if (scopeString.equalsIgnoreCase("subordinates"))
    {
      scope = SearchScope.SUBORDINATES;
    }
    else
    {
      System.err.println("Unknown scope: " + scopeString);
      System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
      return;
    }
    // Initiate the asynchronous connect, bind, and search.
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName,
        port);
    factory.getAsynchronousConnection(new ConnectResultHandlerImpl());
    // Await completion.
    try
    {
      COMPLETION_LATCH.await();
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    try
    {
      WRITER.flush();
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    if (connection != null)
    {
      connection.close();
    }
    System.exit(resultCode);
  }
  private Main()
  {
    // Not used.
  }
}
opendj-sdk/sdk/examples/org/opends/sdk/examples/client/asyncsearch/package-info.java
New file
@@ -0,0 +1,35 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
/**
 * An example client application which searches a Directory Server using the
 * asynchronous APIs.
 */
package org.opends.sdk.examples.client.asyncsearch;
opendj-sdk/sdk/examples/org/opends/sdk/examples/client/modify/Main.java
New file
@@ -0,0 +1,166 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.examples.client.modify;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import org.opends.sdk.Connection;
import org.opends.sdk.ErrorResultException;
import org.opends.sdk.LDAPConnectionFactory;
import org.opends.sdk.ResultCode;
import org.opends.sdk.ldif.ChangeRecord;
import org.opends.sdk.ldif.ConnectionChangeRecordWriter;
import org.opends.sdk.ldif.LDIFChangeRecordReader;
/**
 * An example client application which applies update operations to a Directory
 * Server. The update operations will be read from an LDIF file, or stdin if no
 * filename is provided. This example takes the following command line
 * parameters (it will read from stdin if no LDIF file is provided):
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password> [&lt;ldifFile>]
 * </pre>
 */
public final class Main
{
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: host, port, username, password, LDIF
   *          file name containing the update operations (will use stdin if not
   *          provided).
   */
  public static void main(final String[] args)
  {
    if (args.length < 4 || args.length > 5)
    {
      System.err.println("Usage: host port username password [ldifFileName]");
      System.exit(1);
    }
    // Parse command line arguments.
    final String hostName = args[0];
    final int port = Integer.parseInt(args[1]);
    final String userName = args[2];
    final String password = args[3];
    // Create the LDIF reader which will either used the named file, if
    // provided, or stdin.
    InputStream ldif;
    if (args.length > 4)
    {
      try
      {
        ldif = new FileInputStream(args[4]);
      }
      catch (final FileNotFoundException e)
      {
        System.err.println(e.getMessage());
        System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
        return;
      }
    }
    else
    {
      ldif = System.in;
    }
    final LDIFChangeRecordReader reader = new LDIFChangeRecordReader(ldif);
    // Connect and bind to the server.
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName,
        port);
    Connection connection = null;
    try
    {
      connection = factory.getConnection();
      connection.bind(userName, password);
      // Write the changes.
      final ConnectionChangeRecordWriter writer = new ConnectionChangeRecordWriter(
          connection);
      while (reader.hasNext())
      {
        ChangeRecord changeRecord = reader.readChangeRecord();
        writer.writeChangeRecord(changeRecord);
        System.err.println("Successfully modified entry "
            + changeRecord.getName().toString());
      }
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    finally
    {
      if (connection != null)
      {
        connection.close();
      }
      try
      {
        reader.close();
      }
      catch (final IOException ignored)
      {
        // Ignore.
      }
    }
  }
  private Main()
  {
    // Not used.
  }
}
opendj-sdk/sdk/examples/org/opends/sdk/examples/client/modify/package-info.java
New file
@@ -0,0 +1,36 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
/**
 * An example client application which applies update operations to a Directory
 * Server. The update operations will be read from an LDIF file, or stdin if no
 * filename is provided.
 */
package org.opends.sdk.examples.client.modify;
opendj-sdk/sdk/examples/org/opends/sdk/examples/client/search/Main.java
New file
@@ -0,0 +1,187 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.examples.client.search;
import java.io.IOException;
import java.util.Arrays;
import org.opends.sdk.*;
import org.opends.sdk.ldif.ConnectionEntryReader;
import org.opends.sdk.ldif.LDIFEntryWriter;
import org.opends.sdk.responses.SearchResultEntry;
import org.opends.sdk.responses.SearchResultReference;
/**
 * An example client application which searches a Directory Server. This example
 * takes the following command line parameters:
 *
 * <pre>
 *  &lt;host> &lt;port> &lt;username> &lt;password>
 *      &lt;baseDN> &lt;scope> &lt;filter> [&lt;attibute> &lt;attribute> ...]
 * </pre>
 */
public final class Main
{
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: host, port, username, password, base
   *          DN, scope, filter, and zero or more attributes to be retrieved.
   */
  public static void main(final String[] args)
  {
    if (args.length < 7)
    {
      System.err.println("Usage: host port username password baseDN scope "
          + "filter [attribute ...]");
      System.exit(1);
    }
    // Parse command line arguments.
    final String hostName = args[0];
    final int port = Integer.parseInt(args[1]);
    final String userName = args[2];
    final String password = args[3];
    final String baseDN = args[4];
    final String scopeString = args[5];
    final String filter = args[6];
    String[] attributes;
    if (args.length > 7)
    {
      attributes = Arrays.copyOfRange(args, 7, args.length);
    }
    else
    {
      attributes = new String[0];
    }
    SearchScope scope;
    if (scopeString.equalsIgnoreCase("base"))
    {
      scope = SearchScope.BASE_OBJECT;
    }
    else if (scopeString.equalsIgnoreCase("one"))
    {
      scope = SearchScope.SINGLE_LEVEL;
    }
    else if (scopeString.equalsIgnoreCase("sub"))
    {
      scope = SearchScope.WHOLE_SUBTREE;
    }
    else if (scopeString.equalsIgnoreCase("subordinates"))
    {
      scope = SearchScope.SUBORDINATES;
    }
    else
    {
      System.err.println("Unknown scope: " + scopeString);
      System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
      return;
    }
    // Create an LDIF writer which will write the search results to stdout.
    final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
    // Connect and bind to the server.
    final LDAPConnectionFactory factory = new LDAPConnectionFactory(hostName,
        port);
    Connection connection = null;
    try
    {
      connection = factory.getConnection();
      connection.bind(userName, password);
      // Read the entries and output them as LDIF.
      final ConnectionEntryReader reader = connection.search(baseDN, scope,
          filter, attributes);
      while (reader.hasNext())
      {
        if (!reader.isReference())
        {
          final SearchResultEntry entry = reader.readEntry();
          writer.writeComment("Search result entry: "
              + entry.getName().toString());
          writer.writeEntry(entry);
        }
        else
        {
          final SearchResultReference ref = reader.readReference();
          // Got a continuation reference.
          writer.writeComment("Search result reference: "
              + ref.getURIs().toString());
        }
      }
      writer.flush();
    }
    catch (final ErrorResultException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getResult().getResultCode().intValue());
      return;
    }
    catch (final ErrorResultIOException e)
    {
      System.err.println(e.getMessage());
      System.exit(e.getCause().getResult().getResultCode().intValue());
      return;
    }
    catch (final InterruptedException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_USER_CANCELLED.intValue());
      return;
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    finally
    {
      if (connection != null)
      {
        connection.close();
      }
    }
  }
  private Main()
  {
    // Not used.
  }
}
opendj-sdk/sdk/examples/org/opends/sdk/examples/client/search/package-info.java
New file
@@ -0,0 +1,34 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
/**
 * An example client application which searches a Directory Server.
 */
package org.opends.sdk.examples.client.search;
opendj-sdk/sdk/examples/org/opends/sdk/examples/server/proxy/Main.java
New file
@@ -0,0 +1,515 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.examples.server.proxy;
import static org.opends.sdk.ErrorResultException.newErrorResult;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import org.opends.sdk.*;
import org.opends.sdk.requests.*;
import org.opends.sdk.responses.BindResult;
import org.opends.sdk.responses.CompareResult;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.Result;
/**
 * An LDAP load balancing proxy which forwards requests to one or more remote
 * Directory Servers. This is implementation is very simple and is only intended
 * as an example:
 * <ul>
 * <li>It does not support SSL connections
 * <li>It does not support StartTLS
 * <li>It does not support Abandon or Cancel requests
 * <li>Very basic authentication and authorization support.
 * </ul>
 * This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;listenAddress> &lt;listenPort> &lt;remoteAddress1> &lt;remotePort1>
 *      [&lt;remoteAddress2> &lt;remotePort2> ...]
 * </pre>
 */
public final class Main
{
  /**
   * Proxy server connection factory implementation.
   */
  private static final class Proxy implements
      ServerConnectionFactory<LDAPClientContext, Integer>
  {
    private final class ServerConnectionImpl implements
        ServerConnection<Integer>
    {
      private volatile AsynchronousConnection connection = null;
      private volatile boolean isUnbindRequired = false;
      private AsynchronousConnection getConnection()
          throws ErrorResultException
      {
        if (connection == null)
        {
          synchronized (this)
          {
            if (connection == null)
            {
              try
              {
                connection = factory.getAsynchronousConnection(null).get();
              }
              catch (InterruptedException e)
              {
                throw new RuntimeException(e);
              }
            }
          }
        }
        return connection;
      }
      private ServerConnectionImpl(final LDAPClientContext clientContext)
      {
        // Nothing to do.
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleAbandon(final Integer requestContext,
          final AbandonRequest request) throws UnsupportedOperationException
      {
        // Not implemented.
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleAdd(final Integer requestContext,
          final AddRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        try
        {
          getConnection().add(request, resultHandler,
              intermediateResponseHandler);
        }
        catch (ErrorResultException e)
        {
          resultHandler.handleErrorResult(e);
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleBind(final Integer requestContext, final int version,
          final BindRequest request,
          final ResultHandler<? super BindResult> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        if (request.getAuthenticationType() != ((byte) 0x80))
        {
          // TODO: SASL authentication not implemented.
          resultHandler.handleErrorResult(newErrorResult(
              ResultCode.PROTOCOL_ERROR,
              "non-SIMPLE authentication not supported: "
                  + request.getAuthenticationType()));
        }
        else
        {
          // Note that this connection has received a bind request: the
          // connection should be reverted back to anonymous when the client
          // unbinds.
          isUnbindRequired = true;
          try
          {
            getConnection().bind(request, resultHandler,
                intermediateResponseHandler);
          }
          catch (ErrorResultException e)
          {
            resultHandler.handleErrorResult(e);
          }
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleCompare(final Integer requestContext,
          final CompareRequest request,
          final ResultHandler<? super CompareResult> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        try
        {
          getConnection().compare(request, resultHandler,
              intermediateResponseHandler);
        }
        catch (ErrorResultException e)
        {
          resultHandler.handleErrorResult(e);
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleConnectionClosed(final Integer requestContext,
          final UnbindRequest request)
      {
        // Client connection closed: release the proxy connection.
        close();
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleConnectionDisconnected(final ResultCode resultCode,
          final String message)
      {
        // Client disconnected by server: release the proxy connection.
        close();
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleConnectionError(final Throwable error)
      {
        // Client connection failed: release the proxy connection.
        close();
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleDelete(final Integer requestContext,
          final DeleteRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        try
        {
          getConnection().delete(request, resultHandler,
              intermediateResponseHandler);
        }
        catch (ErrorResultException e)
        {
          resultHandler.handleErrorResult(e);
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public <R extends ExtendedResult> void handleExtendedRequest(
          final Integer requestContext, final ExtendedRequest<R> request,
          final ResultHandler<? super R> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        if (request.getOID().equals(CancelExtendedRequest.OID))
        {
          // TODO: not implemented.
          resultHandler.handleErrorResult(newErrorResult(
              ResultCode.PROTOCOL_ERROR,
              "Cancel extended request operation not supported"));
        }
        else if (request.getOID().equals(StartTLSExtendedRequest.OID))
        {
          // TODO: not implemented.
          resultHandler.handleErrorResult(newErrorResult(
              ResultCode.PROTOCOL_ERROR,
              "StartTLS extended request operation not supported"));
        }
        else
        {
          // Forward all other extended operations.
          try
          {
            getConnection().extendedRequest(request, resultHandler,
                intermediateResponseHandler);
          }
          catch (ErrorResultException e)
          {
            resultHandler.handleErrorResult(e);
          }
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleModify(final Integer requestContext,
          final ModifyRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        try
        {
          getConnection().modify(request, resultHandler,
              intermediateResponseHandler);
        }
        catch (ErrorResultException e)
        {
          resultHandler.handleErrorResult(e);
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleModifyDN(final Integer requestContext,
          final ModifyDNRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        try
        {
          getConnection().modifyDN(request, resultHandler,
              intermediateResponseHandler);
        }
        catch (ErrorResultException e)
        {
          resultHandler.handleErrorResult(e);
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleSearch(final Integer requestContext,
          final SearchRequest request, final SearchResultHandler resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        try
        {
          getConnection().search(request, resultHandler,
              intermediateResponseHandler);
        }
        catch (ErrorResultException e)
        {
          resultHandler.handleErrorResult(e);
        }
      }
      private void close()
      {
        if (isUnbindRequired)
        {
          synchronized (this)
          {
            if (connection != null)
            {
              connection.bind(Requests.newSimpleBindRequest(),
                  new ResultHandler<Result>()
                  {
                    public void handleErrorResult(ErrorResultException error)
                    {
                      // The rebind failed - this is bad because if the
                      // connection is pooled it will remain authenticated as
                      // the wrong user.
                      handleResult(error.getResult());
                    }
                    public void handleResult(Result result)
                    {
                      synchronized (ServerConnectionImpl.this)
                      {
                        if (connection != null)
                        {
                          connection.close();
                        }
                      }
                    }
                  });
            }
          }
        }
      }
    }
    private final ConnectionFactory factory;
    private Proxy(final ConnectionFactory factory)
    {
      this.factory = factory;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ServerConnection<Integer> handleAccept(
        final LDAPClientContext clientContext) throws ErrorResultException
    {
      return new ServerConnectionImpl(clientContext);
    }
  }
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: listen address, listen port, remote
   *          address1, remote port1, remote address2, remote port2, ...
   */
  public static void main(final String[] args)
  {
    if (args.length < 4 || args.length % 2 != 0)
    {
      System.err.println("Usage: listenAddress listenPort "
          + "remoteAddress1 remotePort1 remoteAddress2 remotePort2");
      System.exit(1);
    }
    // Parse command line arguments.
    final String localAddress = args[0];
    final int localPort = Integer.parseInt(args[1]);
    // Create load balancer.
    final List<ConnectionFactory> factories = new LinkedList<ConnectionFactory>();
    for (int i = 2; i < args.length; i += 2)
    {
      final String remoteAddress = args[i];
      final int remotePort = Integer.parseInt(args[i + 1]);
      // factories.add(Connections.newConnectionPool(new LDAPConnectionFactory(
      //    remoteAddress, remotePort), Integer.MAX_VALUE));
      factories.add(new LDAPConnectionFactory(remoteAddress, remotePort));
    }
    final RoundRobinLoadBalancingAlgorithm algorithm = new RoundRobinLoadBalancingAlgorithm(
        factories);
    final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
    // Create listener.
    final LDAPListenerOptions options = new LDAPListenerOptions()
        .setBacklog(4096);
    LDAPListener listener = null;
    try
    {
      listener = new LDAPListener(localAddress, localPort, new Proxy(factory),
          options);
      System.out.println("Press any key to stop the server...");
      System.in.read();
    }
    catch (final IOException e)
    {
      System.out
          .println("Error listening on " + localAddress + ":" + localPort);
      e.printStackTrace();
    }
    finally
    {
      if (listener != null)
      {
        listener.close();
      }
    }
  }
  private Main()
  {
    // Not used.
  }
}
opendj-sdk/sdk/examples/org/opends/sdk/examples/server/proxy/package-info.java
New file
@@ -0,0 +1,34 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
/**
 * An example LDAP proxy which forwards requests to a remote Directory Server.
 */
package org.opends.sdk.examples.server.proxy;
opendj-sdk/sdk/examples/org/opends/sdk/examples/server/store/Main.java
New file
@@ -0,0 +1,589 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.examples.server.store;
import static org.opends.sdk.ErrorResultException.newErrorResult;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.opends.sdk.*;
import org.opends.sdk.ldif.LDIFEntryReader;
import org.opends.sdk.requests.*;
import org.opends.sdk.responses.*;
/**
 * An LDAP directory server which exposes data contained in an LDIF file. This
 * is implementation is very simple and is only intended as an example:
 * <ul>
 * <li>It does not support SSL connections
 * <li>It does not support StartTLS
 * <li>It does not support Abandon or Cancel requests
 * <li>Very basic authentication and authorization support.
 * </ul>
 * This example takes the following command line parameters:
 *
 * <pre>
 *  &lt;listenAddress> &lt;listenPort> [&lt;ldifFile>]
 * </pre>
 */
public final class Main
{
  /**
   * Proxy server connection factory implementation.
   */
  private static final class Store implements
      ServerConnectionFactory<LDAPClientContext, Integer>
  {
    private final class ServerConnectionImpl implements
        ServerConnection<Integer>
    {
      private ServerConnectionImpl(final LDAPClientContext clientContext)
      {
        // Nothing to do.
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleAbandon(final Integer requestContext,
          final AbandonRequest request) throws UnsupportedOperationException
      {
        // Not implemented.
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleAdd(final Integer requestContext,
          final AddRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        // TODO: controls.
        entryLock.writeLock().lock();
        try
        {
          DN dn = request.getName();
          if (entries.containsKey(dn))
          {
            resultHandler.handleErrorResult(ErrorResultException
                .newErrorResult(ResultCode.ENTRY_ALREADY_EXISTS, "The entry "
                    + dn.toString() + " already exists"));
          }
          DN parent = dn.parent();
          if (!entries.containsKey(parent))
          {
            resultHandler.handleErrorResult(ErrorResultException
                .newErrorResult(ResultCode.NO_SUCH_OBJECT, "The parent entry "
                    + parent.toString() + " does not exist"));
          }
          else
          {
            entries.put(dn, request);
            resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
          }
        }
        finally
        {
          entryLock.writeLock().unlock();
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleBind(final Integer requestContext, final int version,
          final BindRequest request,
          final ResultHandler<? super BindResult> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        if (request.getAuthenticationType() != ((byte) 0x80))
        {
          // TODO: SASL authentication not implemented.
          resultHandler.handleErrorResult(newErrorResult(
              ResultCode.PROTOCOL_ERROR,
              "non-SIMPLE authentication not supported: "
                  + request.getAuthenticationType()));
        }
        else
        {
          // TODO: always succeed.
          resultHandler.handleResult(Responses
              .newBindResult(ResultCode.SUCCESS));
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleCompare(final Integer requestContext,
          final CompareRequest request,
          final ResultHandler<? super CompareResult> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        // TODO:
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleConnectionClosed(final Integer requestContext,
          final UnbindRequest request)
      {
        // Nothing to do.
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleConnectionDisconnected(final ResultCode resultCode,
          final String message)
      {
        // Nothing to do.
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleConnectionError(final Throwable error)
      {
        // Nothing to do.
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleDelete(final Integer requestContext,
          final DeleteRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        // TODO: controls.
        entryLock.writeLock().lock();
        try
        {
          // TODO: check for children.
          DN dn = request.getName();
          if (!entries.containsKey(dn))
          {
            resultHandler.handleErrorResult(ErrorResultException
                .newErrorResult(ResultCode.NO_SUCH_OBJECT,
                    "The entry " + dn.toString() + " does not exist"));
          }
          else
          {
            entries.remove(dn);
            resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
          }
        }
        finally
        {
          entryLock.writeLock().unlock();
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public <R extends ExtendedResult> void handleExtendedRequest(
          final Integer requestContext, final ExtendedRequest<R> request,
          final ResultHandler<? super R> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        // TODO: not implemented.
        resultHandler.handleErrorResult(newErrorResult(
            ResultCode.PROTOCOL_ERROR,
            "Extended request operation not supported"));
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleModify(final Integer requestContext,
          final ModifyRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        // TODO: controls.
        // TODO: read lock is not really enough since concurrent updates may
        // still occur to the same entry.
        entryLock.readLock().lock();
        try
        {
          DN dn = request.getName();
          Entry entry = entries.get(dn);
          if (entry == null)
          {
            resultHandler.handleErrorResult(ErrorResultException
                .newErrorResult(ResultCode.NO_SUCH_OBJECT,
                    "The entry " + dn.toString() + " does not exist"));
          }
          Entry newEntry = new LinkedHashMapEntry(entry);
          for (Modification mod : request.getModifications())
          {
            ModificationType modType = mod.getModificationType();
            if (modType.equals(ModificationType.ADD))
            {
              // TODO: Reject empty attribute and duplicate values.
              newEntry.addAttribute(mod.getAttribute(), null);
            }
            else if (modType.equals(ModificationType.DELETE))
            {
              // TODO: Reject missing values.
              newEntry.removeAttribute(mod.getAttribute(), null);
            }
            else if (modType.equals(ModificationType.REPLACE))
            {
              newEntry.replaceAttribute(mod.getAttribute());
            }
            else
            {
              resultHandler.handleErrorResult(newErrorResult(
                  ResultCode.PROTOCOL_ERROR,
                  "Modify request contains an unsupported modification type"));
              return;
            }
          }
          entries.put(dn, newEntry);
          resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
        }
        finally
        {
          entryLock.readLock().unlock();
        }
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleModifyDN(final Integer requestContext,
          final ModifyDNRequest request,
          final ResultHandler<? super Result> resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        // TODO: not implemented.
        resultHandler.handleErrorResult(newErrorResult(
            ResultCode.PROTOCOL_ERROR,
            "ModifyDN request operation not supported"));
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public void handleSearch(final Integer requestContext,
          final SearchRequest request, final SearchResultHandler resultHandler,
          final IntermediateResponseHandler intermediateResponseHandler)
          throws UnsupportedOperationException
      {
        // TODO: controls, limits, etc.
        entryLock.readLock().lock();
        try
        {
          DN dn = request.getName();
          Entry baseEntry = entries.get(dn);
          if (baseEntry == null)
          {
            resultHandler.handleErrorResult(ErrorResultException
                .newErrorResult(ResultCode.NO_SUCH_OBJECT,
                    "The entry " + dn.toString() + " does not exist"));
          }
          SearchScope scope = request.getScope();
          if (scope.equals(SearchScope.BASE_OBJECT))
          {
            sendEntry(request, resultHandler, baseEntry);
          }
          else if (scope.equals(SearchScope.SINGLE_LEVEL))
          {
            sendEntry(request, resultHandler, baseEntry);
            NavigableMap<DN, Entry> subtree = entries.tailMap(dn, false);
            for (Entry entry : subtree.values())
            {
              DN childDN = entry.getName();
              if (childDN.isChildOf(dn))
              {
                if (!sendEntry(request, resultHandler, entry))
                {
                  // Caller has asked to stop sending results.
                  break;
                }
              }
              else if (!childDN.isSubordinateOrEqualTo(dn))
              {
                // The remaining entries will be out of scope.
                break;
              }
            }
          }
          else if (scope.equals(SearchScope.WHOLE_SUBTREE))
          {
            NavigableMap<DN, Entry> subtree = entries.tailMap(dn);
            for (Entry entry : subtree.values())
            {
              DN childDN = entry.getName();
              if (childDN.isSubordinateOrEqualTo(dn))
              {
                if (!sendEntry(request, resultHandler, entry))
                {
                  // Caller has asked to stop sending results.
                  break;
                }
              }
              else
              {
                // The remaining entries will be out of scope.
                break;
              }
            }
          }
          else
          {
            resultHandler.handleErrorResult(newErrorResult(
                ResultCode.PROTOCOL_ERROR,
                "Search request contains an unsupported search scope"));
            return;
          }
          resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
        }
        finally
        {
          entryLock.readLock().unlock();
        }
      }
      private boolean sendEntry(SearchRequest request,
          SearchResultHandler resultHandler, Entry entry)
      {
        // TODO: check filter, strip attributes.
        return resultHandler.handleEntry(Responses.newSearchResultEntry(entry));
      }
    }
    private final ConcurrentSkipListMap<DN, Entry> entries;
    private final ReentrantReadWriteLock entryLock = new ReentrantReadWriteLock();
    private Store(final ConcurrentSkipListMap<DN, Entry> entries)
    {
      this.entries = entries;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ServerConnection<Integer> handleAccept(
        final LDAPClientContext clientContext) throws ErrorResultException
    {
      return new ServerConnectionImpl(clientContext);
    }
  }
  /**
   * Main method.
   *
   * @param args
   *          The command line arguments: listen address, listen port, ldifFile
   */
  public static void main(final String[] args)
  {
    if (args.length != 3)
    {
      System.err.println("Usage: listenAddress listenPort ldifFile");
      System.exit(1);
    }
    // Parse command line arguments.
    final String localAddress = args[0];
    final int localPort = Integer.parseInt(args[1]);
    // Read the LDIF.
    InputStream ldif;
    try
    {
      ldif = new FileInputStream(args[2]);
    }
    catch (final FileNotFoundException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue());
      return;
    }
    final LDIFEntryReader reader = new LDIFEntryReader(ldif);
    ConcurrentSkipListMap<DN, Entry> entries = new ConcurrentSkipListMap<DN, Entry>();
    try
    {
      while (reader.hasNext())
      {
        Entry entry = reader.readEntry();
        entries.put(entry.getName(), entry);
      }
    }
    catch (final IOException e)
    {
      System.err.println(e.getMessage());
      System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue());
      return;
    }
    finally
    {
      try
      {
        reader.close();
      }
      catch (final IOException ignored)
      {
        // Ignore.
      }
    }
    // Quickly sanity check that every entry (except root entries) have a
    // parent.
    boolean isValid = true;
    for (DN dn : entries.keySet())
    {
      if (dn.size() > 1)
      {
        DN parent = dn.parent();
        if (!entries.containsKey(parent))
        {
          System.err.println("The entry \"" + dn.toString()
              + "\" does not have a parent");
          isValid = false;
        }
      }
    }
    if (!isValid)
    {
      System.exit(1);
    }
    // Create listener.
    final LDAPListenerOptions options = new LDAPListenerOptions()
        .setBacklog(4096);
    LDAPListener listener = null;
    try
    {
      listener = new LDAPListener(localAddress, localPort, new Store(entries),
          options);
      System.out.println("Press any key to stop the server...");
      System.in.read();
    }
    catch (final IOException e)
    {
      System.out
          .println("Error listening on " + localAddress + ":" + localPort);
      e.printStackTrace();
    }
    finally
    {
      if (listener != null)
      {
        listener.close();
      }
    }
  }
  private Main()
  {
    // Not used.
  }
}
opendj-sdk/sdk/examples/org/opends/sdk/examples/server/store/package-info.java
New file
@@ -0,0 +1,35 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
/**
 * An example LDAP data store which exposes data which is contained within an
 * LDIF file.
 */
package org.opends.sdk.examples.server.store;
opendj-sdk/sdk/lib/gmbal-api-only.jar
Binary files differ
opendj-sdk/sdk/lib/grizzly.jar
Binary files differ
opendj-sdk/sdk/nbproject/genfiles.properties
New file
@@ -0,0 +1,5 @@
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/jdk.xml.data.CRC32=f285a38e
nbproject/jdk.xml.script.CRC32=5342cb35
nbproject/jdk.xml.stylesheet.CRC32=c45af3dc
opendj-sdk/sdk/nbproject/ide-file-targets.xml
New file
@@ -0,0 +1,218 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir=".." name="Directory Server-IDE">
    <!-- Import build.xml properties and targets -->
    <import file="../build.xml"/>
    <path id="opends.path">
        <pathelement path="build/classes:build/build-tools/classes:lib/activation.jar:lib/aspectjrt.jar:lib/je.jar:lib/mail.jar:build/quicksetup/classes:build/build-tools/build-tools.jar:build/unit-tests/classes:ext/testng/lib/testng-5.7b-jdk15.jar:ext/ant/lib/ant.jar:ext/svnkit/svnkit.jar:ext/emma/lib/emma.jar:build/dsml/classes:resource/dsml/lib/jaxb-api.jar:resource/dsml/lib/jaxb-impl.jar:resource/dsml/lib/jsr173_1.0_api.jar:resource/dsml/lib/saaj-1.3.jar:resource/dsml/lib/saaj-impl-1.3.jar:resource/dsml/lib/j2ee.jar"/>
        <pathelement location="build/build-tools/classes"/>
        <pathelement location="build/classes"/>
        <pathelement location="build/quicksetup/classes"/>
        <pathelement location="build/unit-tests/classes"/>
        <pathelement location="build/dsml/classes"/>
        <fileset dir="${opendmk.lib.dir}">
            <include name="*.jar"/>
        </fileset>
    </path>
    <!-- Prepare testng unit tests environment -->
    <taskdef resource="testngtasks">
        <classpath>
            <fileset dir="${testng.lib.dir}">
                <include name="*.jar"/>
            </fileset>
        </classpath>
    </taskdef>
    <target name="prepare-test">
        <delete failonerror="false">
            <fileset dir="${unittest.report.dir}" includes="*"/>
        </delete>
        <mkdir dir="${unittest.report.dir}"/>
    </target>
    <!-- -->
    <!-- Run a selected testng file -->
    <!-- -->
    <target depends="prepare-test" name="run-selected-testng-file">
        <fail unless="run.class">Must set property 'run.class'</fail>
        <echo message="Running test (normal): ${run.class}"/>
        <testng dumpCommand="true" enableAssert="false" haltonfailure="false" listeners="org.opends.server.TestListener org.testng.reporters.FailedReporter" outputdir="${unittest.report.dir}" suiteRunnerClass="org.opends.server.SuiteRunner" useDefaultListeners="false" verbose="0">
            <classpath refid="opends.path"/>
            <jvmarg value="-Dorg.opends.server.BuildRoot=${basedir}"/>
            <jvmarg value="-Dorg.opends.server.RunningUnitTests=true"/>
            <jvmarg value="-Dorg.opends.test.suppressOutput=false"/>
            <jvmarg value="-Dorg.opends.test.pauseOnFailure=false"/>
            <jvmarg value="-Dorg.opends.test.debug.target=false"/>
            <jvmarg value="-Dorg.opends.server.snmp.opendmk=${opendmk.lib.dir}"/>
            <jvmarg value="-Dorg.opends.test.copyClassesToTestPackage=true"/>
            <jvmarg value="-Dtest.progress=all"/>
            <jvmarg value="-Xms192M"/>
            <jvmarg value="-Xmx192M"/>
            <classfileset file="${unittest.classes.dir}/${run.class}.class"/>
        </testng>
    </target>
    <!-- -->
    <!-- Debug a selected file in testng unit tests sources -->
    <!-- -->
    <target name="debug-selected-testng-file">
        <fail unless="debug.class">Must set property 'debug.class'</fail>
        <echo message="Debugging test (normal): ${debug.class}"/>
        <ant antfile="build.xml" inheritall="false" target="dynamicconstants"/>
        <nbjpdastart addressproperty="jpda.address" name="Directory Server" transport="dt_socket">
            <classpath refid="opends.path"/>
        </nbjpdastart>
        <testng enableAssert="false" haltonfailure="false" listeners="org.opends.server.TestListener org.testng.reporters.FailedReporter" outputdir="${unittest.report.dir}" suiteRunnerClass="org.opends.server.SuiteRunner" useDefaultListeners="false" verbose="5">
            <classpath refid="opends.path"/>
            <jvmarg value="-Dorg.opends.server.LdapPort=1389"/>
            <jvmarg value="-Dorg.opends.server.BuildRoot=${basedir}"/>
            <jvmarg value="-Dorg.opends.server.RunningUnitTests=true"/>
            <jvmarg value="-Dorg.opends.test.suppressOutput=false"/>
            <jvmarg value="-Dorg.opends.test.pauseOnFailure=false"/>
            <jvmarg value="-Dorg.opends.test.debug.target=false"/>
            <jvmarg value="-Dorg.opends.server.snmp.opendmk=${opendmk.lib.dir}"/>
            <jvmarg value="-Dorg.opends.test.copyClassesToTestPackage=true"/>
            <jvmarg value="-Dtest.progress=all"/>
            <jvmarg value="-Xms192M"/>
            <jvmarg value="-Xmx192M"/>
            <jvmarg value="-Xdebug"/>
            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
            <classfileset file="${unittest.classes.dir}/${debug.class}.class"/>
        </testng>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/server folder -->
    <!-- -->
    <target name="compile-selected-files-in-server">
        <fail unless="files">Must set property 'files'</fail>
        <echo message="Compiling source (normal): ${files}"/>
        <!-- TODO decide on and define some value for ${build.classes.dir} -->
        <mkdir dir="${classes.dir}"/>
        <javac destdir="${classes.dir}" includes="${files}" source="1.5" srcdir="src/server">
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Compile a selected file in testng unit tests source folder -->
    <!-- -->
    <target name="compile-selected-testng-file">
        <!-- Compile the test cases -->
        <echo message="Compiling test (normal): ${files}"/>
        <mkdir dir="${unittest.classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" deprecation="true" destdir="${unittest.classes.dir}" fork="true" includes="${files}" memoryInitialSize="${MEM}" memoryMaximumSize="${MEM}" source="1.5" srcdir="${unittest.testng.src.dir}" target="1.5">
            <compilerarg value="-Xlint:all"/>
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Run a selected class in src/server folder -->
    <!-- -->
    <target depends="dynamicconstants" name="run-selected-file-in-server">
        <fail unless="run.class">Must set property 'run.class'</fail>
        <property location="${package.dir}/${SHORT_NAME}-${VERSION_NUMBER_STRING}" name="pdir"/>
        <ant antfile="build.xml" inheritall="false" target="dynamicconstants"/>
        <java classname="${run.class}" failonerror="true" fork="true">
            <classpath refid="opends.path"/>
            <jvmarg value="-Dorg.opends.server.BuildRoot=${pdir}"/>
            <jvmarg value="-Dorg.opends.server.scriptName=start-ds"/>
            <jvmarg value="-Dorg.opends.server.ServerRoot=${pdir}"/>
            <arg value="--configClass=org.opends.server.extensions.ConfigFileHandler"/>
            <arg value="--configFile=${pdir}/config/config.ldif"/>
            <arg value="--nodetach"/>
        </java>
    </target>
    <!-- -->
    <!-- Debug a selected file in src/server folder -->
    <!-- -->
    <target depends="dynamicconstants" name="debug-selected-file-in-server">
        <fail unless="debug.class">Must set property 'debug.class'</fail>
        <property location="${package.dir}/${SHORT_NAME}-${VERSION_NUMBER_STRING}" name="pdir"/>
        <ant antfile="build.xml" inheritall="false" target="dynamicconstants"/>
        <nbjpdastart addressproperty="jpda.address" name="Directory Server" transport="dt_socket">
            <classpath refid="opends.path"/>
        </nbjpdastart>
        <java classname="${debug.class}" fork="true">
            <classpath refid="cp"/>
            <jvmarg value="-Xdebug"/>
            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
            <jvmarg value="-Dorg.opends.server.BuildRoot=${pdir}"/>
            <jvmarg value="-Dorg.opends.server.scriptName=start-ds"/>
            <jvmarg value="-Dorg.opends.server.ServerRoot=${pdir}"/>
            <jvmarg value="-Dorg.opends.server.debug.enabled=true"/>
            <arg value="--configClass=org.opends.server.extensions.ConfigFileHandler"/>
            <arg value="--configFile=${pdir}/config/config.ldif"/>
            <arg value="--nodetach"/>
        </java>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/ads folder -->
    <!-- -->
    <target name="compile-selected-files-in-ads">
        <fail unless="files">Must set property 'files'</fail>
        <echo message="Compiling source (normal): ${files}"/>
        <mkdir dir="${classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" destdir="${classes.dir}" includes="${files}" source="1.5" srcdir="src/ads">
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/build-tools folder -->
    <!-- -->
    <target name="compile-selected-files-in-build-tools">
        <fail unless="files">Must set property 'files'</fail>
        <echo message="Compiling source (normal): ${files}"/>
        <mkdir dir="${buildtools.classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" destdir="${buildtools.classes.dir}" includes="${files}" source="1.5" srcdir="src/build-tools">
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/guitools folder -->
    <!-- -->
    <target name="compile-selected-files-in-guitools">
        <fail unless="files">Must set property 'files'</fail>
        <echo message="Compiling source (normal): ${files}"/>
        <mkdir dir="${classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" destdir="${classes.dir}" includes="${files}" source="1.5" srcdir="src/guitools">
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/messages/src folder -->
    <!-- -->
    <target name="compile-selected-files-in-messages-src">
        <fail unless="files">Must set property 'files'</fail>
        <echo message="Compiling source (normal): ${files}"/>
        <mkdir dir="${classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" destdir="${classes.dir}" includes="${files}" source="1.5" srcdir="src/messages/src">
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/quicksetup folder -->
    <!-- -->
    <target name="compile-selected-files-in-quicksetup">
        <fail unless="files">Must set property 'files'</fail>
        <mkdir dir="${quicksetup.classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" destdir="${quicksetup.classes.dir}" includes="${files}" source="1.5" srcdir="src/quicksetup">
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/dsml folder -->
    <!-- -->
    <target name="compile-selected-files-in-dsml">
        <fail unless="files">Must set property 'files'</fail>
        <echo message="Compiling source (normal): ${files}"/>
        <mkdir dir="${dsml.classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" destdir="${dsml.classes.dir}" includes="${files}" source="1.5" srcdir="src/dsml">
            <classpath refid="opends.path"/>
        </javac>
    </target>
    <!-- -->
    <!-- Compile a selected file in src/snmp/src folder -->
    <!-- -->
    <target name="compile-selected-files-in-snmp">
        <fail unless="files">Must set property 'files'</fail>
        <mkdir dir="${classes.dir}"/>
        <javac debug="on" debuglevel="${build.debuglevel}" destdir="${classes.dir}" includes="${files}" source="1.5" srcdir="src/snmp/src">
            <classpath refid="opends.path"/>
        </javac>
    </target>
</project>
opendj-sdk/sdk/nbproject/ide-targets.xml
New file
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir=".." name="Directory Server-IDE">
    <!-- Import build.xml definitions -->
    <import file="../build.xml"/>
    <!-- OpenDS path -->
    <path id="opends.path">
        <pathelement path="build/classes:build/build-tools/classes:lib/activation.jar:lib/aspectjrt.jar:lib/je.jar:lib/mail.jar:build/quicksetup/classes:build/build-tools/build-tools.jar:build/unit-tests/classes:ext/testng/lib/testng-5.7b-jdk15.jar:ext/ant/lib/ant.jar:ext/svnkit/svnkit.jar:ext/emma/lib/emma.jar:build/dsml/classes:resource/dsml/lib/jaxb-api.jar:resource/dsml/lib/jaxb-impl.jar:resource/dsml/lib/jsr173_1.0_api.jar:resource/dsml/lib/saaj-1.3.jar:resource/dsml/lib/saaj-impl-1.3.jar:resource/dsml/lib/j2ee.jar"/>
        <pathelement location="build/build-tools/classes"/>
        <pathelement location="build/classes"/>
        <pathelement location="build/quicksetup/classes"/>
        <pathelement location="build/unit-tests/classes"/>
        <pathelement location="build/dsml/classes"/>
    </path>
    <!-- -->
    <!-- Debug target call by NetBeans IDE -->
    <!-- -->
    <target depends="dynamicconstants" name="debug-nb">
        <!-- Set properties needed to find the packaged files -->
        <property location="${package.dir}/${SHORT_NAME}-${VERSION_NUMBER_STRING}" name="pdir"/>
        <nbjpdastart addressproperty="jpda.address" name="Directory Server" transport="dt_socket">
            <classpath refid="opends.path"/>
        </nbjpdastart>
        <java classname="org.opends.server.core.DirectoryServer" failonerror="true" fork="true">
            <classpath refid="opends.path"/>
            <jvmarg value="-Dorg.opends.server.BuildRoot=${pdir}"/>
            <jvmarg value="-Dorg.opends.server.scriptName=start-ds"/>
            <jvmarg value="-Dorg.opends.server.ServerRoot=${pdir}"/>
            <arg value="--configClass=org.opends.server.extensions.ConfigFileHandler"/>
            <arg value="--configFile=${pdir}/config/config.ldif"/>
            <arg value="--nodetach"/>
            <jvmarg value="-Xdebug"/>
            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
        </java>
    </target>
</project>
opendj-sdk/sdk/nbproject/project.xml
New file
@@ -0,0 +1,283 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
    <type>org.netbeans.modules.ant.freeform</type>
    <configuration>
        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
            <name>OpenDJ SDK</name>
        </general-data>
        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/2">
            <!-- Do not use Project Properties customizer when editing this file manually. -->
            <name>OpenDJ SDK</name>
            <properties/>
            <folders>
                <source-folder>
                    <label>OpenDJ SDK</label>
                    <location>.</location>
                    <encoding>UTF-8</encoding>
                </source-folder>
                <source-folder>
                    <label>src</label>
                    <type>java</type>
                    <location>src</location>
                    <encoding>UTF-8</encoding>
                </source-folder>
                <source-folder>
                    <label>tests/unit-tests-testng/src</label>
                    <type>java</type>
                    <location>tests/unit-tests-testng/src</location>
                    <encoding>UTF-8</encoding>
                </source-folder>
            </folders>
            <ide-actions>
                <action name="build">
                    <script>build.xml</script>
                    <target>package</target>
                </action>
                <action name="clean">
                    <script>build.xml</script>
                    <target>clean</target>
                </action>
                <action name="javadoc">
                    <script>build.xml</script>
                    <target>javadoc</target>
                </action>
                <action name="run">
                    <script>build.xml</script>
                    <target>run-server</target>
                </action>
                <action name="test">
                    <script>build.xml</script>
                    <target>test</target>
                </action>
                <action name="rebuild">
                    <script>build.xml</script>
                    <target>clean</target>
                    <target>package</target>
                </action>
                <action name="debug">
                    <script>nbproject/ide-targets.xml</script>
                    <target>debug-nb</target>
                </action>
                <action name="run.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>run-selected-testng-file</target>
                    <context>
                        <property>run.class</property>
                        <folder>tests/unit-tests-testng/src/server</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path-noext</format>
                        <arity>
                            <one-file-only/>
                        </arity>
                    </context>
                </action>
                <action name="debug.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>debug-selected-testng-file</target>
                    <context>
                        <property>debug.class</property>
                        <folder>tests/unit-tests-testng/src/server</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path-noext</format>
                        <arity>
                            <one-file-only/>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-server</target>
                    <context>
                        <property>files</property>
                        <folder>src/server</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-testng-file</target>
                    <context>
                        <property>files</property>
                        <folder>tests/unit-tests-testng/src/server</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="run.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>run-selected-file-in-server</target>
                    <context>
                        <property>run.class</property>
                        <folder>src/server</folder>
                        <pattern>\.java$</pattern>
                        <format>java-name</format>
                        <arity>
                            <one-file-only/>
                        </arity>
                    </context>
                </action>
                <action name="debug.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>debug-selected-file-in-server</target>
                    <context>
                        <property>debug.class</property>
                        <folder>src/server</folder>
                        <pattern>\.java$</pattern>
                        <format>java-name</format>
                        <arity>
                            <one-file-only/>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-ads</target>
                    <context>
                        <property>files</property>
                        <folder>src/ads</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-build-tools</target>
                    <context>
                        <property>files</property>
                        <folder>src/build-tools</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-guitools</target>
                    <context>
                        <property>files</property>
                        <folder>src/guitools</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-messages-src</target>
                    <context>
                        <property>files</property>
                        <folder>src/messages/src</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-quicksetup</target>
                    <context>
                        <property>files</property>
                        <folder>src/quicksetup</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-dsml</target>
                    <context>
                        <property>files</property>
                        <folder>src/dsml</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-snmp</target>
                    <context>
                        <property>files</property>
                        <folder>src/snmp/src</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
                <action name="compile.single">
                    <script>nbproject/ide-file-targets.xml</script>
                    <target>compile-selected-files-in-server</target>
                    <context>
                        <property>files</property>
                        <folder>tests/unit-tests-testng/src/server</folder>
                        <pattern>\.java$</pattern>
                        <format>relative-path</format>
                        <arity>
                            <separated-files>,</separated-files>
                        </arity>
                    </context>
                </action>
            </ide-actions>
            <view>
                <items>
                    <source-folder style="packages">
                        <label>src</label>
                        <location>src</location>
                    </source-folder>
                    <source-folder style="packages">
                        <label>tests/unit-tests-testng/src</label>
                        <location>tests/unit-tests-testng/src</location>
                    </source-folder>
                    <source-file>
                        <location>build.xml</location>
                    </source-file>
                </items>
                <context-menu>
                    <ide-action name="build"/>
                    <ide-action name="rebuild"/>
                    <ide-action name="clean"/>
                    <ide-action name="javadoc"/>
                    <ide-action name="run"/>
                    <ide-action name="test"/>
                    <ide-action name="debug"/>
                </context-menu>
            </view>
            <subprojects/>
        </general-data>
        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3">
            <compilation-unit>
                <package-root>src</package-root>
                <classpath mode="compile">src-generated</classpath>
                <source-level>1.6</source-level>
            </compilation-unit>
            <compilation-unit>
                <package-root>tests/unit-tests-testng/src</package-root>
                <unit-tests/>
                <source-level>1.6</source-level>
            </compilation-unit>
        </java-data>
    </configuration>
</project>
opendj-sdk/sdk/resource/example-2000.ldif.zip
Binary files differ
opendj-sdk/sdk/src/com/sun/opends/sdk/extensions/GetConnectionIDExtendedRequest.java
@@ -38,6 +38,7 @@
import org.opends.sdk.requests.AbstractExtendedRequest;
import org.opends.sdk.requests.ExtendedRequest;
import org.opends.sdk.requests.ExtendedRequestDecoder;
import org.opends.sdk.responses.AbstractExtendedResultDecoder;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
@@ -75,13 +76,13 @@
  private static final class ResultDecoder implements
      ExtendedResultDecoder<GetConnectionIDExtendedResult>
  private static final class ResultDecoder extends
      AbstractExtendedResultDecoder<GetConnectionIDExtendedResult>
  {
    /**
     * {@inheritDoc}
     */
    public GetConnectionIDExtendedResult adaptExtendedErrorResult(
    public GetConnectionIDExtendedResult newExtendedErrorResult(
        final ResultCode resultCode, final String matchedDN,
        final String diagnosticMessage)
    {
opendj-sdk/sdk/src/com/sun/opends/sdk/extensions/GetSymmetricKeyExtendedRequest.java
@@ -42,6 +42,7 @@
import org.opends.sdk.requests.AbstractExtendedRequest;
import org.opends.sdk.requests.ExtendedRequest;
import org.opends.sdk.requests.ExtendedRequestDecoder;
import org.opends.sdk.responses.AbstractExtendedResultDecoder;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
import org.opends.sdk.responses.Responses;
@@ -116,11 +117,11 @@
  private static final class ResultDecoder implements
      ExtendedResultDecoder<ExtendedResult>
  private static final class ResultDecoder extends
      AbstractExtendedResultDecoder<ExtendedResult>
  {
    public ExtendedResult adaptExtendedErrorResult(final ResultCode resultCode,
    public ExtendedResult newExtendedErrorResult(final ResultCode resultCode,
        final String matchedDN, final String diagnosticMessage)
    {
      return Responses.newGenericExtendedResult(resultCode).setMatchedDN(
opendj-sdk/sdk/src/com/sun/opends/sdk/extensions/PasswordPolicyStateExtendedRequest.java
@@ -49,6 +49,7 @@
import org.opends.sdk.requests.AbstractExtendedRequest;
import org.opends.sdk.requests.ExtendedRequest;
import org.opends.sdk.requests.ExtendedRequestDecoder;
import org.opends.sdk.responses.AbstractExtendedResultDecoder;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
@@ -227,14 +228,14 @@
  private static final class ResultDecoder implements
      ExtendedResultDecoder<PasswordPolicyStateExtendedResult>
  private static final class ResultDecoder extends
      AbstractExtendedResultDecoder<PasswordPolicyStateExtendedResult>
  {
    /**
     * {@inheritDoc}
     */
    public PasswordPolicyStateExtendedResult adaptExtendedErrorResult(
    public PasswordPolicyStateExtendedResult newExtendedErrorResult(
        final ResultCode resultCode, final String matchedDN,
        final String diagnosticMessage)
    {
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferReader.java
@@ -45,9 +45,9 @@
import org.opends.sdk.asn1.ASN1Reader;
import org.opends.sdk.asn1.AbstractASN1Reader;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.memory.ByteBuffersBuffer;
import com.sun.grizzly.memory.CompositeBuffer;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.memory.ByteBuffersBuffer;
import org.glassfish.grizzly.memory.CompositeBuffer;
import com.sun.opends.sdk.util.StaticUtils;
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferWriter.java
@@ -42,10 +42,10 @@
import org.opends.sdk.asn1.ASN1Writer;
import org.opends.sdk.asn1.AbstractASN1Writer;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.Cacheable;
import com.sun.grizzly.ThreadCache;
import com.sun.grizzly.memory.ByteBufferWrapper;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Cacheable;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.memory.ByteBufferWrapper;
import com.sun.opends.sdk.util.StaticUtils;
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/AbstractLDAPFutureResultImpl.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package com.sun.opends.sdk.ldap;
@@ -34,7 +34,7 @@
import org.opends.sdk.responses.IntermediateResponse;
import org.opends.sdk.responses.Result;
import com.sun.opends.sdk.util.AbstractFutureResult;
import com.sun.opends.sdk.util.AsynchronousFutureResult;
@@ -45,12 +45,11 @@
 *          The type of result returned by this future.
 */
abstract class AbstractLDAPFutureResultImpl<S extends Result> extends
    AbstractFutureResult<S> implements FutureResult<S>,
    IntermediateResponseHandler
    AsynchronousFutureResult<S> implements IntermediateResponseHandler
{
  private final AsynchronousConnection connection;
  private final int messageID;
  private final int requestID;
  private IntermediateResponseHandler intermediateResponseHandler;
@@ -58,13 +57,13 @@
  AbstractLDAPFutureResultImpl(final int messageID,
  AbstractLDAPFutureResultImpl(final int requestID,
      final ResultHandler<? super S> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final AsynchronousConnection connection)
  {
    super(resultHandler);
    this.messageID = messageID;
    this.requestID = requestID;
    this.connection = connection;
    this.intermediateResponseHandler = intermediateResponseHandler;
    this.timestamp = System.currentTimeMillis();
@@ -75,13 +74,15 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public final int getRequestID()
  {
    return messageID;
    return requestID;
  }
  @Override
  public final boolean handleIntermediateResponse(
      final IntermediateResponse response)
  {
@@ -112,7 +113,7 @@
  protected final ErrorResultException handleCancelRequest(
      final boolean mayInterruptIfRunning)
  {
    connection.abandon(Requests.newAbandonRequest(messageID));
    connection.abandon(Requests.newAbandonRequest(requestID));
    return null;
  }
@@ -121,8 +122,8 @@
  @Override
  protected void toString(final StringBuilder sb)
  {
    sb.append(" messageID = ");
    sb.append(messageID);
    sb.append(" requestID = ");
    sb.append(requestID);
    sb.append(" timestamp = ");
    sb.append(timestamp);
    super.toString(sb);
@@ -132,8 +133,8 @@
  final void adaptErrorResult(final Result result)
  {
    final S errorResult = newErrorResult(result.getResultCode(), result
        .getDiagnosticMessage(), result.getCause());
    final S errorResult = newErrorResult(result.getResultCode(),
        result.getDiagnosticMessage(), result.getCause());
    setResultOrError(errorResult);
  }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java
@@ -31,11 +31,11 @@
import java.io.IOException;
import com.sun.grizzly.TransportFactory;
import com.sun.grizzly.nio.NIOTransportFactory;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import com.sun.grizzly.nio.transport.UDPNIOTransport;
import com.sun.grizzly.threadpool.ThreadPoolConfig;
import org.glassfish.grizzly.TransportFactory;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.UDPNIOTransport;
import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
import com.sun.opends.sdk.util.StaticUtils;
@@ -43,9 +43,9 @@
/**
 * A static Grizzly transport that can be used by default globally in the SDK.
 */
public final class GlobalTransportFactory extends NIOTransportFactory
public final class GlobalTransportFactory extends TransportFactory
{
  private static final GlobalTransportFactory INSTANCE = new GlobalTransportFactory();
  private static boolean isInitialized = false;
@@ -54,22 +54,14 @@
   *
   * @return The global Grizzly transport factory.
   */
  public static TransportFactory getInstance()
  public static synchronized TransportFactory getInstance()
  {
    return INSTANCE;
  }
  /**
   * Sets the global Grizzly transport factory.
   *
   * @param factory
   *          The global Grizzly transport factory.
   */
  public static void setInstance(final TransportFactory factory)
  {
    throw new UnsupportedOperationException("not yet implemented");
    if (!isInitialized)
    {
      TransportFactory.setInstance(new GlobalTransportFactory());
      isInitialized = true;
    }
    return TransportFactory.getInstance();
  }
@@ -94,7 +86,7 @@
  /**
   * Close the {@link com.sun.grizzly.TransportFactory} and release all
   * Close the {@link org.glassfish.grizzly.TransportFactory} and release all
   * resources.
   */
  @Override
@@ -118,9 +110,9 @@
  /**
   * Create instance of TCP {@link com.sun.grizzly.Transport}.
   * Create instance of TCP {@link org.glassfish.grizzly.Transport}.
   *
   * @return instance of TCP {@link com.sun.grizzly.Transport}.
   * @return instance of TCP {@link org.glassfish.grizzly.Transport}.
   */
  @Override
  public synchronized TCPNIOTransport createTCPTransport()
@@ -149,8 +141,9 @@
  /**
   * Creating an UDP transport is unsupported with this factory. A {@code
   * UnsupportedOperationException} will be thrown when this method is called.
   * Creating an UDP transport is unsupported with this factory. A
   * {@code UnsupportedOperationException} will be thrown when this method is
   * called.
   *
   * @return This method will always throw {@code UnsupportedOperationException}
   *         .
@@ -210,4 +203,5 @@
    super.initialize();
  }
}
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPListenerOptions.java
@@ -31,7 +31,7 @@
import org.opends.sdk.LDAPListenerOptions;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPOptions.java
@@ -31,7 +31,7 @@
import org.opends.sdk.LDAPOptions;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java
@@ -50,8 +50,7 @@
public final class InternalConnection extends AbstractAsynchronousConnection
{
  private static final class InternalBindFutureResultImpl extends
      AbstractLDAPFutureResultImpl<BindResult> implements
      FutureResult<BindResult>
      AbstractLDAPFutureResultImpl<BindResult>
  {
    private final BindRequest bindRequest;
@@ -90,8 +89,8 @@
    BindResult newErrorResult(final ResultCode resultCode,
        final String diagnosticMessage, final Throwable cause)
    {
      return Responses.newBindResult(resultCode).setDiagnosticMessage(
          diagnosticMessage).setCause(cause);
      return Responses.newBindResult(resultCode)
          .setDiagnosticMessage(diagnosticMessage).setCause(cause);
    }
  }
@@ -120,6 +119,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Void> abandon(final AbandonRequest request)
      throws UnsupportedOperationException, IllegalStateException,
      NullPointerException
@@ -134,6 +134,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> add(final AddRequest request,
      final ResultHandler<? super Result> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler)
@@ -152,6 +153,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public void addConnectionEventListener(final ConnectionEventListener listener)
      throws IllegalStateException, NullPointerException
  {
@@ -164,6 +166,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<BindResult> bind(final BindRequest request,
      final ResultHandler<? super BindResult> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler)
@@ -182,6 +185,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public void close(final UnbindRequest request, final String reason)
  {
    final int i = messageID.getAndIncrement();
@@ -193,6 +197,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<CompareResult> compare(final CompareRequest request,
      final ResultHandler<? super CompareResult> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler)
@@ -211,6 +216,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> delete(final DeleteRequest request,
      final ResultHandler<? super Result> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler)
@@ -229,6 +235,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public <R extends ExtendedResult> FutureResult<R> extendedRequest(
      final ExtendedRequest<R> request,
      final ResultHandler<? super R> resultHandler,
@@ -248,6 +255,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isClosed()
  {
    // FIXME: this should be true after close has been called.
@@ -259,6 +267,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isValid()
  {
    // FIXME: this should be false if this connection is disconnected.
@@ -270,6 +279,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> modify(final ModifyRequest request,
      final ResultHandler<? super Result> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler)
@@ -288,6 +298,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> modifyDN(final ModifyDNRequest request,
      final ResultHandler<? super Result> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler)
@@ -306,6 +317,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public void removeConnectionEventListener(
      final ConnectionEventListener listener) throws NullPointerException
  {
@@ -318,6 +330,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<Result> search(final SearchRequest request,
      final SearchResultHandler resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler)
@@ -327,7 +340,22 @@
    final int i = messageID.getAndIncrement();
    final LDAPSearchFutureResultImpl future = new LDAPSearchFutureResultImpl(i,
        request, resultHandler, intermediateResponseHandler, this);
    serverConnection.handleSearch(i, request, future, future, future);
    serverConnection.handleSearch(i, request, future, future);
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    StringBuilder builder = new StringBuilder();
    builder.append("InternalConnection(");
    builder.append(String.valueOf(serverConnection));
    builder.append(')');
    return builder.toString();
  }
}
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java
@@ -40,19 +40,18 @@
 * Bind result future implementation.
 */
final class LDAPBindFutureResultImpl extends
    AbstractLDAPFutureResultImpl<BindResult> implements
    FutureResult<BindResult>
    AbstractLDAPFutureResultImpl<BindResult>
{
  private final BindClient bindClient;
  LDAPBindFutureResultImpl(final int messageID, final BindClient bindClient,
  LDAPBindFutureResultImpl(final int requestID, final BindClient bindClient,
      final ResultHandler<? super BindResult> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final AsynchronousConnection connection)
  {
    super(messageID, resultHandler, intermediateResponseHandler, connection);
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.bindClient = bindClient;
  }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java
@@ -43,14 +43,14 @@
import org.opends.sdk.requests.*;
import org.opends.sdk.responses.*;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.Connection;
import com.sun.grizzly.EmptyCompletionHandler;
import com.sun.grizzly.Grizzly;
import com.sun.grizzly.attributes.Attribute;
import com.sun.grizzly.filterchain.BaseFilter;
import com.sun.grizzly.filterchain.FilterChainContext;
import com.sun.grizzly.filterchain.NextAction;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.EmptyCompletionHandler;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.filterchain.BaseFilter;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.filterchain.NextAction;
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPCompareFutureResultImpl.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package com.sun.opends.sdk.ldap;
@@ -40,20 +40,19 @@
 * Compare result future implementation.
 */
final class LDAPCompareFutureResultImpl extends
    AbstractLDAPFutureResultImpl<CompareResult> implements
    FutureResult<CompareResult>
    AbstractLDAPFutureResultImpl<CompareResult>
{
  private final CompareRequest request;
  LDAPCompareFutureResultImpl(final int messageID,
  LDAPCompareFutureResultImpl(final int requestID,
      final CompareRequest request,
      final ResultHandler<? super CompareResult> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final AsynchronousConnection connection)
  {
    super(messageID, resultHandler, intermediateResponseHandler, connection);
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
  }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
@@ -44,12 +44,12 @@
import org.opends.sdk.requests.*;
import org.opends.sdk.responses.*;
import com.sun.grizzly.CompletionHandler;
import com.sun.grizzly.filterchain.DefaultFilterChain;
import com.sun.grizzly.filterchain.Filter;
import com.sun.grizzly.filterchain.FilterChain;
import com.sun.grizzly.ssl.SSLEngineConfigurator;
import com.sun.grizzly.ssl.SSLFilter;
import org.glassfish.grizzly.CompletionHandler;
import org.glassfish.grizzly.filterchain.DefaultFilterChain;
import org.glassfish.grizzly.filterchain.Filter;
import org.glassfish.grizzly.filterchain.FilterChain;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.ssl.SSLFilter;
import com.sun.opends.sdk.util.CompletedFutureResult;
import com.sun.opends.sdk.util.StaticUtils;
import com.sun.opends.sdk.util.Validator;
@@ -64,7 +64,7 @@
final class LDAPConnection extends AbstractAsynchronousConnection implements
    AsynchronousConnection
{
  private final com.sun.grizzly.Connection<?> connection;
  private final org.glassfish.grizzly.Connection<?> connection;
  private Result connectionInvalidReason;
@@ -79,9 +79,8 @@
  private boolean bindOrStartTLSInProgress = false;
  private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>
    pendingRequests =
      new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
  private final ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>> pendingRequests =
    new ConcurrentHashMap<Integer, AbstractLDAPFutureResultImpl<?>>();
  private final Object stateLock = new Object();
@@ -99,7 +98,7 @@
   * @param options
   *          The LDAP client options.
   */
  LDAPConnection(final com.sun.grizzly.Connection<?> connection,
  LDAPConnection(final org.glassfish.grizzly.Connection<?> connection,
      final LDAPOptions options)
  {
    this.connection = connection;
@@ -120,20 +119,20 @@
    {
      if (connectionInvalidReason != null)
      {
        return new CompletedFutureResult<Void>(ErrorResultException
            .wrap(connectionInvalidReason), messageID);
        return new CompletedFutureResult<Void>(
            ErrorResultException.wrap(connectionInvalidReason), messageID);
      }
      if (bindOrStartTLSInProgress)
      {
        final Result errorResult = Responses.newResult(
            ResultCode.OPERATIONS_ERROR).setDiagnosticMessage(
            "Bind or Start TLS operation in progress");
        return new CompletedFutureResult<Void>(ErrorResultException
            .wrap(errorResult), messageID);
        return new CompletedFutureResult<Void>(
            ErrorResultException.wrap(errorResult), messageID);
      }
      // First remove the future associated with the request to be abandoned.
      pendingRequest = pendingRequests.remove(request.getMessageID());
      pendingRequest = pendingRequests.remove(request.getRequestID());
    }
    if (pendingRequest == null)
@@ -169,8 +168,8 @@
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_ENCODING_ERROR).setCause(e);
      connectionErrorOccurred(errorResult);
      return new CompletedFutureResult<Void>(ErrorResultException
          .wrap(errorResult), messageID);
      return new CompletedFutureResult<Void>(
          ErrorResultException.wrap(errorResult), messageID);
    }
  }
@@ -258,18 +257,20 @@
    BindClient context;
    try
    {
      context = request.createBindClient(
          connection.getPeerAddress() instanceof InetSocketAddress ?
              ((InetSocketAddress)connection.getPeerAddress()).getHostName() :
              connection.getPeerAddress().toString());
      context = request
          .createBindClient(connection.getPeerAddress() instanceof InetSocketAddress ?
              ((InetSocketAddress) connection
              .getPeerAddress()).getHostName() : connection.getPeerAddress()
              .toString());
    }
    catch (final Exception e)
    {
      // FIXME: I18N need to have a better error message.
      // FIXME: Is this the best result code?
      final Result errorResult = Responses.newResult(
          ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
          "An error occurred while creating a bind context").setCause(e);
      final Result errorResult = Responses
          .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
          .setDiagnosticMessage(
              "An error occurred while creating a bind context").setCause(e);
      final ErrorResultException error = ErrorResultException.wrap(errorResult);
      if (resultHandler != null)
      {
@@ -349,9 +350,13 @@
    // FIXME: I18N need to internationalize this message.
    Validator.ensureNotNull(request);
    close(request, false, Responses.newResult(
        ResultCode.CLIENT_SIDE_USER_CANCELLED).setDiagnosticMessage(
        "Connection closed by client" + (reason != null ? ": " + reason : "")));
    close(
        request,
        false,
        Responses.newResult(ResultCode.CLIENT_SIDE_USER_CANCELLED)
            .setDiagnosticMessage(
                "Connection closed by client"
                    + (reason != null ? ": " + reason : "")));
  }
@@ -494,7 +499,7 @@
      if (bindOrStartTLSInProgress)
      {
        future.setResultOrError(request.getResultDecoder()
            .adaptExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
            .newExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
                "Bind or Start TLS operation in progress"));
        return future;
      }
@@ -503,14 +508,14 @@
        if (!pendingRequests.isEmpty())
        {
          future.setResultOrError(request.getResultDecoder()
              .adaptExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
              .newExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
                  "There are pending operations on this connection"));
          return future;
        }
        if (isTLSEnabled())
        {
          future.setResultOrError(request.getResultDecoder()
              .adaptExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
              .newExtendedErrorResult(ResultCode.OPERATIONS_ERROR, "",
                  "This connection is already TLS enabled"));
        }
        bindOrStartTLSInProgress = true;
@@ -754,13 +759,29 @@
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    StringBuilder builder = new StringBuilder();
    builder.append("LDAPConnection(");
    builder.append(connection.getLocalAddress());
    builder.append(',');
    builder.append(connection.getPeerAddress());
    builder.append(')');
    return builder.toString();
  }
  int addPendingRequest(final AbstractLDAPFutureResultImpl<?> request)
      throws ErrorResultException
  {
    final int newMsgID = nextMsgID.getAndIncrement();
    synchronized(stateLock)
    synchronized (stateLock)
    {
      if(connectionInvalidReason != null)
      if (connectionInvalidReason != null)
      {
        throw ErrorResultException.wrap(connectionInvalidReason);
      }
@@ -779,9 +800,9 @@
    {
      for (int requestID : pendingRequests.keySet())
      {
        final AbstractLDAPFutureResultImpl<?> future =
            pendingRequests.get(requestID);
        if(future != null)
        final AbstractLDAPFutureResultImpl<?> future = pendingRequests
            .get(requestID);
        if (future != null)
        {
          final long diff = (future.getTimestamp() + timeout) - currentTime;
          if (diff <= 0 && pendingRequests.remove(requestID) != null)
@@ -814,9 +835,15 @@
    synchronized (stateLock)
    {
      if (isClosed || connectionInvalidReason != null)
      if (isClosed) {
        // Already closed.
        return;
      }
      if (connectionInvalidReason != null)
      {
        // Already closed.
        isClosed = true;
        return;
      }
@@ -832,20 +859,15 @@
      }
      // Mark the connection as invalid.
      connectionInvalidReason =
          reason.getResultCode() == ResultCode.CLIENT_SIDE_USER_CANCELLED ?
              reason : Responses.newResult(
              ResultCode.CLIENT_SIDE_USER_CANCELLED).setCause(
              ErrorResultException.wrap(reason)).setDiagnosticMessage(
              "Connection closed: " + reason.getDiagnosticMessage());
      connectionInvalidReason = reason;
    }
    // First abort all outstanding requests.
    for (int requestID : pendingRequests.keySet())
    {
      final AbstractLDAPFutureResultImpl<?> future =
          pendingRequests.remove(requestID);
      if(future != null)
      final AbstractLDAPFutureResultImpl<?> future = pendingRequests
          .remove(requestID);
      if (future != null)
      {
        future.adaptErrorResult(reason);
      }
@@ -939,8 +961,8 @@
  {
    if (customFilterChain == null)
    {
      customFilterChain = new DefaultFilterChain((FilterChain) connection
          .getProcessor());
      customFilterChain = new DefaultFilterChain(
          (FilterChain) connection.getProcessor());
      connection.setProcessor(customFilterChain);
    }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
@@ -42,13 +42,13 @@
import org.opends.sdk.responses.Responses;
import org.opends.sdk.responses.Result;
import com.sun.grizzly.CompletionHandler;
import com.sun.grizzly.Connection;
import com.sun.grizzly.EmptyCompletionHandler;
import com.sun.grizzly.filterchain.DefaultFilterChain;
import com.sun.grizzly.filterchain.FilterChain;
import com.sun.grizzly.filterchain.TransportFilter;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.CompletionHandler;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.EmptyCompletionHandler;
import org.glassfish.grizzly.filterchain.DefaultFilterChain;
import org.glassfish.grizzly.filterchain.FilterChain;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import com.sun.opends.sdk.util.CompletedFutureResult;
import com.sun.opends.sdk.util.FutureResultTransformer;
import com.sun.opends.sdk.util.RecursiveFutureResult;
@@ -87,7 +87,10 @@
          // Ensure that the connection is closed.
          try
          {
            connection.close();
            if (connection != null)
            {
              connection.close();
            }
          }
          catch (final Exception e)
          {
@@ -148,10 +151,10 @@
                    @Override
                    public void failed(final Throwable throwable)
                    {
                      final Result errorResult = Responses.newResult(
                          ResultCode.CLIENT_SIDE_CONNECT_ERROR).setCause(
                          throwable).setDiagnosticMessage(
                          throwable.getMessage());
                      final Result errorResult = Responses
                          .newResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR)
                          .setCause(throwable)
                          .setDiagnosticMessage(throwable.getMessage());
                      handler.handleErrorResult(ErrorResultException
                          .wrap(errorResult));
                    }
@@ -160,9 +163,9 @@
            }
            catch (final IOException ioe)
            {
              final Result errorResult = Responses.newResult(
                  ResultCode.CLIENT_SIDE_CONNECT_ERROR).setCause(ioe)
                  .setDiagnosticMessage(ioe.getMessage());
              final Result errorResult = Responses
                  .newResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR)
                  .setCause(ioe).setDiagnosticMessage(ioe.getMessage());
              throw ErrorResultException.wrap(errorResult);
            }
          }
@@ -259,8 +262,8 @@
    this.socketAddress = address;
    this.options = new LDAPOptions(options);
    this.clientFilter = new LDAPClientFilter(new LDAPReader(this.options
        .getDecodeOptions()), 0);
    this.clientFilter = new LDAPClientFilter(new LDAPReader(
        this.options.getDecodeOptions()), 0);
    this.defaultFilterChain = new DefaultFilterChain();
    this.defaultFilterChain.add(new TransportFilter());
    this.defaultFilterChain.add(clientFilter);
@@ -292,6 +295,32 @@
  /**
   * Returns the address of the Directory Server.
   *
   * @return The address of the Directory Server.
   */
  public SocketAddress getSocketAddress()
  {
    return socketAddress;
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("LDAPConnectionFactory(");
    builder.append(getSocketAddress().toString());
    builder.append(')');
    return builder.toString();
  }
  private LDAPConnection adaptConnection(final Connection<?> connection)
  {
    // Test shows that its much faster with non block writes but risk
@@ -314,9 +343,9 @@
      t = t.getCause();
    }
    final Result result = Responses.newResult(
        ResultCode.CLIENT_SIDE_CONNECT_ERROR).setCause(t).setDiagnosticMessage(
        t.getMessage());
    final Result result = Responses
        .newResult(ResultCode.CLIENT_SIDE_CONNECT_ERROR).setCause(t)
        .setDiagnosticMessage(t.getMessage());
    return ErrorResultException.wrap(result);
  }
}
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java
@@ -43,19 +43,19 @@
 *          The type of result returned by this future.
 */
final class LDAPExtendedFutureResultImpl<R extends ExtendedResult> extends
    AbstractLDAPFutureResultImpl<R> implements FutureResult<R>
    AbstractLDAPFutureResultImpl<R>
{
  private final ExtendedRequest<R> request;
  LDAPExtendedFutureResultImpl(final int messageID,
  LDAPExtendedFutureResultImpl(final int requestID,
      final ExtendedRequest<R> request,
      final ResultHandler<? super R> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final AsynchronousConnection connection)
  {
    super(messageID, resultHandler, intermediateResponseHandler, connection);
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
  }
@@ -107,7 +107,7 @@
  R newErrorResult(final ResultCode resultCode, final String diagnosticMessage,
      final Throwable cause)
  {
    return request.getResultDecoder().adaptExtendedErrorResult(resultCode, "",
    return request.getResultDecoder().newExtendedErrorResult(resultCode, "",
        diagnosticMessage);
  }
}
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java
@@ -29,7 +29,10 @@
import org.opends.sdk.*;
import org.opends.sdk.AsynchronousConnection;
import org.opends.sdk.IntermediateResponseHandler;
import org.opends.sdk.ResultCode;
import org.opends.sdk.ResultHandler;
import org.opends.sdk.requests.Request;
import org.opends.sdk.responses.Responses;
import org.opends.sdk.responses.Result;
@@ -40,18 +43,17 @@
 * Result future implementation.
 */
final class LDAPFutureResultImpl extends AbstractLDAPFutureResultImpl<Result>
    implements FutureResult<Result>
{
  private final Request request;
  LDAPFutureResultImpl(final int messageID, final Request request,
  LDAPFutureResultImpl(final int requestID, final Request request,
      final ResultHandler<? super Result> resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final AsynchronousConnection connection)
  {
    super(messageID, resultHandler, intermediateResponseHandler, connection);
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
  }
@@ -85,7 +87,7 @@
  Result newErrorResult(final ResultCode resultCode,
      final String diagnosticMessage, final Throwable cause)
  {
    return Responses.newResult(resultCode).setDiagnosticMessage(
        diagnosticMessage).setCause(cause);
    return Responses.newResult(resultCode)
        .setDiagnosticMessage(diagnosticMessage).setCause(cause);
  }
}
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPListenerImpl.java
@@ -32,21 +32,23 @@
import java.io.Closeable;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.logging.Level;
import javax.net.ssl.SSLContext;
import org.glassfish.grizzly.filterchain.DefaultFilterChain;
import org.glassfish.grizzly.filterchain.FilterChain;
import org.glassfish.grizzly.filterchain.TransportFilter;
import org.glassfish.grizzly.nio.transport.TCPNIOServerConnection;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.ssl.SSLFilter;
import org.opends.sdk.DecodeOptions;
import org.opends.sdk.LDAPClientContext;
import org.opends.sdk.LDAPListenerOptions;
import org.opends.sdk.ServerConnectionFactory;
import com.sun.grizzly.filterchain.DefaultFilterChain;
import com.sun.grizzly.filterchain.FilterChain;
import com.sun.grizzly.filterchain.TransportFilter;
import com.sun.grizzly.nio.transport.TCPNIOServerConnection;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import com.sun.grizzly.ssl.SSLEngineConfigurator;
import com.sun.grizzly.ssl.SSLFilter;
import com.sun.opends.sdk.util.StaticUtils;
@@ -118,9 +120,53 @@
  /**
   * {@inheritDoc}
   */
  public void close() throws IOException
  @Override
  public void close()
  {
    transport.unbind(serverConnection);
    try
    {
      serverConnection.close().get();
    }
    catch (final InterruptedException e)
    {
      // Cannot handle here.
      Thread.currentThread().interrupt();
    }
    catch (final Exception e)
    {
      // Ignore the exception.
      if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
      {
        StaticUtils.DEBUG_LOG.log(Level.WARNING,
            "Exception occurred while closing listener:" + e.getMessage(), e);
      }
    }
  }
  /**
   * Returns the address that this LDAP listener is listening on.
   *
   * @return The address that this LDAP listener is listening on.
   */
  public SocketAddress getSocketAddress()
  {
    return serverConnection.getLocalAddress();
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("LDAPListener(");
    builder.append(getSocketAddress().toString());
    builder.append(')');
    return builder.toString();
  }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java
@@ -42,8 +42,7 @@
 * Search result future implementation.
 */
final class LDAPSearchFutureResultImpl extends
    AbstractLDAPFutureResultImpl<Result> implements FutureResult<Result>,
    SearchResultHandler
    AbstractLDAPFutureResultImpl<Result> implements SearchResultHandler
{
  private SearchResultHandler searchResultHandler;
@@ -52,12 +51,12 @@
  LDAPSearchFutureResultImpl(final int messageID, final SearchRequest request,
  LDAPSearchFutureResultImpl(final int requestID, final SearchRequest request,
      final SearchResultHandler resultHandler,
      final IntermediateResponseHandler intermediateResponseHandler,
      final AsynchronousConnection connection)
  {
    super(messageID, resultHandler, intermediateResponseHandler, connection);
    super(requestID, resultHandler, intermediateResponseHandler, connection);
    this.request = request;
    this.searchResultHandler = resultHandler;
  }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java
@@ -33,24 +33,25 @@
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.*;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.filterchain.*;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.grizzly.ssl.SSLFilter;
import org.glassfish.grizzly.ssl.SSLUtils;
import org.opends.sdk.*;
import org.opends.sdk.controls.Control;
import org.opends.sdk.requests.*;
import org.opends.sdk.responses.*;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.Connection;
import com.sun.grizzly.Grizzly;
import com.sun.grizzly.attributes.Attribute;
import com.sun.grizzly.filterchain.*;
import com.sun.grizzly.ssl.SSLEngineConfigurator;
import com.sun.grizzly.ssl.SSLFilter;
import com.sun.grizzly.ssl.SSLUtils;
import com.sun.opends.sdk.util.StaticUtils;
import com.sun.opends.sdk.util.Validator;
@@ -80,7 +81,7 @@
    @Override
    public boolean handleIntermediateResponse(
    public final boolean handleIntermediateResponse(
        final IntermediateResponse response)
    {
      final ASN1BufferWriter asn1Writer = ASN1BufferWriter.getWriter();
@@ -100,7 +101,6 @@
      }
      return true;
    }
  }
@@ -204,12 +204,7 @@
  {
    private final Connection<?> connection;
    // Connection state guarded by stateLock.
    private final Object stateLock = new Object();
    private List<ConnectionEventListener> connectionEventListeners = null;
    private boolean isClosed = false;
    private Throwable connectionError = null;
    private ExtendedResult disconnectNotification = null;
    private volatile boolean isClosed = false;
    private ServerConnection<Integer> serverConnection = null;
@@ -222,45 +217,10 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public void addConnectionEventListener(
        final ConnectionEventListener listener) throws NullPointerException
    {
      Validator.ensureNotNull(listener);
      boolean invokeImmediately = false;
      synchronized (stateLock)
      {
        if (isClosed)
        {
          invokeImmediately = true;
        }
        else
        {
          if (connectionEventListeners == null)
          {
            connectionEventListeners = new LinkedList<ConnectionEventListener>();
          }
          connectionEventListeners.add(listener);
        }
      }
      // Invoke listener immediately if this connection is already closed.
      if (invokeImmediately)
      {
        invokeListener(listener);
      }
    }
    @Override
    public void disconnect()
    {
      LDAPServerFilter.notifyConnectionClosed(connection, -1, null);
      LDAPServerFilter.notifyConnectionDisconnected(connection, null, null);
    }
@@ -274,7 +234,8 @@
          .newGenericExtendedResult(resultCode)
          .setOID(OID_NOTICE_OF_DISCONNECTION).setDiagnosticMessage(message);
      sendUnsolicitedNotification(notification);
      disconnect();
      LDAPServerFilter.notifyConnectionDisconnected(connection, resultCode,
          message);
    }
@@ -325,32 +286,7 @@
    @Override
    public boolean isClosed()
    {
      final boolean tmp;
      synchronized (stateLock)
      {
        tmp = isClosed;
      }
      return tmp;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void removeConnectionEventListener(
        final ConnectionEventListener listener) throws NullPointerException
    {
      Validator.ensureNotNull(listener);
      synchronized (stateLock)
      {
        if (connectionEventListeners != null)
        {
          connectionEventListeners.remove(listener);
        }
      }
      return isClosed;
    }
@@ -372,38 +308,6 @@
      {
        asn1Writer.recycle();
      }
      // Update state and notify event listeners if necessary, only if the
      // notification was sent successfully.
      if (notification.getOID().equals(OID_NOTICE_OF_DISCONNECTION))
      {
        // Don't notify listeners yet - wait for disconnect.
        synchronized (stateLock)
        {
          disconnectNotification = notification;
        }
      }
      else
      {
        // Notify listeners.
        List<ConnectionEventListener> tmpList = null;
        synchronized (stateLock)
        {
          if (!isClosed && connectionEventListeners != null)
          {
            tmpList = new ArrayList<ConnectionEventListener>(
                connectionEventListeners);
          }
        }
        if (tmpList != null)
        {
          for (final ConnectionEventListener listener : tmpList)
          {
            listener.handleUnsolicitedNotification(notification);
          }
        }
      }
    }
@@ -435,6 +339,29 @@
    /**
     * {@inheritDoc}
     */
    public String toString()
    {
      StringBuilder builder = new StringBuilder();
      builder.append("LDAPClientContext(");
      builder.append(getLocalAddress());
      builder.append(',');
      builder.append(getPeerAddress());
      builder.append(')');
      return builder.toString();
    }
    private void close()
    {
      isClosed = true;
    }
    private ServerConnection<Integer> getServerConnection()
    {
      return serverConnection;
@@ -442,87 +369,6 @@
    private void invokeListener(final ConnectionEventListener listener)
    {
      if (connectionError != null)
      {
        final Result result;
        if (connectionError instanceof DecodeException)
        {
          final DecodeException e = (DecodeException) connectionError;
          result = Responses.newResult(ResultCode.PROTOCOL_ERROR)
              .setDiagnosticMessage(e.getMessage()).setCause(connectionError);
        }
        else
        {
          result = Responses.newResult(ResultCode.OTHER)
              .setDiagnosticMessage(connectionError.getMessage())
              .setCause(connectionError);
        }
        listener
            .handleConnectionError(false, ErrorResultException.wrap(result));
      }
      else if (disconnectNotification != null)
      {
        listener.handleConnectionError(true,
            ErrorResultException.wrap(disconnectNotification));
      }
      else
      {
        listener.handleConnectionClosed();
      }
    }
    private void notifyConnectionClosed(final int messageID,
        final UnbindRequest unbindRequest)
    {
      final List<ConnectionEventListener> tmpList;
      synchronized (stateLock)
      {
        if (!isClosed)
        {
          isClosed = true;
        }
        tmpList = connectionEventListeners;
        connectionEventListeners = null;
      }
      if (tmpList != null)
      {
        for (final ConnectionEventListener listener : tmpList)
        {
          invokeListener(listener);
        }
      }
    }
    private void notifyConnectionException(final Throwable error)
    {
      final List<ConnectionEventListener> tmpList;
      synchronized (stateLock)
      {
        if (!isClosed)
        {
          connectionError = error;
          isClosed = true;
        }
        tmpList = connectionEventListeners;
        connectionEventListeners = null;
      }
      if (tmpList != null)
      {
        for (final ConnectionEventListener listener : tmpList)
        {
          invokeListener(listener);
        }
      }
    }
    private void setServerConnection(
        final ServerConnection<Integer> serverConnection)
    {
@@ -875,12 +721,10 @@
  private static final LDAPWriter LDAP_WRITER = new LDAPWriter();
  private static final Attribute<ClientContextImpl> LDAP_CONNECTION_ATTR =
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER
      .createAttribute("LDAPServerConnection");
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPServerConnection");
  private static final Attribute<ASN1BufferReader> LDAP_ASN1_READER_ATTR =
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER
      .createAttribute("LDAPASN1Reader");
    Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute("LDAPASN1Reader");
@@ -891,8 +735,8 @@
        .remove(connection);
    if (clientContext != null)
    {
      // First notify connection event listeners.
      clientContext.notifyConnectionClosed(messageID, unbindRequest);
      // Close the connection context.
      clientContext.close();
      // Notify the server connection: it may be null if disconnect is invoked
      // during accept.
@@ -925,15 +769,16 @@
  private static void notifyConnectionException(final Connection<?> connection,
      final Throwable error)
  private static void notifyConnectionDisconnected(
      final Connection<?> connection, final ResultCode resultCode,
      final String message)
  {
    final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR
        .remove(connection);
    if (clientContext != null)
    {
      // First notify connection event listeners.
      clientContext.notifyConnectionException(error);
      // Close the connection context.
      clientContext.close();
      // Notify the server connection: it may be null if disconnect is invoked
      // during accept.
@@ -941,7 +786,40 @@
          .getServerConnection();
      if (serverConnection != null)
      {
        serverConnection.handleConnectionException(error);
        serverConnection.handleConnectionDisconnected(resultCode, message);
      }
      // Close the connection.
      try
      {
        connection.close();
      }
      catch (final IOException e)
      {
        StaticUtils.DEBUG_LOG.warning("Error closing connection: " + e);
      }
    }
  }
  private static void notifyConnectionException(final Connection<?> connection,
      final Throwable error)
  {
    final ClientContextImpl clientContext = LDAP_CONNECTION_ATTR
        .remove(connection);
    if (clientContext != null)
    {
      // Close the connection context.
      clientContext.close();
      // Notify the server connection: it may be null if disconnect is invoked
      // during accept.
      final ServerConnection<Integer> serverConnection = clientContext
          .getServerConnection();
      if (serverConnection != null)
      {
        serverConnection.handleConnectionError(error);
      }
      // Close the connection.
@@ -1079,7 +957,7 @@
          ctx.getConnection()).getServerConnection();
      final SearchHandler handler = new SearchHandler(messageID,
          ctx.getConnection());
      conn.handleSearch(messageID, request, handler, handler, handler);
      conn.handleSearch(messageID, request, handler, handler);
    }
@@ -1102,6 +980,7 @@
          new UnsupportedMessageException(messageID, messageTag, messageBytes));
    }
  };
  private final int maxASN1ElementSize;
  private final LDAPReader ldapReader;
@@ -1139,7 +1018,7 @@
    {
      final ClientContextImpl clientContext = new ClientContextImpl(connection);
      final ServerConnection<Integer> serverConn = listener
          .getConnectionFactory().accept(clientContext);
          .getConnectionFactory().handleAccept(clientContext);
      clientContext.setServerConnection(serverConn);
      LDAP_CONNECTION_ATTR.set(connection, clientContext);
    }
@@ -1194,7 +1073,7 @@
  private synchronized void installFilter(final Connection<?> connection,
      final com.sun.grizzly.filterchain.Filter filter)
      final org.glassfish.grizzly.filterchain.Filter filter)
  {
    FilterChain filterChain = (FilterChain) connection.getProcessor();
    if (filter instanceof SSLFilter)
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/LDAPWriter.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package com.sun.opends.sdk.ldap;
@@ -204,7 +204,7 @@
          request));
    }
    encodeMessageHeader(writer, messageID);
    writer.writeInteger(OP_TYPE_ABANDON_REQUEST, request.getMessageID());
    writer.writeInteger(OP_TYPE_ABANDON_REQUEST, request.getRequestID());
    encodeMessageFooter(writer, request);
  }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SASLDecoderTransformer.java
@@ -32,10 +32,10 @@
import org.opends.sdk.ConnectionSecurityLayer;
import org.opends.sdk.ErrorResultException;
import com.sun.grizzly.*;
import com.sun.grizzly.attributes.AttributeStorage;
import com.sun.grizzly.memory.MemoryManager;
import com.sun.grizzly.memory.MemoryUtils;
import org.glassfish.grizzly.*;
import org.glassfish.grizzly.attributes.AttributeStorage;
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.MemoryManager;
@@ -96,7 +96,7 @@
    try
    {
      final Buffer output = MemoryUtils.wrap(memoryManager, bindContext.unwrap(
      final Buffer output = Buffers.wrap(memoryManager, bindContext.unwrap(
          buffer, 0, len));
      return TransformationResult.createCompletedResult(output, input);
    }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SASLEncoderTransformer.java
@@ -32,10 +32,10 @@
import org.opends.sdk.ConnectionSecurityLayer;
import org.opends.sdk.ErrorResultException;
import com.sun.grizzly.*;
import com.sun.grizzly.attributes.AttributeStorage;
import com.sun.grizzly.memory.MemoryManager;
import com.sun.grizzly.memory.MemoryUtils;
import org.glassfish.grizzly.*;
import org.glassfish.grizzly.attributes.AttributeStorage;
import org.glassfish.grizzly.memory.Buffers;
import org.glassfish.grizzly.memory.MemoryManager;
@@ -96,7 +96,7 @@
    try
    {
      final Buffer output = MemoryUtils.wrap(memoryManager, bindContext.wrap(
      final Buffer output = Buffers.wrap(memoryManager, bindContext.wrap(
          buffer, 0, len));
      return TransformationResult.createCompletedResult(output, input);
    }
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/SASLFilter.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package com.sun.opends.sdk.ldap;
@@ -31,8 +31,8 @@
import org.opends.sdk.ConnectionSecurityLayer;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.filterchain.AbstractCodecFilter;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.filterchain.AbstractCodecFilter;
opendj-sdk/sdk/src/com/sun/opends/sdk/ldap/TimeoutChecker.java
@@ -33,7 +33,7 @@
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import com.sun.grizzly.utils.LinkedTransferQueue;
import org.glassfish.grizzly.utils.LinkedTransferQueue;
import com.sun.opends.sdk.util.StaticUtils;
opendj-sdk/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
@@ -644,6 +644,16 @@
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    return connFactory.toString();
  }
  private String getKDC() throws ArgumentException, CLIException
  {
    String value = null;
opendj-sdk/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java
@@ -50,13 +50,13 @@
 * operations with the exception of Bind requests. Attempts to perform a Bind
 * will result in an {@code UnsupportedOperationException}.
 * <p>
 * In addition, the returned connections support retrieval of the {@code
 * BindResult} returned from the initial Bind request, or last rebind.
 * In addition, the returned connections support retrieval of the
 * {@code BindResult} returned from the initial Bind request, or last rebind.
 * <p>
 * Support for connection re-authentication is provided through the
 * {@link #setRebindAllowed} method which, if set to {@code true}, causes
 * subsequent connections created using the factory to support the {@code
 * rebind} method.
 * subsequent connections created using the factory to support the
 * {@code rebind} method.
 * <p>
 * If the Bind request fails for some reason (e.g. invalid credentials), then
 * the connection attempt will fail and an {@code ErrorResultException} will be
@@ -388,8 +388,8 @@
     * @throws UnsupportedOperationException
     *           If this connection does not support rebind operations.
     * @throws IllegalStateException
     *           If this connection has already been closed, i.e. if {@code
     *           isClosed() == true}.
     *           If this connection has already been closed, i.e. if
     *           {@code isClosed() == true}.
     */
    public FutureResult<BindResult> rebind(
        final ResultHandler<? super BindResult> handler)
@@ -481,6 +481,20 @@
      return connection.searchSingleEntry(request, resultHandler);
    }
    /**
     * {@inheritDoc}
     */
    public String toString()
    {
      StringBuilder builder = new StringBuilder();
      builder.append("AuthenticatedConnection(");
      builder.append(connection);
      builder.append(')');
      return builder.toString();
    }
  }
@@ -559,8 +573,8 @@
     * @throws UnsupportedOperationException
     *           If this connection does not support rebind operations.
     * @throws IllegalStateException
     *           If this connection has already been closed, i.e. if {@code
     *           isClosed() == true}.
     *           If this connection has already been closed, i.e. if
     *           {@code isClosed() == true}.
     */
    public BindResult rebind() throws ErrorResultException,
        InterruptedException, UnsupportedOperationException,
@@ -730,4 +744,18 @@
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("AuthenticatedConnectionFactory(");
    builder.append(String.valueOf(parentFactory));
    builder.append(')');
    return builder.toString();
  }
}
opendj-sdk/sdk/src/com/sun/opends/sdk/util/AsynchronousFutureResult.java
New file
@@ -0,0 +1,483 @@
/*
 * 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-2010 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 class AsynchronousFutureResult<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;
    /**
     * Allow all threads to acquire if future has completed.
     */
    @Override
    protected int tryAcquireShared(final int ignore)
    {
      return innerIsDone() ? 1 : -1;
    }
    /**
     * Signal that the future has completed and threads waiting on get() can be
     * released.
     */
    @Override
    protected boolean tryReleaseShared(final int finalState)
    {
      // Ensures that errorResult/result is published.
      setState(finalState);
      return true;
    }
    boolean innerCancel(final boolean mayInterruptIfRunning)
    {
      if (!isCancelable() || !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(final 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(final 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(final M result)
    {
      if (setStatePending())
      {
        this.result = result;
        try
        {
          // Invoke result completion handler.
          if (handler != null)
          {
            handler.handleResult(result);
          }
        }
        finally
        {
          releaseShared(SUCCESS); // Publishes result.
        }
      }
    }
    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;
        }
      }
    }
  }
  private final Sync sync = new Sync();
  private final ResultHandler<? super M> handler;
  private final int requestID;
  /**
   * Creates a new asynchronous future result with the provided result handler
   * and a request ID of -1.
   *
   * @param handler
   *          A result handler which will be forwarded the result or error when
   *          it arrives, may be {@code null}.
   */
  public AsynchronousFutureResult(final ResultHandler<? super M> handler)
  {
    this(handler, -1);
  }
  /**
   * Creates a new asynchronous future result with the provided result handler
   * and request ID.
   *
   * @param handler
   *          A result handler which will be forwarded the result or error when
   *          it arrives, may be {@code null}.
   * @param requestID
   *          The request ID which will be returned by the default
   *          implementation of {@link #getRequestID}.
   */
  public AsynchronousFutureResult(final ResultHandler<? super M> handler,
      final int requestID)
  {
    this.handler = handler;
    this.requestID = requestID;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public final boolean cancel(final boolean mayInterruptIfRunning)
  {
    return sync.innerCancel(mayInterruptIfRunning);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public final M get() throws ErrorResultException, InterruptedException
  {
    return sync.innerGet();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public final M get(final long timeout, final TimeUnit unit)
      throws ErrorResultException, TimeoutException, InterruptedException
  {
    return sync.innerGet(unit.toNanos(timeout));
  }
  /**
   * {@inheritDoc}
   * <p>
   * The default implementation returns the request ID passed in during
   * construction, or -1 if none was provided.
   */
  @Override
  public int getRequestID()
  {
    return requestID;
  }
  /**
   * 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.
   */
  @Override
  public final void handleErrorResult(final 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.
   */
  @Override
  public final void handleResult(final M result)
  {
    sync.innerSetResult(result);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public final boolean isCancelled()
  {
    return sync.innerIsCancelled();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  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(
      final boolean mayInterruptIfRunning)
  {
    // Do nothing by default.
    return null;
  }
  /**
   * Indicates whether this future result can be canceled.
   *
   * @return {@code true} if this future result is cancelable or {@code false}
   *         otherwise.
   */
  protected boolean isCancelable()
  {
    // Return true by default.
    return true;
  }
  /**
   * Appends a string representation of this future's state to the provided
   * builder.
   *
   * @param sb
   *          The string builder.
   */
  protected void toString(final StringBuilder sb)
  {
    sb.append(" state = ");
    sb.append(sync);
  }
}
opendj-sdk/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package com.sun.opends.sdk.util;
@@ -54,7 +54,7 @@
public abstract class RecursiveFutureResult<M, N> implements FutureResult<N>,
    ResultHandler<M>
{
  private final class FutureResultImpl extends AbstractFutureResult<N>
  private final class FutureResultImpl extends AsynchronousFutureResult<N>
  {
    private FutureResultImpl(final ResultHandler<? super N> handler)
    {
opendj-sdk/sdk/src/com/sun/opends/sdk/util/StaticUtils.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package com.sun.opends.sdk.util;
@@ -35,6 +35,10 @@
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.DataFormatException;
@@ -67,6 +71,10 @@
  private static final TimeZone TIME_ZONE_UTC_OBJ = TimeZone
      .getTimeZone(TIME_ZONE_UTC);
  private static ScheduledExecutorService defaultScheduler = null;
  private static final Object DEFAULT_SCHEDULER_LOCK = new Object();
  /**
@@ -1394,6 +1402,27 @@
  /**
   * Returns the default scheduler which should be used by the SDK.
   *
   * @return The default scheduler.
   */
  public static ScheduledExecutorService getDefaultScheduler()
  {
    synchronized (DEFAULT_SCHEDULER_LOCK)
    {
      if (defaultScheduler == null)
      {
        final ThreadFactory factory = newThreadFactory(null,
            "OpenDS SDK Default Scheduler", true);
        defaultScheduler = Executors.newSingleThreadScheduledExecutor(factory);
      }
    }
    return defaultScheduler;
  }
  /**
   * Retrieves the best human-readable message for the provided exception. For
   * exceptions defined in the OpenDS project, it will attempt to use the
   * message (combining it with the message ID if available). For some
@@ -1711,6 +1740,83 @@
  /**
   * Returns a string whose content is the string representation of the objects
   * contained in the provided collection concatenated together using the
   * provided separator.
   *
   * @param c
   *          The collection whose elements are to be joined.
   * @param separator
   *          The separator string.
   * @return A string whose content is the string representation of the objects
   *         contained in the provided collection concatenated together using
   *         the provided separator.
   * @throws NullPointerException
   *           If {@code c} or {@code separator} were {@code null}.
   */
  public static String joinCollection(Collection<?> c, String separator)
      throws NullPointerException
  {
    Validator.ensureNotNull(c, separator);
    switch (c.size())
    {
    case 0:
      return "";
    case 1:
      return String.valueOf(c.iterator().next());
    default:
      StringBuilder builder = new StringBuilder();
      Iterator<?> i = c.iterator();
      builder.append(i.next());
      while (i.hasNext())
      {
        builder.append(separator);
        builder.append(i.next());
      }
      String s = builder.toString();
      return s;
    }
  }
  /**
   * Creates a new thread factory which will create threads using the specified
   * thread group, naming template, and daemon status.
   *
   * @param group
   *          The thread group, which may be {@code null}.
   * @param nameTemplate
   *          The thread name format string which may contain a "%d" format
   *          option which will be substituted with the thread count.
   * @param isDaemon
   *          Indicates whether or not threads should be daemon threads.
   * @return The new thread factory.
   */
  public static ThreadFactory newThreadFactory(final ThreadGroup group,
      final String nameTemplate, final boolean isDaemon)
  {
    return new ThreadFactory()
    {
      private final AtomicInteger count = new AtomicInteger();
      public Thread newThread(Runnable r)
      {
        final String name = String
            .format(nameTemplate, count.getAndIncrement());
        final Thread t = new Thread(group, r, name);
        t.setDaemon(isDaemon);
        return t;
      }
    };
  }
  /**
   * Returns a string representation of the contents of the provided byte
   * sequence using hexadecimal characters and a space between each byte.
   *
opendj-sdk/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
@@ -44,8 +44,8 @@
/**
 * This class provides a skeletal implementation of the {@code
 * AsynchronousConnection} interface, to minimize the effort required to
 * This class provides a skeletal implementation of the
 * {@code AsynchronousConnection} interface, to minimize the effort required to
 * implement this interface.
 */
public abstract class AbstractAsynchronousConnection implements
@@ -394,9 +394,8 @@
   * {@inheritDoc}
   */
  public FutureResult<Result> search(final SearchRequest request,
      final SearchResultHandler handler)
      throws UnsupportedOperationException, IllegalStateException,
      NullPointerException
      final SearchResultHandler handler) throws UnsupportedOperationException,
      IllegalStateException, NullPointerException
  {
    return search(request, handler, null);
  }
@@ -417,4 +416,15 @@
    innerFuture.setResultFuture(future);
    return innerFuture;
  }
  /**
   * {@inheritDoc}
   * <p>
   * Sub-classes should provide an implementation which returns an appropriate
   * description of the connection which may be used for debugging purposes.
   */
  public abstract String toString();
}
opendj-sdk/sdk/src/org/opends/sdk/AbstractConnection.java
@@ -481,4 +481,14 @@
    return searchSingleEntry(request);
  }
  /**
   * {@inheritDoc}
   * <p>
   * Sub-classes should provide an implementation which returns an appropriate
   * description of the connection which may be used for debugging purposes.
   */
  public abstract String toString();
}
opendj-sdk/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
@@ -80,4 +80,11 @@
  {
    return getAsynchronousConnection(null).get().getSynchronousConnection();
  }
  /**
   * {@inheritDoc}
   */
  public abstract String toString();
}
opendj-sdk/sdk/src/org/opends/sdk/AbstractLoadBalancingAlgorithm.java
New file
@@ -0,0 +1,428 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import org.opends.sdk.responses.Responses;
import com.sun.opends.sdk.util.AsynchronousFutureResult;
import com.sun.opends.sdk.util.StaticUtils;
import com.sun.opends.sdk.util.Validator;
/**
 * An abstract load balancing algorithm providing monitoring and failover
 * capabilities.
 * <p>
 * Implementations should override the method
 * {@code getInitialConnectionFactoryIndex()} in order to provide the policy for
 * selecting the first connection factory to use for each connection request.
 */
abstract class AbstractLoadBalancingAlgorithm implements LoadBalancingAlgorithm
{
  private final class MonitoredConnectionFactory extends
      AbstractConnectionFactory implements
      ResultHandler<AsynchronousConnection>
  {
    private final ConnectionFactory factory;
    private final AtomicBoolean isOperational = new AtomicBoolean(true);
    private volatile FutureResult<?> pendingConnectFuture = null;
    private final int index;
    private MonitoredConnectionFactory(final ConnectionFactory factory,
        final int index)
    {
      this.factory = factory;
      this.index = index;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public FutureResult<AsynchronousConnection> getAsynchronousConnection(
        final ResultHandler<? super AsynchronousConnection> resultHandler)
    {
      final AsynchronousFutureResult<AsynchronousConnection> future =
        new AsynchronousFutureResult<AsynchronousConnection>(resultHandler);
      final ResultHandler<AsynchronousConnection> failoverHandler =
        new ResultHandler<AsynchronousConnection>()
      {
        @Override
        public void handleErrorResult(final ErrorResultException error)
        {
          // Attempt failed - try next factory.
          notifyOffline(error);
          final int nextIndex = (index + 1) % monitoredFactories.size();
          try
          {
            final MonitoredConnectionFactory nextFactory =
              getMonitoredConnectionFactory(nextIndex);
            nextFactory.getAsynchronousConnection(future);
          }
          catch (final ErrorResultException e)
          {
            future.handleErrorResult(e);
          }
        }
        @Override
        public void handleResult(final AsynchronousConnection result)
        {
          notifyOnline();
          future.handleResult(result);
        }
      };
      factory.getAsynchronousConnection(failoverHandler);
      return future;
    }
    /**
     * Handle monitoring connection request failure.
     */
    @Override
    public void handleErrorResult(final ErrorResultException error)
    {
      notifyOffline(error);
    }
    /**
     * Handle monitoring connection request success.
     */
    @Override
    public void handleResult(final AsynchronousConnection connection)
    {
      notifyOnline();
      // The connection is not going to be used, so close it immediately.
      connection.close();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
      return factory.toString();
    }
    /**
     * Attempt to connect to the factory if it is offline and there is no
     * pending monitoring request.
     */
    private synchronized void checkIfAvailable()
    {
      if (!isOperational.get()
          && (pendingConnectFuture == null || pendingConnectFuture.isDone()))
      {
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
        {
          StaticUtils.DEBUG_LOG.finest(String
              .format("Attempting connect on factory " + this));
        }
        pendingConnectFuture = factory.getAsynchronousConnection(this);
      }
    }
    private void notifyOffline(final ErrorResultException error)
    {
      if (isOperational.getAndSet(false))
      {
        // Transition from online to offline.
        synchronized (stateLock)
        {
          offlineFactoriesCount++;
          if (offlineFactoriesCount == 1)
          {
            // Enable monitoring.
            monitoringFuture = scheduler.scheduleWithFixedDelay(
                new MonitorThread(), 0, monitoringInterval,
                monitoringIntervalTimeUnit);
          }
        }
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE))
        {
          StaticUtils.DEBUG_LOG.fine(String.format("Connection factory "
              + factory + " is no longer operational: " + error.getMessage()));
        }
      }
    }
    private void notifyOnline()
    {
      if (!isOperational.getAndSet(true))
      {
        // Transition from offline to online.
        synchronized (stateLock)
        {
          offlineFactoriesCount--;
          if (offlineFactoriesCount == 0)
          {
            monitoringFuture.cancel(false);
            monitoringFuture = null;
          }
        }
        if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINE))
        {
          StaticUtils.DEBUG_LOG.fine(String.format("Connection factory "
              + factory + " is now operational"));
        }
      }
    }
  }
  private final class MonitorThread implements Runnable
  {
    private MonitorThread()
    {
      // Nothing to do.
    }
    @Override
    public void run()
    {
      for (final MonitoredConnectionFactory factory : monitoredFactories)
      {
        factory.checkIfAvailable();
      }
    }
  }
  private final List<MonitoredConnectionFactory> monitoredFactories;
  private final ScheduledExecutorService scheduler;
  private final Object stateLock = new Object();
  // Guarded by stateLock.
  private int offlineFactoriesCount = 0;
  private final long monitoringInterval;
  private final TimeUnit monitoringIntervalTimeUnit;
  // Guarded by stateLock.
  private ScheduledFuture<?> monitoringFuture;
  /**
   * Creates a new abstract load balancing algorithm.
   *
   * @param factories
   *          The connection factories.
   */
  AbstractLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories)
  {
    this(factories, StaticUtils.getDefaultScheduler());
  }
  /**
   * Creates a new abstract load balancing algorithm.
   *
   * @param factories
   *          The connection factories.
   * @param scheduler
   *          The scheduler which should for periodically monitoring dead
   *          connection factories to see if they are usable again.
   */
  AbstractLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories,
      final ScheduledExecutorService scheduler)
  {
    this(factories, StaticUtils.getDefaultScheduler(), 10, TimeUnit.SECONDS);
  }
  /**
   * Creates a new abstract load balancing algorithm.
   *
   * @param factories
   *          The connection factories.
   * @param scheduler
   *          The scheduler which should for periodically monitoring dead
   *          connection factories to see if they are usable again.
   * @param interval
   *          The interval between attempts to poll offline factories.
   * @param unit
   *          The time unit for the interval between attempts to poll offline
   *          factories.
   */
  AbstractLoadBalancingAlgorithm(final Collection<ConnectionFactory> factories,
      final ScheduledExecutorService scheduler, final long interval,
      final TimeUnit unit)
  {
    Validator.ensureNotNull(factories, scheduler, unit);
    this.monitoredFactories = new ArrayList<MonitoredConnectionFactory>(
        factories.size());
    int i = 0;
    for (final ConnectionFactory f : factories)
    {
      this.monitoredFactories.add(new MonitoredConnectionFactory(f, i++));
    }
    this.scheduler = scheduler;
    this.monitoringInterval = interval;
    this.monitoringIntervalTimeUnit = unit;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public final ConnectionFactory getConnectionFactory()
      throws ErrorResultException
  {
    final int index = getInitialConnectionFactoryIndex();
    return getMonitoredConnectionFactory(index);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append(getAlgorithmName());
    builder.append('(');
    boolean isFirst = true;
    for (final ConnectionFactory factory : monitoredFactories)
    {
      if (!isFirst)
      {
        builder.append(',');
      }
      else
      {
        isFirst = false;
      }
      builder.append(factory);
    }
    builder.append(')');
    return builder.toString();
  }
  /**
   * Returns the name of this load balancing algorithm.
   *
   * @return The name of this load balancing algorithm.
   */
  abstract String getAlgorithmName();
  /**
   * Returns the index of the first connection factory which should be used in
   * order to satisfy the next connection request.
   *
   * @return The index of the first connection factory which should be used in
   *         order to satisfy the next connection request.
   */
  abstract int getInitialConnectionFactoryIndex();
  // Return the first factory after index which is operational.
  private MonitoredConnectionFactory getMonitoredConnectionFactory(
      final int initialIndex) throws ErrorResultException
  {
    int index = initialIndex;
    final int maxIndex = monitoredFactories.size();
    do
    {
      final MonitoredConnectionFactory factory = monitoredFactories.get(index);
      if (factory.isOperational.get())
      {
        return factory;
      }
      index = (index + 1) % maxIndex;
    }
    while (index != initialIndex);
    // All factories are offline so give up. We could have a
    // configurable policy here such as waiting indefinitely, or for a
    // configurable timeout period.
    throw ErrorResultException.wrap(Responses.newResult(
        ResultCode.CLIENT_SIDE_CONNECT_ERROR).setDiagnosticMessage(
        "No operational connection factories available"));
  }
}
opendj-sdk/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
@@ -389,6 +389,20 @@
      return connection.searchSingleEntry(request, resultHandler);
    }
    /**
     * {@inheritDoc}
     */
    public String toString()
    {
      StringBuilder builder = new StringBuilder();
      builder.append("AuthenticatedConnection(");
      builder.append(connection);
      builder.append(')');
      return builder.toString();
    }
  }
@@ -502,4 +516,18 @@
    return future.futureBindResult;
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("AuthenticatedConnectionFactory(");
    builder.append(String.valueOf(parentFactory));
    builder.append(')');
    return builder.toString();
  }
}
opendj-sdk/sdk/src/org/opends/sdk/ConnectionPool.java
@@ -38,7 +38,7 @@
import org.opends.sdk.responses.*;
import org.opends.sdk.schema.Schema;
import com.sun.opends.sdk.util.AbstractFutureResult;
import com.sun.opends.sdk.util.AsynchronousFutureResult;
import com.sun.opends.sdk.util.CompletedFutureResult;
import com.sun.opends.sdk.util.StaticUtils;
@@ -51,24 +51,13 @@
{
  // Future used for waiting for pooled connections to become available.
  private static final class FuturePooledConnection extends
      AbstractFutureResult<AsynchronousConnection>
      AsynchronousFutureResult<AsynchronousConnection>
  {
    private FuturePooledConnection(
        final ResultHandler<? super AsynchronousConnection> handler)
    {
      super(handler);
    }
    /**
     * {@inheritDoc}
     */
    public int getRequestID()
    {
      return -1;
    }
  }
@@ -289,8 +278,7 @@
    public void handleUnsolicitedNotification(
        final ExtendedResult notification)
    public void handleUnsolicitedNotification(final ExtendedResult notification)
    {
      // Ignore
    }
@@ -567,6 +555,20 @@
      }
      return connection.searchSingleEntry(request, resultHandler);
    }
    /**
     * {@inheritDoc}
     */
    public String toString()
    {
      StringBuilder builder = new StringBuilder();
      builder.append("PooledConnection(");
      builder.append(connection);
      builder.append(')');
      return builder.toString();
    }
  }
@@ -738,7 +740,22 @@
      handler.handleResult(pooledConnection);
    }
    return new CompletedFutureResult<AsynchronousConnection>(pooledConnection);
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("ConnectionPool(");
    builder.append(String.valueOf(connectionFactory));
    builder.append(',');
    builder.append(poolSize);
    builder.append(')');
    return builder.toString();
  }
opendj-sdk/sdk/src/org/opends/sdk/Connections.java
@@ -22,14 +22,13 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import org.opends.sdk.requests.BindRequest;
@@ -104,69 +103,6 @@
  /**
   * Creates a new connection factory providing fault tolerance across multiple
   * underlying connection factories.
   * <p>
   * The returned fail-over connection factory forwards connection requests to
   * one of the provided connection factories. If the request fails for some
   * reason (for example, due to network failure), then the fail-over connection
   * factory switches to another connection faction, repeating the process until
   * the connection request succeeds, or until all the connection factories are
   * determined to be unavailable, in which case the connection request will
   * fail.
   * <p>
   * The implementation periodically attempts to connect to failed connection
   * factories in order to determine if they have become available again.
   *
   * @param factories
   *          The connection factories which will be used for fail-over.
   * @return The new fail-over connection factory.
   * @throws NullPointerException
   *           If {@code factories} was {@code null}.
   */
  public static ConnectionFactory newFailoverConnectionFactory(
      final Collection<ConnectionFactory> factories)
      throws NullPointerException
  {
    final FailoverLoadBalancingAlgorithm algorithm = new FailoverLoadBalancingAlgorithm(
        factories);
    return new LoadBalancingConnectionFactory(algorithm);
  }
  /**
   * Creates a new connection factory providing fault tolerance across multiple
   * underlying connection factories.
   * <p>
   * The returned fail-over connection factory forwards connection requests to
   * one of the provided connection factories. If the request fails for some
   * reason (for example, due to network failure), then the fail-over connection
   * factory switches to another connection faction, repeating the process until
   * the connection request succeeds, or until all the connection factories are
   * determined to be unavailable, in which case the connection request will
   * fail.
   * <p>
   * The implementation periodically attempts to connect to failed connection
   * factories in order to determine if they have become available again.
   *
   * @param factories
   *          The connection factories which will be used for fail-over.
   * @return The new fail-over connection factory.
   * @throws NullPointerException
   *           If {@code factories} was {@code null}.
   */
  public static ConnectionFactory newFailoverConnectionFactory(
      final ConnectionFactory... factories) throws NullPointerException
  {
    final FailoverLoadBalancingAlgorithm algorithm = new FailoverLoadBalancingAlgorithm(
        factories);
    return new LoadBalancingConnectionFactory(algorithm);
  }
  /**
   * Creates a new connection factory which will create connections using the
   * provided connection factory and periodically probe any created connections
   * in order to detect that they are still alive.
@@ -230,10 +166,21 @@
  /**
   * Creates a new connection factory which will create internal connections
   * using the provided server connection factory. The server connection will be
   * provided with request context whose value is unique integer associated with
   * the request.
   * Creates a new connection factory which binds internal client connections to
   * {@link ServerConnection}s created using the provided
   * {@link ServerConnectionFactory}.
   * <p>
   * When processing requests, {@code ServerConnection} implementations are
   * passed an integer as the first parameter. This integer represents a pseudo
   * {@code requestID} which is incremented for each successive internal request
   * on a per client connection basis. The request ID may be useful for logging
   * purposes.
   * <p>
   * An internal connection factory does not require {@code ServerConnection}
   * implementations to return a result when processing requests. However, it is
   * recommended that implementations do always return results even for
   * abandoned requests. This is because application client threads may block
   * indefinitely waiting for results.
   *
   * @param <C>
   *          The type of client context.
@@ -255,6 +202,81 @@
  /**
   * Creates a new load balancer which will obtain connections using the
   * provided load balancing algorithm.
   *
   * @param algorithm
   *          The load balancing algorithm which will be used to obtain the next
   * @return The new load balancer.
   * @throws NullPointerException
   *           If {@code algorithm} was {@code null}.
   */
  public static ConnectionFactory newLoadBalancer(
      final LoadBalancingAlgorithm algorithm) throws NullPointerException
  {
    return new LoadBalancer(algorithm);
  }
  /**
   * Creates a new connection factory which forwards connection requests to the
   * provided factory, but whose {@code toString} method will always return
   * {@code name}.
   * <p>
   * This method may be useful for debugging purposes in order to more easily
   * identity connection factories.
   *
   * @param factory
   *          The connection factory to be named.
   * @param name
   *          The name of the connection factory.
   * @return The named connection factory.
   * @throws NullPointerException
   *           If {@code factory} or {@code name} was {@code null}.
   */
  public static ConnectionFactory newNamedConnectionFactory(
      final ConnectionFactory factory, final String name)
      throws NullPointerException
  {
    Validator.ensureNotNull(factory, name);
    return new ConnectionFactory()
    {
      @Override
      public FutureResult<AsynchronousConnection> getAsynchronousConnection(
          final ResultHandler<? super AsynchronousConnection> handler)
      {
        return factory.getAsynchronousConnection(handler);
      }
      @Override
      public Connection getConnection() throws ErrorResultException,
          InterruptedException
      {
        return factory.getConnection();
      }
      /**
       * {@inheritDoc}
       */
      @Override
      public String toString()
      {
        return name;
      }
    };
  }
  // Prevent instantiation.
  private Connections()
  {
opendj-sdk/sdk/src/org/opends/sdk/Entries.java
@@ -30,14 +30,21 @@
import java.util.Collection;
import java.util.Iterator;
import org.opends.sdk.requests.ModifyRequest;
import org.opends.sdk.requests.Requests;
import com.sun.opends.sdk.util.Function;
import com.sun.opends.sdk.util.Iterables;
import com.sun.opends.sdk.util.Validator;
/**
 * This class contains methods for creating and manipulating entries.
 *
 * @see Entry
 */
public final class Entries
{
@@ -58,6 +65,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean addAttribute(final Attribute attribute)
        throws UnsupportedOperationException, NullPointerException
    {
@@ -69,6 +77,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean addAttribute(final Attribute attribute,
        final Collection<ByteString> duplicateValues)
        throws UnsupportedOperationException, NullPointerException
@@ -81,6 +90,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public Entry addAttribute(final String attributeDescription,
        final Object... values) throws LocalizedIllegalArgumentException,
        UnsupportedOperationException, NullPointerException
@@ -90,6 +100,7 @@
    @Override
    public Entry clearAttributes() throws UnsupportedOperationException
    {
      throw new UnsupportedOperationException();
@@ -97,6 +108,7 @@
    @Override
    public boolean containsAttribute(final Attribute attribute,
        final Collection<ByteString> missingValues) throws NullPointerException
    {
@@ -105,6 +117,7 @@
    @Override
    public boolean containsAttribute(final String attributeDescription,
        final Object... values) throws LocalizedIllegalArgumentException,
        NullPointerException
@@ -125,19 +138,21 @@
    @Override
    public Iterable<Attribute> getAllAttributes()
    {
      return Iterables.unmodifiable(Iterables.transform(entry
          .getAllAttributes(), UNMODIFIABLE_ATTRIBUTE_FUNCTION));
      return Iterables.unmodifiable(Iterables.transform(
          entry.getAllAttributes(), UNMODIFIABLE_ATTRIBUTE_FUNCTION));
    }
    @Override
    public Iterable<Attribute> getAllAttributes(
        final AttributeDescription attributeDescription)
    {
      return Iterables.unmodifiable(Iterables.transform(entry
          .getAllAttributes(attributeDescription),
      return Iterables.unmodifiable(Iterables.transform(
          entry.getAllAttributes(attributeDescription),
          UNMODIFIABLE_ATTRIBUTE_FUNCTION));
    }
@@ -146,17 +161,19 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public Iterable<Attribute> getAllAttributes(
        final String attributeDescription)
        throws LocalizedIllegalArgumentException, NullPointerException
    {
      return Iterables.unmodifiable(Iterables.transform(entry
          .getAllAttributes(attributeDescription),
      return Iterables.unmodifiable(Iterables.transform(
          entry.getAllAttributes(attributeDescription),
          UNMODIFIABLE_ATTRIBUTE_FUNCTION));
    }
    @Override
    public Attribute getAttribute(
        final AttributeDescription attributeDescription)
    {
@@ -176,6 +193,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public Attribute getAttribute(final String attributeDescription)
        throws LocalizedIllegalArgumentException, NullPointerException
    {
@@ -192,6 +210,7 @@
    @Override
    public int getAttributeCount()
    {
      return entry.getAttributeCount();
@@ -202,6 +221,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public DN getName()
    {
      return entry.getName();
@@ -223,6 +243,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean removeAttribute(final Attribute attribute,
        final Collection<ByteString> missingValues)
        throws UnsupportedOperationException, NullPointerException
@@ -232,6 +253,7 @@
    @Override
    public boolean removeAttribute(
        final AttributeDescription attributeDescription)
        throws UnsupportedOperationException, NullPointerException
@@ -244,6 +266,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public Entry removeAttribute(final String attributeDescription,
        final Object... values) throws LocalizedIllegalArgumentException,
        UnsupportedOperationException, NullPointerException
@@ -256,6 +279,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean replaceAttribute(final Attribute attribute)
        throws UnsupportedOperationException, NullPointerException
    {
@@ -267,6 +291,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public Entry replaceAttribute(final String attributeDescription,
        final Object... values) throws LocalizedIllegalArgumentException,
        UnsupportedOperationException, NullPointerException
@@ -276,6 +301,7 @@
    @Override
    public Entry setName(final DN dn) throws UnsupportedOperationException,
        NullPointerException
    {
@@ -287,6 +313,7 @@
    /**
     * {@inheritDoc}
     */
    @Override
    public Entry setName(final String dn)
        throws LocalizedIllegalArgumentException,
        UnsupportedOperationException, NullPointerException
@@ -309,10 +336,11 @@
  private static final Function<Attribute, Attribute, Void>
    UNMODIFIABLE_ATTRIBUTE_FUNCTION = new Function<Attribute, Attribute, Void>()
  private static final Function<Attribute, Attribute, Void> UNMODIFIABLE_ATTRIBUTE_FUNCTION =
    new Function<Attribute, Attribute, Void>()
  {
    @Override
    public Attribute apply(final Attribute value, final Void p)
    {
      return Attributes.unmodifiableAttribute(value);
@@ -323,8 +351,144 @@
  /**
   * Creates a new modify request containing a list of modifications which can
   * be used to transform {@code fromEntry} into entry {@code toEntry}.
   * <p>
   * The modify request is reversible: it will contain only modifications of
   * type {@link ModificationType#ADD ADD} and {@link ModificationType#DELETE
   * DELETE}.
   * <p>
   * Finally, the modify request will use the distinguished name taken from
   * {@code fromEntry}. Moreover, this method will not check to see if both
   * {@code fromEntry} and {@code toEntry} have the same distinguished name.
   * <p>
   * This method is equivalent to:
   *
   * <pre>
   * ModifyRequest request = Requests.newModifyRequest(fromEntry, toEntry);
   * </pre>
   *
   * @param fromEntry
   *          The source entry.
   * @param toEntry
   *          The destination entry.
   * @return A modify request containing a list of modifications which can be
   *         used to transform {@code fromEntry} into entry {@code toEntry}.
   * @throws NullPointerException
   *           If {@code fromEntry} or {@code toEntry} were {@code null}.
   * @see Requests#newModifyRequest(Entry, Entry)
   */
  public static final ModifyRequest diffEntries(final Entry fromEntry,
      final Entry toEntry) throws NullPointerException
  {
    Validator.ensureNotNull(fromEntry, toEntry);
    final ModifyRequest request = Requests
        .newModifyRequest(fromEntry.getName());
    TreeMapEntry tfrom;
    if (fromEntry instanceof TreeMapEntry)
    {
      tfrom = (TreeMapEntry) fromEntry;
    }
    else
    {
      tfrom = new TreeMapEntry(fromEntry);
    }
    TreeMapEntry tto;
    if (toEntry instanceof TreeMapEntry)
    {
      tto = (TreeMapEntry) toEntry;
    }
    else
    {
      tto = new TreeMapEntry(toEntry);
    }
    final Iterator<Attribute> ifrom = tfrom.getAllAttributes().iterator();
    final Iterator<Attribute> ito = tto.getAllAttributes().iterator();
    Attribute afrom = ifrom.hasNext() ? ifrom.next() : null;
    Attribute ato = ito.hasNext() ? ito.next() : null;
    while (afrom != null && ato != null)
    {
      final AttributeDescription adfrom = afrom.getAttributeDescription();
      final AttributeDescription adto = ato.getAttributeDescription();
      final int cmp = adfrom.compareTo(adto);
      if (cmp == 0)
      {
        // Attribute is in both entries. Compute the set of values to be added
        // and removed. We won't replace the attribute because this is not
        // reversible.
        final Attribute addedValues = new LinkedAttribute(ato);
        addedValues.removeAll(afrom);
        if (!addedValues.isEmpty())
        {
          request.addModification(new Modification(ModificationType.ADD,
              addedValues));
        }
        final Attribute deletedValues = new LinkedAttribute(afrom);
        deletedValues.removeAll(ato);
        if (!deletedValues.isEmpty())
        {
          request.addModification(new Modification(ModificationType.DELETE,
              deletedValues));
        }
        afrom = ifrom.hasNext() ? ifrom.next() : null;
        ato = ito.hasNext() ? ito.next() : null;
      }
      else if (cmp < 0)
      {
        // afrom in source, but not destination.
        request
            .addModification(new Modification(ModificationType.DELETE, afrom));
        afrom = ifrom.hasNext() ? ifrom.next() : null;
      }
      else
      {
        // ato in destination, but not in source.
        request.addModification(new Modification(ModificationType.ADD, ato));
        ato = ito.hasNext() ? ito.next() : null;
      }
    }
    // Additional attributes in source entry: these must be deleted.
    if (afrom != null)
    {
      request.addModification(new Modification(ModificationType.DELETE, afrom));
    }
    while (ifrom.hasNext())
    {
      final Attribute a = ifrom.next();
      request.addModification(new Modification(ModificationType.DELETE, a));
    }
    // Additional attributes in destination entry: these must be added.
    if (ato != null)
    {
      request.addModification(new Modification(ModificationType.ADD, ato));
    }
    while (ito.hasNext())
    {
      final Attribute a = ito.next();
      request.addModification(new Modification(ModificationType.ADD, a));
    }
    return request;
  }
  /**
   * Returns a read-only view of {@code entry} and its attributes. Query
   * operations on the returned entry and its attributes"read-through" to the
   * operations on the returned entry and its attributes "read-through" to the
   * underlying entry or attribute, and attempts to modify the returned entry
   * and its attributes either directly or indirectly via an iterator result in
   * an {@code UnsupportedOperationException}.
opendj-sdk/sdk/src/org/opends/sdk/Entry.java
@@ -57,6 +57,8 @@
 * {@link #replaceAttribute} and the conditions, if any, where a reference to
 * the passed in attribute is maintained.
 * </ul>
 *
 * @see Entries
 */
public interface Entry
{
opendj-sdk/sdk/src/org/opends/sdk/ErrorResultException.java
@@ -31,6 +31,7 @@
import java.util.concurrent.ExecutionException;
import org.opends.sdk.responses.Responses;
import org.opends.sdk.responses.Result;
@@ -42,6 +43,60 @@
@SuppressWarnings("serial")
public class ErrorResultException extends ExecutionException
{
  /**
   * Creates a new error result exception with the provided result code and
   * diagnostic message.
   *
   * @param resultCode
   *          The result code.
   * @param diagnosticMessage
   *          The diagnostic message, which may be empty or {@code null}
   *          indicating that none was provided.
   * @return The new error result exception.
   * @throws IllegalArgumentException
   *           If the provided result code does not represent a failure.
   * @throws NullPointerException
   *           If {@code resultCode} was {@code null}.
   */
  public static ErrorResultException newErrorResult(ResultCode resultCode,
      String diagnosticMessage) throws IllegalArgumentException,
      NullPointerException
  {
    return newErrorResult(resultCode, diagnosticMessage, null);
  }
  /**
   * Creates a new error result exception with the provided result code,
   * diagnostic message, and cause.
   *
   * @param resultCode
   *          The result code.
   * @param diagnosticMessage
   *          The diagnostic message, which may be empty or {@code null}
   *          indicating that none was provided.
   * @param cause
   *          The throwable cause, which may be null indicating that none was
   *          provided.
   * @return The new error result exception.
   * @throws IllegalArgumentException
   *           If the provided result code does not represent a failure.
   * @throws NullPointerException
   *           If {@code resultCode} was {@code null}.
   */
  public static ErrorResultException newErrorResult(ResultCode resultCode,
      String diagnosticMessage, Throwable cause)
      throws IllegalArgumentException, NullPointerException
  {
    Result result = Responses.newResult(resultCode)
        .setDiagnosticMessage(diagnosticMessage).setCause(cause);
    return wrap(result);
  }
  /**
   * Wraps the provided result in an appropriate error result exception. The
   * type of error result exception used depends on the underlying result code.
opendj-sdk/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
@@ -29,15 +29,9 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import org.opends.sdk.responses.Responses;
import com.sun.opends.sdk.util.StaticUtils;
import com.sun.opends.sdk.util.Validator;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -45,213 +39,115 @@
 * A fail-over load balancing algorithm provides fault tolerance across multiple
 * underlying connection factories.
 * <p>
 * This algorithm is typically used for load-balancing <i>between</i> data
 * centers, where there is preference to always always forward connection
 * requests to the <i>closest available</i> data center. This algorithm
 * contrasts with the {@link RoundRobinLoadBalancingAlgorithm} which is used for
 * load-balancing <i>within</i> a data center.
 * <p>
 * This algorithm selects connection factories based on the order in which they
 * were provided during construction. More specifically, an attempt to obtain a
 * connection factory will always return the <i>first operational</i> connection
 * factory in the list. Applications should, therefore, organize the connection
 * factories such that the <i>preferred</i> (usually the closest) connection
 * factories appear before those which are less preferred.
 * <p>
 * If a problem occurs that temporarily prevents connections from being obtained
 * for one of the connection factories, then this algorithm "fails over" to
 * another operational connection factory in the list. If none of the connection
 * factories are operational then a {@code ConnectionException} is returned to
 * the client.
 * for one of the connection factories, then this algorithm automatically
 * "fails over" to the next operational connection factory in the list. If none
 * of the connection factories are operational then a
 * {@code ConnectionException} is returned to the client.
 * <p>
 * The implementation periodically attempts to connect to failed connection
 * factories in order to determine if they have become available again.
 *
 * @see RoundRobinLoadBalancingAlgorithm
 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm)
 */
class FailoverLoadBalancingAlgorithm implements LoadBalancingAlgorithm
public final class FailoverLoadBalancingAlgorithm extends
    AbstractLoadBalancingAlgorithm
{
  private static final class MonitoredConnectionFactory extends
      AbstractConnectionFactory implements
      ResultHandler<AsynchronousConnection>
  {
    private final ConnectionFactory factory;
    private volatile boolean isOperational;
    private volatile FutureResult<?> pendingConnectFuture;
    private MonitoredConnectionFactory(final ConnectionFactory factory)
    {
      this.factory = factory;
      this.isOperational = true;
    }
    @Override
    public FutureResult<AsynchronousConnection> getAsynchronousConnection(
        final ResultHandler<? super AsynchronousConnection> resultHandler)
    {
      final ResultHandler<AsynchronousConnection> handler =
        new ResultHandler<AsynchronousConnection>()
      {
        public void handleErrorResult(final ErrorResultException error)
        {
          isOperational = false;
          if (resultHandler != null)
          {
            resultHandler.handleErrorResult(error);
          }
          if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
          {
            StaticUtils.DEBUG_LOG
                .warning(String.format("Connection factory " + factory
                    + " is no longer operational: " + error.getMessage()));
          }
        }
        public void handleResult(final AsynchronousConnection result)
        {
          isOperational = true;
          if (resultHandler != null)
          {
            resultHandler.handleResult(result);
          }
          if (StaticUtils.DEBUG_LOG.isLoggable(Level.WARNING))
          {
            StaticUtils.DEBUG_LOG.warning(String.format("Connection factory "
                + factory + " is now operational"));
          }
        }
      };
      return factory.getAsynchronousConnection(handler);
    }
    public void handleErrorResult(final ErrorResultException error)
    {
      isOperational = false;
    }
    public void handleResult(final AsynchronousConnection result)
    {
      isOperational = true;
      // TODO: Notify the server is back up
      result.close();
    }
    private boolean isOperational()
    {
      return isOperational;
    }
  }
  private final class MonitorThread extends Thread
  {
    private MonitorThread()
    {
      super("Connection Factory Health Monitor");
      this.setDaemon(true);
    }
    @Override
    public void run()
    {
      while (true)
      {
        for (final MonitoredConnectionFactory f : monitoredFactories)
        {
          if (!f.isOperational
              && (f.pendingConnectFuture == null || f.pendingConnectFuture
                  .isDone()))
          {
            if (StaticUtils.DEBUG_LOG.isLoggable(Level.FINEST))
            {
              StaticUtils.DEBUG_LOG.finest(String
                  .format("Attempting connect on factory " + f));
            }
            f.pendingConnectFuture = f.factory.getAsynchronousConnection(f);
          }
        }
        try
        {
          sleep(10000);
        }
        catch (final InterruptedException e)
        {
          // Termination requested - exit.
          break;
        }
      }
    }
  }
  private final List<MonitoredConnectionFactory> monitoredFactories;
  /**
   * Creates a new fail-over load balancing algorithm which will fail-over
   * across the provided collection of connection factories.
   * Creates a new fail-over load balancing algorithm which will use a default
   * scheduler for monitoring offline connection factories every 10 seconds.
   *
   * @param factories
   *          The connection factories which will be used for fail-over.
   *          The ordered collection of connection factories.
   */
  public FailoverLoadBalancingAlgorithm(
      final Collection<ConnectionFactory> factories)
  {
    Validator.ensureNotNull(factories);
    monitoredFactories = new ArrayList<MonitoredConnectionFactory>(factories
        .size());
    for (final ConnectionFactory f : factories)
    {
      monitoredFactories.add(new MonitoredConnectionFactory(f));
    }
    new MonitorThread().start();
    super(factories);
  }
  /**
   * Creates a new fail-over load balancing algorithm which will fail-over
   * across the provided list of connection factories.
   * Creates a new fail-over load balancing algorithm which will use the
   * provided scheduler for monitoring offline connection factories every 10
   * seconds.
   *
   * @param factories
   *          The connection factories which will be used for fail-over.
   *          The ordered collection of connection factories.
   * @param scheduler
   *          The scheduler which should for periodically monitoring offline
   *          connection factories to see if they are usable again.
   */
  public FailoverLoadBalancingAlgorithm(final ConnectionFactory... factories)
  public FailoverLoadBalancingAlgorithm(
      final Collection<ConnectionFactory> factories,
      final ScheduledExecutorService scheduler)
  {
    Validator.ensureNotNull((Object[]) factories);
    monitoredFactories = new ArrayList<MonitoredConnectionFactory>(
        factories.length);
    for (final ConnectionFactory f : factories)
    {
      monitoredFactories.add(new MonitoredConnectionFactory(f));
    }
    new MonitorThread().start();
    super(factories, scheduler);
  }
  public ConnectionFactory getNextConnectionFactory()
      throws ErrorResultException
  /**
   * Creates a new fail-over load balancing algorithm which will use the
   * provided scheduler for monitoring offline connection factories.
   *
   * @param factories
   *          The ordered collection of connection factories.
   * @param scheduler
   *          The scheduler which should for periodically monitoring offline
   *          connection factories to see if they are usable again.
   * @param interval
   *          The interval between attempts to poll offline connection
   *          factories.
   * @param unit
   *          The time unit for the interval between attempts to poll offline
   *          connection factories.
   */
  public FailoverLoadBalancingAlgorithm(
      final Collection<ConnectionFactory> factories,
      final ScheduledExecutorService scheduler, final long interval,
      final TimeUnit unit)
  {
    for (final MonitoredConnectionFactory f : monitoredFactories)
    {
      if (f.isOperational())
      {
        return f;
      }
    }
    throw ErrorResultException.wrap(Responses.newResult(
        ResultCode.CLIENT_SIDE_CONNECT_ERROR).setDiagnosticMessage(
        "No operational connection factories available"));
    super(factories, scheduler, interval, unit);
  }
  /**
   * {@inheritDoc}
   */
  @Override
  String getAlgorithmName()
  {
    return "Failover";
  }
  /**
   * {@inheritDoc}
   */
  @Override
  int getInitialConnectionFactoryIndex()
  {
    // Always start with the first connection factory.
    return 0;
  }
}
opendj-sdk/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
@@ -198,8 +198,7 @@
    public void handleUnsolicitedNotification(
        final ExtendedResult notification)
    public void handleUnsolicitedNotification(final ExtendedResult notification)
    {
      // Do nothing
    }
@@ -455,14 +454,27 @@
    {
      return connection.searchSingleEntry(request, resultHandler);
    }
    /**
     * {@inheritDoc}
     */
    public String toString()
    {
      StringBuilder builder = new StringBuilder();
      builder.append("HeartBeatConnection(");
      builder.append(connection);
      builder.append(')');
      return builder.toString();
    }
  }
  private final class FutureResultImpl extends
      FutureResultTransformer<AsynchronousConnection, AsynchronousConnection>
      implements FutureResult<AsynchronousConnection>,
      ResultHandler<AsynchronousConnection>
      implements ResultHandler<AsynchronousConnection>
  {
    private FutureResultImpl(
@@ -616,4 +628,18 @@
    future.setFutureResult(parentFactory.getAsynchronousConnection(future));
    return future;
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("HeartBeatConnectionFactory(");
    builder.append(String.valueOf(parentFactory));
    builder.append(')');
    return builder.toString();
  }
}
opendj-sdk/sdk/src/org/opends/sdk/InternalConnectionFactory.java
@@ -35,8 +35,20 @@
/**
 * A special {@code ConnectionFactory} that can be used to perform internal
 * operations against a {@code ServerConnectionFactory} implementation.
 * A special {@code ConnectionFactory} which waits for internal connection
 * requests and binds them to a {@link ServerConnection} created using the
 * provided {@link ServerConnectionFactory}.
 * <p>
 * When processing requests, {@code ServerConnection} implementations are passed
 * an integer as the first parameter. This integer represents a pseudo
 * {@code requestID} which is incremented for each successive internal request
 * on a per connection basis. The request ID may be useful for logging purposes.
 * <p>
 * An {@code InternalConnectionFactory} does not require
 * {@code ServerConnection} implementations to return a result when processing
 * requests. However, it is recommended that implementations do always return
 * results even for abandoned requests. This is because application client
 * threads may block indefinitely waiting for results.
 *
 * @param <C>
 *          The type of client context.
@@ -66,7 +78,7 @@
    final ServerConnection<Integer> serverConnection;
    try
    {
      serverConnection = factory.accept(clientContext);
      serverConnection = factory.handleAccept(clientContext);
    }
    catch (final ErrorResultException e)
    {
@@ -85,4 +97,20 @@
    }
    return new CompletedFutureResult<AsynchronousConnection>(connection);
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("InternalConnectionFactory(");
    builder.append(String.valueOf(clientContext));
    builder.append(',');
    builder.append(String.valueOf(factory));
    builder.append(')');
    return builder.toString();
  }
}
opendj-sdk/sdk/src/org/opends/sdk/LDAPClientContext.java
@@ -38,36 +38,20 @@
/**
 * An LDAP client which has connected to a {@link ServerConnectionFactory}. The
 * An LDAP client which has connected to a {@link ServerConnectionFactory}. An
 * LDAP client context can be used to query information about the client's
 * connection such as their network address, as well as managing the state of
 * the connection.
 */
public interface LDAPClientContext
{
  /**
   * Registers the provided connection event listener so that it will be
   * notified when the underlying connection is closed by the client, receives
   * an unsolicited notification, or experiences a fatal error.
   * <p>
   * This method provides a event notification mechanism which can be used by
   * asynchronous request handler implementations to detect connection
   * termination.
   *
   * @param listener
   *          The listener which wants to be notified when events occur on the
   *          underlying connection.
   * @throws NullPointerException
   *           If the {@code listener} was {@code null}.
   * @see #isClosed
   */
  void addConnectionEventListener(ConnectionEventListener listener)
      throws NullPointerException;
  /**
   * Disconnects the client without sending a disconnect notification.
   * <p>
   * <b>Server connections:</b> invoking this method causes
   * {@link ServerConnection#handleConnectionDisconnected
   * handleConnectionDisconnected} to be called before this method returns.
   */
  void disconnect();
@@ -76,6 +60,10 @@
  /**
   * Disconnects the client and sends a disconnect notification, if possible,
   * containing the provided result code and diagnostic message.
   * <p>
   * <b>Server connections:</b> invoking this method causes
   * {@link ServerConnection#handleConnectionDisconnected
   * handleConnectionDisconnected} to be called before this method returns.
   *
   * @param resultCode
   *          The result code which should be included with the disconnect
@@ -118,39 +106,27 @@
  /**
   * Returns {@code true} if the underlying connection is closed by the client,
   * receives an unsolicited notification, or experiences a fatal error.
   * Returns {@code true} if the underlying connection has been closed as a
   * result of a client disconnect, a fatal connection error, or a server-side
   * {@link #disconnect}.
   * <p>
   * This method provides a polling mechanism which can be used by synchronous
   * request handler implementations to detect connection termination.
   * <p>
   * <b>Server connections:</b> this method will always return {@code true} when
   * called from within {@link ServerConnection#handleConnectionClosed
   * handleConnectionClosed},
   * {@link ServerConnection#handleConnectionDisconnected
   * handleConnectionDisconnected}, or
   * {@link ServerConnection#handleConnectionError handleConnectionError}.
   *
   * @return {@code true} if the underlying connection is closed by the client,
   *         receives an unsolicited notification, or experiences a fatal error,
   *         otherwise {@code false}.
   * @see #addConnectionEventListener
   * @return {@code true} if the underlying connection has been closed.
   */
  boolean isClosed();
  /**
   * Removes the provided connection event listener from this client context so
   * that it will no longer be notified when the underlying connection is closed
   * by the application, receives an unsolicited notification, or experiences a
   * fatal error.
   *
   * @param listener
   *          The listener which no longer wants to be notified when events
   *          occur on the underlying connection.
   * @throws NullPointerException
   *           If the {@code listener} was {@code null}.
   */
  void removeConnectionEventListener(ConnectionEventListener listener)
      throws NullPointerException;
  /**
   * Sends an unsolicited notification to the client.
   *
   * @param notification
opendj-sdk/sdk/src/org/opends/sdk/LDAPConnectionFactory.java
@@ -29,6 +29,7 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -131,8 +132,31 @@
  /**
   * Returns the {@code InetAddress} that this LDAP listener is listening on.
   *
   * @return The {@code InetAddress} that this LDAP listener is listening on, or
   *         {@code null} if it is unknown.
   */
  public InetAddress getAddress()
  {
    final SocketAddress socketAddress = getSocketAddress();
    if (socketAddress instanceof InetSocketAddress)
    {
      final InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
      return inetSocketAddress.getAddress();
    }
    else
    {
      return null;
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<AsynchronousConnection> getAsynchronousConnection(
      final ResultHandler<? super AsynchronousConnection> handler)
  {
@@ -144,9 +168,77 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public Connection getConnection() throws ErrorResultException,
      InterruptedException
  {
    return impl.getConnection();
  }
  /**
   * Returns the host name that this LDAP listener is listening on.
   *
   * @return The host name that this LDAP listener is listening on, or
   *         {@code null} if it is unknown.
   */
  public String getHostname()
  {
    final SocketAddress socketAddress = getSocketAddress();
    if (socketAddress instanceof InetSocketAddress)
    {
      final InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
      return inetSocketAddress.getHostName();
    }
    else
    {
      return null;
    }
  }
  /**
   * Returns the port that this LDAP listener is listening on.
   *
   * @return The port that this LDAP listener is listening on, or {@code -1} if
   *         it is unknown.
   */
  public int getPort()
  {
    final SocketAddress socketAddress = getSocketAddress();
    if (socketAddress instanceof InetSocketAddress)
    {
      final InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
      return inetSocketAddress.getPort();
    }
    else
    {
      return -1;
    }
  }
  /**
   * Returns the address that this LDAP listener is listening on.
   *
   * @return The address that this LDAP listener is listening on.
   */
  public SocketAddress getSocketAddress()
  {
    return impl.getSocketAddress();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
    return impl.toString();
  }
}
opendj-sdk/sdk/src/org/opends/sdk/LDAPListener.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
@@ -31,6 +31,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -41,7 +42,21 @@
/**
 * An LDAP server connection listener which waits for LDAP connection requests
 * to come in over the network and binds them to a {@link ServerConnection}.
 * to come in over the network and binds them to a {@link ServerConnection}
 * created using the provided {@link ServerConnectionFactory}.
 * <p>
 * When processing requests, {@code ServerConnection} implementations are passed
 * an integer as the first parameter. This integer represents the
 * {@code requestID} associated with the client request and corresponds to the
 * {@code requestID} passed as a parameter to abandon and cancel extended
 * requests. The request ID may also be useful for logging purposes.
 * <p>
 * An {@code LDAPListener} does not require {@code ServerConnection}
 * implementations to return a result when processing requests. More
 * specifically, an {@code LDAPListener} does not maintain any internal state
 * information associated with each request which must be released. This is
 * useful when implementing LDAP abandon operations which may prevent results
 * being sent for abandoned operations.
 * <p>
 * The following code illustrates how to create a simple LDAP server:
 *
@@ -59,7 +74,7 @@
 *
 *
 *
 *   public void add(Integer context, AddRequest request,
 *   public void add(Integer requestID, AddRequest request,
 *       ResultHandler&lt;Result&gt; handler,
 *       IntermediateResponseHandler intermediateResponseHandler)
 *       throws UnsupportedOperationException
@@ -73,9 +88,10 @@
 *
 *
 *
 * class MyServer implements ServerConnectionFactory&lt;LDAPClientContext, Integer&gt;
 * class MyServer implements
 *     ServerConnectionFactory&lt;LDAPClientContext, RequestContext&gt;
 * {
 *   public ServerConnection&lt;Integer&gt; accept(LDAPClientContext context)
 *   public ServerConnection&lt;RequestContext&gt; accept(LDAPClientContext context)
 *   {
 *     System.out.println(&quot;Connection from: &quot; + context.getPeerAddress());
 *     return new MyClientConnection(context);
@@ -160,64 +176,6 @@
   * Creates a new LDAP listener implementation which will listen for LDAP
   * client connections at the provided address.
   *
   * @param port
   *          The port to listen on.
   * @param host
   *          The address to listen on.
   * @param factory
   *          The server connection factory which will be used to create server
   *          connections.
   * @throws IOException
   *           If an error occurred while trying to listen on the provided
   *           address.
   * @throws NullPointerException
   *           If {@code host} or {code factory} was {@code null}.
   */
  public LDAPListener(final int port, final String host,
      final ServerConnectionFactory<LDAPClientContext, Integer> factory)
      throws IOException, NullPointerException
  {
    this(port, host, factory, new LDAPListenerOptions());
  }
  /**
   * Creates a new LDAP listener implementation which will listen for LDAP
   * client connections at the provided address.
   *
   * @param port
   *          The port to listen on.
   * @param host
   *          The address to listen on.
   * @param factory
   *          The server connection factory which will be used to create server
   *          connections.
   * @param options
   *          The LDAP listener options.
   * @throws IOException
   *           If an error occurred while trying to listen on the provided
   *           address.
   * @throws NullPointerException
   *           If {@code host}, {code factory}, or {@code options} was {@code
   *           null}.
   */
  public LDAPListener(final int port, final String host,
      final ServerConnectionFactory<LDAPClientContext, Integer> factory,
      final LDAPListenerOptions options) throws IOException,
      NullPointerException
  {
    Validator.ensureNotNull(host, factory, options);
    final SocketAddress address = new InetSocketAddress(host, port);
    this.impl = new LDAPListenerImpl(address, factory, options);
  }
  /**
   * Creates a new LDAP listener implementation which will listen for LDAP
   * client connections at the provided address.
   *
   * @param address
   *          The address to listen on.
   * @param factory
@@ -268,14 +226,158 @@
  /**
   * Closes this LDAP connection listener.
   * Creates a new LDAP listener implementation which will listen for LDAP
   * client connections at the provided address.
   *
   * @param host
   *          The address to listen on.
   * @param port
   *          The port to listen on.
   * @param factory
   *          The server connection factory which will be used to create server
   *          connections.
   * @throws IOException
   *           If an IO error occurred while closing this listener.
   *           If an error occurred while trying to listen on the provided
   *           address.
   * @throws NullPointerException
   *           If {@code host} or {code factory} was {@code null}.
   */
  public void close() throws IOException
  public LDAPListener(final String host, final int port,
      final ServerConnectionFactory<LDAPClientContext, Integer> factory)
      throws IOException, NullPointerException
  {
    this(host, port, factory, new LDAPListenerOptions());
  }
  /**
   * Creates a new LDAP listener implementation which will listen for LDAP
   * client connections at the provided address.
   *
   * @param host
   *          The address to listen on.
   * @param port
   *          The port to listen on.
   * @param factory
   *          The server connection factory which will be used to create server
   *          connections.
   * @param options
   *          The LDAP listener options.
   * @throws IOException
   *           If an error occurred while trying to listen on the provided
   *           address.
   * @throws NullPointerException
   *           If {@code host}, {code factory}, or {@code options} was
   *           {@code null}.
   */
  public LDAPListener(final String host, final int port,
      final ServerConnectionFactory<LDAPClientContext, Integer> factory,
      final LDAPListenerOptions options) throws IOException,
      NullPointerException
  {
    Validator.ensureNotNull(host, factory, options);
    final SocketAddress address = new InetSocketAddress(host, port);
    this.impl = new LDAPListenerImpl(address, factory, options);
  }
  /**
   * Closes this LDAP connection listener.
   */
  @Override
  public void close()
  {
    impl.close();
  }
  /**
   * Returns the {@code InetAddress} that this LDAP listener is listening on.
   *
   * @return The {@code InetAddress} that this LDAP listener is listening on, or
   *         {@code null} if it is unknown.
   */
  public InetAddress getAddress()
  {
    final SocketAddress socketAddress = getSocketAddress();
    if (socketAddress instanceof InetSocketAddress)
    {
      final InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
      return inetSocketAddress.getAddress();
    }
    else
    {
      return null;
    }
  }
  /**
   * Returns the host name that this LDAP listener is listening on.
   *
   * @return The host name that this LDAP listener is listening on, or
   *         {@code null} if it is unknown.
   */
  public String getHostname()
  {
    final SocketAddress socketAddress = getSocketAddress();
    if (socketAddress instanceof InetSocketAddress)
    {
      final InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
      return inetSocketAddress.getHostName();
    }
    else
    {
      return null;
    }
  }
  /**
   * Returns the port that this LDAP listener is listening on.
   *
   * @return The port that this LDAP listener is listening on, or {@code -1} if
   *         it is unknown.
   */
  public int getPort()
  {
    final SocketAddress socketAddress = getSocketAddress();
    if (socketAddress instanceof InetSocketAddress)
    {
      final InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
      return inetSocketAddress.getPort();
    }
    else
    {
      return -1;
    }
  }
  /**
   * Returns the address that this LDAP listener is listening on.
   *
   * @return The address that this LDAP listener is listening on.
   */
  public SocketAddress getSocketAddress()
  {
    return impl.getSocketAddress();
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    return impl.toString();
  }
}
opendj-sdk/sdk/src/org/opends/sdk/LinkedAttribute.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
@@ -57,31 +57,6 @@
    boolean addAll(final LinkedAttribute attribute,
        final Collection<? extends ByteString> values,
        final Collection<? super ByteString> duplicateValues)
        throws NullPointerException
    {
      // TODO: could optimize if values is a BasicAttribute.
      ensureCapacity(attribute, values.size());
      boolean modified = false;
      for (final ByteString value : values)
      {
        if (add(attribute, value))
        {
          modified = true;
        }
        else if (duplicateValues != null)
        {
          duplicateValues.add(value);
        }
      }
      resize(attribute);
      return modified;
    }
    abstract void clear(LinkedAttribute attribute);
@@ -93,7 +68,8 @@
    boolean containsAll(final LinkedAttribute attribute,
        final Collection<?> values)
    {
      // TODO: could optimize if objects is a BasicAttribute.
      // TODO: could optimize if objects is a LinkedAttribute having the same
      // equality matching rule.
      for (final Object value : values)
      {
        if (!contains(attribute, ByteString.valueOf(value)))
@@ -106,10 +82,6 @@
    abstract void ensureCapacity(LinkedAttribute attribute, int size);
    abstract ByteString firstValue(LinkedAttribute attribute)
        throws NoSuchElementException;
@@ -123,31 +95,6 @@
    <T> boolean removeAll(final LinkedAttribute attribute,
        final Collection<T> values, final Collection<? super T> missingValues)
    {
      // TODO: could optimize if objects is a BasicAttribute.
      boolean modified = false;
      for (final T value : values)
      {
        if (remove(attribute, ByteString.valueOf(value)))
        {
          modified = true;
        }
        else if (missingValues != null)
        {
          missingValues.add(value);
        }
      }
      return modified;
    }
    abstract void resize(LinkedAttribute attribute);
    abstract <T> boolean retainAll(LinkedAttribute attribute,
        Collection<T> values, Collection<? super T> missingValues);
@@ -196,14 +143,6 @@
    @Override
    void ensureCapacity(final LinkedAttribute attribute, final int size)
    {
      // Nothing to do.
    }
    @Override
    ByteString firstValue(final LinkedAttribute attribute)
        throws NoSuchElementException
    {
@@ -224,6 +163,7 @@
        @Override
        public boolean hasNext()
        {
          return iterator.hasNext();
@@ -231,6 +171,7 @@
        @Override
        public ByteString next()
        {
          if (attribute.pimpl != expectedImpl)
@@ -245,6 +186,7 @@
        @Override
        public void remove()
        {
          if (attribute.pimpl != expectedImpl)
@@ -292,37 +234,11 @@
    @Override
    void resize(final LinkedAttribute attribute)
    {
      // May need to resize if initial size estimate was wrong (e.g. all
      // values in added collection were the same).
      switch (attribute.multipleValues.size())
      {
      case 0:
        attribute.multipleValues = null;
        attribute.pimpl = ZERO_VALUE_IMPL;
        break;
      case 1:
        final Map.Entry<ByteString, ByteString> e = attribute.multipleValues
            .entrySet().iterator().next();
        attribute.singleValue = e.getValue();
        attribute.normalizedSingleValue = e.getKey();
        attribute.multipleValues = null;
        attribute.pimpl = SINGLE_VALUE_IMPL;
        break;
      default:
        // Nothing to do.
        break;
      }
    }
    @Override
    <T> boolean retainAll(final LinkedAttribute attribute,
        final Collection<T> values, final Collection<? super T> missingValues)
    {
      // TODO: could optimize if objects is a BasicAttribute.
      // TODO: could optimize if objects is a LinkedAttribute having the same
      // equality matching rule.
      if (values.isEmpty())
      {
        clear(attribute);
@@ -367,6 +283,32 @@
    {
      return attribute.multipleValues.size();
    }
    private void resize(final LinkedAttribute attribute)
    {
      // May need to resize if initial size estimate was wrong (e.g. all
      // values in added collection were the same).
      switch (attribute.multipleValues.size())
      {
      case 0:
        attribute.multipleValues = null;
        attribute.pimpl = ZERO_VALUE_IMPL;
        break;
      case 1:
        final Map.Entry<ByteString, ByteString> e = attribute.multipleValues
            .entrySet().iterator().next();
        attribute.singleValue = e.getValue();
        attribute.normalizedSingleValue = e.getKey();
        attribute.multipleValues = null;
        attribute.pimpl = SINGLE_VALUE_IMPL;
        break;
      default:
        // Nothing to do.
        break;
      }
    }
  }
@@ -416,25 +358,6 @@
    @Override
    void ensureCapacity(final LinkedAttribute attribute, final int size)
    {
      if (size == 0)
      {
        return;
      }
      attribute.multipleValues = new LinkedHashMap<ByteString, ByteString>(
          1 + size);
      attribute.multipleValues.put(attribute.normalizedSingleValue,
          attribute.singleValue);
      attribute.singleValue = null;
      attribute.normalizedSingleValue = null;
      attribute.pimpl = MULTI_VALUE_IMPL;
    }
    @Override
    ByteString firstValue(final LinkedAttribute attribute)
        throws NoSuchElementException
    {
@@ -461,6 +384,7 @@
        @Override
        public boolean hasNext()
        {
          return hasNext;
@@ -468,6 +392,7 @@
        @Override
        public ByteString next()
        {
          if (attribute.pimpl != expectedImpl)
@@ -487,6 +412,7 @@
        @Override
        public void remove()
        {
          if (attribute.pimpl != expectedImpl)
@@ -526,18 +452,11 @@
    @Override
    void resize(final LinkedAttribute attribute)
    {
      // Nothing to do.
    }
    @Override
    <T> boolean retainAll(final LinkedAttribute attribute,
        final Collection<T> values, final Collection<? super T> missingValues)
    {
      // TODO: could optimize if objects is a BasicAttribute.
      // TODO: could optimize if objects is a LinkedAttribute having the same
      // equality matching rule.
      if (values.isEmpty())
      {
        clear(attribute);
@@ -549,8 +468,8 @@
      boolean retained = false;
      for (final T value : values)
      {
        final ByteString normalizedValue = normalizeValue(attribute, ByteString
            .valueOf(value));
        final ByteString normalizedValue = normalizeValue(attribute,
            ByteString.valueOf(value));
        if (normalizedSingleValue.equals(normalizedValue))
        {
          if (missingValues == null)
@@ -627,20 +546,6 @@
    @Override
    void ensureCapacity(final LinkedAttribute attribute, final int size)
    {
      if (size < 2)
      {
        return;
      }
      attribute.multipleValues = new LinkedHashMap<ByteString, ByteString>(size);
      attribute.pimpl = MULTI_VALUE_IMPL;
    }
    @Override
    ByteString firstValue(final LinkedAttribute attribute)
        throws NoSuchElementException
    {
@@ -654,6 +559,7 @@
    {
      return new Iterator<ByteString>()
      {
        @Override
        public boolean hasNext()
        {
          return false;
@@ -661,6 +567,7 @@
        @Override
        public ByteString next()
        {
          if (attribute.pimpl != ZERO_VALUE_IMPL)
@@ -675,6 +582,7 @@
        @Override
        public void remove()
        {
          if (attribute.pimpl != ZERO_VALUE_IMPL)
@@ -701,14 +609,6 @@
    @Override
    void resize(final LinkedAttribute attribute)
    {
      // Nothing to do.
    }
    @Override
    <T> boolean retainAll(final LinkedAttribute attribute,
        final Collection<T> values, final Collection<? super T> missingValues)
    {
@@ -736,6 +636,7 @@
   */
  public static final AttributeFactory FACTORY = new AttributeFactory()
  {
    @Override
    public Attribute newAttribute(
        final AttributeDescription attributeDescription)
        throws NullPointerException
@@ -843,8 +744,8 @@
   * @param values
   *          The attribute values.
   * @throws NullPointerException
   *           If {@code attributeDescription} or {@code values} was {@code
   *           null}.
   *           If {@code attributeDescription} or {@code values} was
   *           {@code null}.
   */
  public LinkedAttribute(final AttributeDescription attributeDescription,
      final ByteString... values) throws NullPointerException
@@ -864,8 +765,8 @@
   * @param values
   *          The attribute values.
   * @throws NullPointerException
   *           If {@code attributeDescription} or {@code values} was {@code
   *           null}.
   *           If {@code attributeDescription} or {@code values} was
   *           {@code null}.
   */
  public LinkedAttribute(final AttributeDescription attributeDescription,
      final Collection<ByteString> values) throws NullPointerException
@@ -941,8 +842,8 @@
   *           If {@code attributeDescription} could not be decoded using the
   *           default schema.
   * @throws NullPointerException
   *           If {@code attributeDescription} or {@code values} was {@code
   *           null}.
   *           If {@code attributeDescription} or {@code values} was
   *           {@code null}.
   */
  public LinkedAttribute(final String attributeDescription,
      final Object... values) throws LocalizedIllegalArgumentException,
@@ -978,7 +879,22 @@
      throws NullPointerException
  {
    Validator.ensureNotNull(values);
    return pimpl.addAll(this, values, duplicateValues);
    // TODO: could optimize if objects is a LinkedAttribute having the same
    // equality matching rule.
    boolean modified = false;
    for (final ByteString value : values)
    {
      if (add(value))
      {
        modified = true;
      }
      else if (duplicateValues != null)
      {
        duplicateValues.add(value);
      }
    }
    return modified;
  }
@@ -1072,7 +988,22 @@
      final Collection<? super T> missingValues) throws NullPointerException
  {
    Validator.ensureNotNull(values);
    return pimpl.removeAll(this, values, missingValues);
    // TODO: could optimize if objects is a LinkedAttribute having the same
    // equality matching rule.
    boolean modified = false;
    for (final T value : values)
    {
      if (remove(ByteString.valueOf(value)))
      {
        modified = true;
      }
      else if (missingValues != null)
      {
        missingValues.add(value);
      }
    }
    return modified;
  }
opendj-sdk/sdk/src/org/opends/sdk/LoadBalancer.java
New file
@@ -0,0 +1,100 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
import com.sun.opends.sdk.util.CompletedFutureResult;
import com.sun.opends.sdk.util.Validator;
/**
 * A load balancing connection factory allocates connections using the provided
 * algorithm.
 */
final class LoadBalancer extends AbstractConnectionFactory
{
  private final LoadBalancingAlgorithm algorithm;
  /**
   * Creates a new load balancer using the provided algorithm.
   *
   * @param algorithm
   *          The load balancing algorithm which will be used to obtain the next
   *          connection factory.
   */
  public LoadBalancer(final LoadBalancingAlgorithm algorithm)
  {
    Validator.ensureNotNull(algorithm);
    this.algorithm = algorithm;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public FutureResult<AsynchronousConnection> getAsynchronousConnection(
      final ResultHandler<? super AsynchronousConnection> resultHandler)
  {
    final ConnectionFactory factory;
    try
    {
      factory = algorithm.getConnectionFactory();
    }
    catch (final ErrorResultException e)
    {
      if (resultHandler != null)
      {
        resultHandler.handleErrorResult(e);
      }
      return new CompletedFutureResult<AsynchronousConnection>(e);
    }
    return factory.getAsynchronousConnection(resultHandler);
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("LoadBalancer(");
    builder.append(String.valueOf(algorithm));
    builder.append(')');
    return builder.toString();
  }
}
opendj-sdk/sdk/src/org/opends/sdk/LoadBalancingAlgorithm.java
@@ -32,16 +32,18 @@
/**
 * A load balancing algorithm distributes connection requests across one or more
 * underlying connection factories in an implementation defined manner.
 *
 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm) newLoadBalancer
 */
interface LoadBalancingAlgorithm
public interface LoadBalancingAlgorithm
{
  /**
   * Returns the next connection factory which should be used in order to obtain
   * a connection.
   * Returns a connection factory which should be used in order to satisfy the
   * next connection request.
   *
   * @return The next connection factory.
   * @return The connection factory.
   * @throws ErrorResultException
   *           If no connection factories are available for use.
   */
  ConnectionFactory getNextConnectionFactory() throws ErrorResultException;
  ConnectionFactory getConnectionFactory() throws ErrorResultException;
}
opendj-sdk/sdk/src/org/opends/sdk/RoundRobinLoadBalancingAlgorithm.java
New file
@@ -0,0 +1,178 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * A round robin load balancing algorithm distributes connection requests across
 * a list of connection factories one at a time. When the end of the list is
 * reached, the algorithm starts again from the beginning.
 * <p>
 * This algorithm is typically used for load-balancing <i>within</i> data
 * centers, where load must be distributed equally across multiple data sources.
 * This algorithm contrasts with the {@link FailoverLoadBalancingAlgorithm}
 * which is used for load-balancing <i>between</i> data centers.
 * <p>
 * If a problem occurs that temporarily prevents connections from being obtained
 * for one of the connection factories, then this algorithm automatically
 * "fails over" to the next operational connection factory in the list. If none
 * of the connection factories are operational then a
 * {@code ConnectionException} is returned to the client.
 * <p>
 * The implementation periodically attempts to connect to failed connection
 * factories in order to determine if they have become available again.
 *
 * @see FailoverLoadBalancingAlgorithm
 * @see Connections#newLoadBalancer(LoadBalancingAlgorithm)
 */
public final class RoundRobinLoadBalancingAlgorithm extends
    AbstractLoadBalancingAlgorithm
{
  private final int maxIndex;
  private final AtomicInteger nextIndex = new AtomicInteger(-1);
  /**
   * Creates a new round robin load balancing algorithm which will use a default
   * scheduler for monitoring offline connection factories every 10 seconds.
   *
   * @param factories
   *          The unordered collection of connection factories.
   */
  public RoundRobinLoadBalancingAlgorithm(
      final Collection<ConnectionFactory> factories)
  {
    super(factories);
    this.maxIndex = factories.size();
  }
  /**
   * Creates a new round robin load balancing algorithm which will use the
   * provided scheduler for monitoring offline connection factories every 10
   * seconds.
   *
   * @param factories
   *          The unordered collection of connection factories.
   * @param scheduler
   *          The scheduler which should for periodically monitoring offline
   *          connection factories to see if they are usable again.
   */
  public RoundRobinLoadBalancingAlgorithm(
      final Collection<ConnectionFactory> factories,
      final ScheduledExecutorService scheduler)
  {
    super(factories, scheduler);
    this.maxIndex = factories.size();
  }
  /**
   * Creates a new round robin load balancing algorithm which will use the
   * provided scheduler for monitoring offline connection factories.
   *
   * @param factories
   *          The unordered collection of connection factories.
   * @param scheduler
   *          The scheduler which should for periodically monitoring offline
   *          connection factories to see if they are usable again.
   * @param interval
   *          The interval between attempts to poll offline connection
   *          factories.
   * @param unit
   *          The time unit for the interval between attempts to poll offline
   *          connection factories.
   */
  public RoundRobinLoadBalancingAlgorithm(
      final Collection<ConnectionFactory> factories,
      final ScheduledExecutorService scheduler, final long interval,
      final TimeUnit unit)
  {
    super(factories, scheduler, interval, unit);
    this.maxIndex = factories.size();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  String getAlgorithmName()
  {
    return "RoundRobin";
  }
  /**
   * {@inheritDoc}
   */
  @Override
  int getInitialConnectionFactoryIndex()
  {
    // A round robin pool of one connection factories is unlikely in practice
    // and requires special treatment.
    if (maxIndex == 1)
    {
      return 0;
    }
    // Determine the next factory to use: avoid blocking algorithm.
    int oldNextIndex;
    int newNextIndex;
    do
    {
      oldNextIndex = nextIndex.get();
      newNextIndex = oldNextIndex + 1;
      if (newNextIndex == maxIndex)
      {
        newNextIndex = 0;
      }
    }
    while (!nextIndex.compareAndSet(oldNextIndex, newNextIndex));
    // There's a potential, but benign, race condition here: other threads could
    // jump in and rotate through the list before we return the connection
    // factory.
    return newNextIndex;
  }
}
opendj-sdk/sdk/src/org/opends/sdk/ServerConnection.java
@@ -39,9 +39,21 @@
/**
 * A handler interface for processing requests from clients.
 * <p>
 * Implementations should always return results via the provided
 * {@code RequestHandler} when processing requests unless otherwise indicated by
 * the component passing requests to the {@code ServerConnection}. For example,
 * an {@link LDAPListener} does not require {@code ServerConnection}
 * implementations to return results, which may be useful when implementing
 * abandon operation functionality.
 * <p>
 * Note that {@code ServerConnection}s may be stacked, and that a lower
 * {@code ServerConnection} implementation, such as a logger, may require upper
 * {@code ServerConnection} implementations to always return results.
 *
 * @param <C>
 *          The type of request context.
 * @see ServerConnectionFactory
 */
public interface ServerConnection<C>
{
@@ -137,7 +149,8 @@
   * request.
   *
   * @param requestContext
   *          The request context.
   *          The request context which should be ignored if there was no
   *          associated unbind request.
   * @param request
   *          The unbind request, which may be {@code null} if one was not sent
   *          before the connection was closed.
@@ -147,12 +160,28 @@
  /**
   * Invoked when the server disconnects the client connection, possibly using a
   * disconnect notification.
   *
   * @param resultCode
   *          The result code which was included with the disconnect
   *          notification, or {@code null} if no disconnect notification was
   *          sent.
   * @param message
   *          The diagnostic message, which may be empty or {@code null}
   *          indicating that none was provided.
   */
  void handleConnectionDisconnected(ResultCode resultCode, String message);
  /**
   * Invoked when an error occurs on the connection and it is no longer usable.
   *
   * @param error
   *          The exception describing the problem that occurred.
   */
  void handleConnectionException(Throwable error);
  void handleConnectionError(Throwable error);
@@ -258,11 +287,8 @@
   * @param request
   *          The search request.
   * @param resultHandler
   *          The handler which should be used to send back the result to the
   *          client.
   * @param searchResulthandler
   *          The handler which should be used to send back the search result
   *          entries and references to the client.
   *          The handler which should be used to send back the search results
   *          to the client.
   * @param intermediateResponseHandler
   *          The handler which should be used to send back any intermediate
   *          responses to the client.
@@ -270,8 +296,7 @@
   *           If this server connection does not handle search requests.
   */
  void handleSearch(C requestContext, SearchRequest request,
      ResultHandler<? super Result> resultHandler,
      SearchResultHandler searchResulthandler,
      SearchResultHandler resultHandler,
      IntermediateResponseHandler intermediateResponseHandler)
      throws UnsupportedOperationException;
}
opendj-sdk/sdk/src/org/opends/sdk/ServerConnectionFactory.java
@@ -31,17 +31,26 @@
/**
 * A handler interface for accepting new connections from clients.
 * <p>
 * A connection listener implementation, such as {@link LDAPListener} or
 * {@link Connections#newInternalConnectionFactory newInternalConnectionFactory}
 * , invoke the method {@link #handleAccept(Object) handleAccept} whenever a new
 * client connection is accepted.
 *
 * @param <C>
 *          The type of client context.
 * @param <R>
 *          The type of request context.
 * @see LDAPListener
 * @see Connections#newInternalConnectionFactory(ServerConnectionFactory,
 *      Object) newInternalConnectionFactory
 */
public interface ServerConnectionFactory<C, R>
{
  /**
   * Returns a {@code ServerConnection} which will be used to handle requests
   * from a client connection.
   * Invoked when a new client connection is accepted by the associated
   * listener. Implementations should return a {@code ServerConnection} which
   * will be used to handle requests from the client connection.
   *
   * @param clientContext
   *          The protocol dependent context information associated with the
@@ -49,11 +58,11 @@
   *          information about the client such as their address and level
   *          connection security. It may also be used to manage the state of
   *          the client's connection.
   * @return a {@code ServerConnection} which will be used to handle requests
   * @return A {@code ServerConnection} which will be used to handle requests
   *         from a client connection.
   * @throws ErrorResultException
   *           If this server connection factory cannot accept the client
   *           connection.
   */
  ServerConnection<R> accept(C clientContext) throws ErrorResultException;
  ServerConnection<R> handleAccept(C clientContext) throws ErrorResultException;
}
opendj-sdk/sdk/src/org/opends/sdk/SynchronousConnection.java
@@ -39,6 +39,7 @@
import java.util.concurrent.BlockingQueue;
/**
 * A {@code SynchronousConnection} adapts an {@code AsynchronousConnection} into
 * a synchronous {@code Connection}.
@@ -361,15 +362,27 @@
    }
  }
  /**
   * {@inheritDoc}
   */
  public ConnectionEntryReader search(final SearchRequest request,
                            BlockingQueue<Response> entries)
      throws UnsupportedOperationException, IllegalStateException,
      NullPointerException
      BlockingQueue<Response> entries) throws UnsupportedOperationException,
      IllegalStateException, NullPointerException
  {
    return new ConnectionEntryReader(getAsynchronousConnection(),
        request, entries);
    return new ConnectionEntryReader(getAsynchronousConnection(), request,
        entries);
  }
  /**
   * {@inheritDoc}
   */
  public String toString()
  {
    return connection.toString();
  }
}
opendj-sdk/sdk/src/org/opends/sdk/controls/package-info.java
@@ -22,11 +22,11 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
/**
 * Classes implementing common LDAP controls.
 * Classes and interfaces for common LDAP controls.
 */
package org.opends.sdk.controls;
opendj-sdk/sdk/src/org/opends/sdk/package-info.java
@@ -22,11 +22,12 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
/**
 * Core OpenDS SDK API including connections, entries, and attributes.
 * Classes and interfaces for core types including connections, entries, and
 * attributes.
 */
package org.opends.sdk;
opendj-sdk/sdk/src/org/opends/sdk/requests/AbandonRequest.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.requests;
@@ -70,23 +70,23 @@
  /**
   * Returns the message ID of the request to be abandoned.
   * Returns the request ID of the request to be abandoned.
   *
   * @return The message ID of the request to be abandoned.
   * @return The request ID of the request to be abandoned.
   */
  int getMessageID();
  int getRequestID();
  /**
   * Sets the message ID of the request to be abandoned.
   * Sets the request ID of the request to be abandoned.
   *
   * @param id
   *          The message ID of the request to be abandoned.
   *          The request ID of the request to be abandoned.
   * @return This abandon request.
   * @throws UnsupportedOperationException
   *           If this abandon request does not permit the message ID to be set.
   *           If this abandon request does not permit the request ID to be set.
   */
  AbandonRequest setMessageID(int id) throws UnsupportedOperationException;
  AbandonRequest setRequestID(int id) throws UnsupportedOperationException;
}
opendj-sdk/sdk/src/org/opends/sdk/requests/AbandonRequestImpl.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.requests;
@@ -36,26 +36,26 @@
    implements AbandonRequest
{
  private int messageID;
  private int requestID;
  /**
   * Creates a new abandon request using the provided message ID.
   *
   * @param messageID
   * @param requestID
   *          The message ID of the request to be abandoned.
   */
  AbandonRequestImpl(final int messageID)
  AbandonRequestImpl(final int requestID)
  {
    this.messageID = messageID;
    this.requestID = requestID;
  }
  public int getMessageID()
  public int getRequestID()
  {
    return messageID;
    return requestID;
  }
@@ -63,10 +63,10 @@
  /**
   * {@inheritDoc}
   */
  public AbandonRequest setMessageID(final int id)
  public AbandonRequest setRequestID(final int id)
      throws UnsupportedOperationException
  {
    this.messageID = id;
    this.requestID = id;
    return this;
  }
@@ -79,8 +79,8 @@
  public String toString()
  {
    final StringBuilder builder = new StringBuilder();
    builder.append("AbandonRequest(messageID=");
    builder.append(getMessageID());
    builder.append("AbandonRequest(requestID=");
    builder.append(getRequestID());
    builder.append(", controls=");
    builder.append(getControls());
    builder.append(")");
opendj-sdk/sdk/src/org/opends/sdk/requests/CancelExtendedRequest.java
@@ -93,18 +93,18 @@
  /**
   * Returns the message ID of the request to be abandoned.
   *
   * @return The message ID of the request to be abandoned.
   * {@inheritDoc}
   */
  int getMessageID();
  String getOID();
  /**
   * {@inheritDoc}
   * Returns the request ID of the request to be abandoned.
   *
   * @return The request ID of the request to be abandoned.
   */
  String getOID();
  int getRequestID();
@@ -130,14 +130,14 @@
  /**
   * Sets the message ID of the request to be abandoned.
   * Sets the request ID of the request to be abandoned.
   *
   * @param id
   *          The message ID of the request to be abandoned.
   *          The request ID of the request to be abandoned.
   * @return This abandon request.
   * @throws UnsupportedOperationException
   *           If this abandon request does not permit the message ID to be set.
   *           If this abandon request does not permit the request ID to be set.
   */
  CancelExtendedRequest setMessageID(int id)
  CancelExtendedRequest setRequestID(int id)
      throws UnsupportedOperationException;
}
opendj-sdk/sdk/src/org/opends/sdk/requests/CancelExtendedRequestImpl.java
@@ -40,6 +40,7 @@
import org.opends.sdk.asn1.ASN1Reader;
import org.opends.sdk.asn1.ASN1Writer;
import org.opends.sdk.controls.Control;
import org.opends.sdk.responses.AbstractExtendedResultDecoder;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
import org.opends.sdk.responses.Responses;
@@ -94,10 +95,10 @@
  private static final class ResultDecoder implements
      ExtendedResultDecoder<ExtendedResult>
  private static final class ResultDecoder extends
      AbstractExtendedResultDecoder<ExtendedResult>
  {
    public ExtendedResult adaptExtendedErrorResult(final ResultCode resultCode,
    public ExtendedResult newExtendedErrorResult(final ResultCode resultCode,
        final String matchedDN, final String diagnosticMessage)
    {
      return Responses.newGenericExtendedResult(resultCode).setMatchedDN(
@@ -116,7 +117,7 @@
  private int messageID;
  private int requestID;
  // No need to expose this.
  private static final ExtendedResultDecoder<ExtendedResult> RESULT_DECODER = new ResultDecoder();
@@ -124,9 +125,9 @@
  // Instantiation via factory.
  CancelExtendedRequestImpl(final int messageID)
  CancelExtendedRequestImpl(final int requestID)
  {
    this.messageID = messageID;
    this.requestID = requestID;
  }
@@ -134,9 +135,9 @@
  /**
   * {@inheritDoc}
   */
  public int getMessageID()
  public int getRequestID()
  {
    return messageID;
    return requestID;
  }
@@ -175,7 +176,7 @@
    try
    {
      writer.writeStartSequence();
      writer.writeInteger(messageID);
      writer.writeInteger(requestID);
      writer.writeEndSequence();
    }
    catch (final IOException ioe)
@@ -203,9 +204,9 @@
  /**
   * {@inheritDoc}
   */
  public CancelExtendedRequest setMessageID(final int id)
  public CancelExtendedRequest setRequestID(final int id)
  {
    this.messageID = id;
    this.requestID = id;
    return this;
  }
@@ -220,8 +221,8 @@
    final StringBuilder builder = new StringBuilder();
    builder.append("CancelExtendedRequest(requestName=");
    builder.append(getOID());
    builder.append(", messageID=");
    builder.append(messageID);
    builder.append(", requestID=");
    builder.append(requestID);
    builder.append(", controls=");
    builder.append(getControls());
    builder.append(")");
opendj-sdk/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequest.java
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import org.opends.sdk.*;
import org.opends.sdk.controls.Control;
@@ -64,68 +65,98 @@
   */
  public static final String SASL_MECHANISM_NAME = "DIGEST-MD5";
  /**
   * Indicates that the client will accept authentication only. More
   * specifically, the underlying connection will not be protected using
   * integrity protection or encryption, unless previously established using
   * SSL/TLS. This is the default if no QOP option is present in the bind
   * request.
   */
  public static final String QOP_AUTH = "auth";
  /**
   * Supported quality-of-protection options.
   * Indicates that the client will accept authentication with connection
   * integrity protection. More specifically, the underlying connection will not
   * be encrypted, unless previously established using SSL/TLS.
   */
  public static enum QOPOption
  {
    /**
     * Authentication only.
     */
    AUTH,
  public static final String QOP_AUTH_INT = "auth-int";
    /**
     * Authentication plus integrity protection.
     */
    AUTH_INT,
  /**
   * Indicates that the client will accept authentication with connection
   * integrity protection and encryption.
   */
  public static final String QOP_AUTH_CONF = "auth-conf";
    /**
     * Authentication plus integrity and confidentiality protection.
     */
    AUTH_CONF
  }
  /**
   * Indicates that the client will accept connection encryption using the high
   * strength triple-DES cipher.
   */
  public static final String CIPHER_3DES = "3des";
  /**
   * Indicates that the client will accept connection encryption using the high
   * strength 128-bit RC4 cipher.
   */
  public static final String CIPHER_RC4_128 = "rc4";
  /**
   * Indicates that the client will accept connection encryption using the
   * medium strength DES cipher.
   */
  public static final String CIPHER_DES = "des";
  /**
   * Indicates that the client will accept connection encryption using the
   * medium strength 56-bit RC4 cipher.
   */
  public static final String CIPHER_RC4_56 = "rc4-56";
  /**
   * Indicates that the client will accept connection encryption using the low
   * strength 40-bit RC4 cipher.
   */
  public static final String CIPHER_RC4_40 = "rc4-40";
  /**
   * Indicates that the client will accept connection encryption using the
   * strongest supported cipher, as long as the cipher is considered to be high
   * strength.
   */
  public static final String CIPHER_HIGH = "high";
  /**
   * Indicates that the client will accept connection encryption using the
   * strongest supported cipher, as long as the cipher is considered to be high
   * or medium strength.
   */
  public static final String CIPHER_MEDIUM = "medium";
  /**
   * Indicates that the client will accept connection encryption using the
   * strongest supported cipher, even if the strongest cipher is considered to
   * be medium or low strength.
   */
  public static final String CIPHER_LOW = "low";
  /**
   * Cipher options for use with the security layer.
   * Adds the provided additional authentication parameter to the list of
   * parameters to be passed to the underlying mechanism implementation. This
   * method is provided in order to allow for future extensions.
   *
   * @param name
   *          The name of the additional authentication parameter.
   * @param value
   *          The value of the additional authentication parameter.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit additional authentication
   *           parameters to be added.
   * @throws NullPointerException
   *           If {@code name} or {@code value} was {@code null}.
   */
  public static enum CipherOption
  {
    /**
     * Triple DES
     *   The "triple DES" cipher in CBC mode with EDE with the
     *   same key for each E stage (aka "two keys mode") for a
     *   total key length of 112 bits.
     * <p>
     * RC4 128 bits
     *   The RC4 cipher with a 128 bit key.
     */
    TRIPLE_DES_RC4,
    /**
     * DES
     *   The Data Encryption Standard (DES) cipher [FIPS] in
     *   cipher block chaining (CBC) mode with a 56 bit key.
     * <p>
     * RC4 56 bits
     *   The RC4 cipher with a 56 bit key.
     */
    DES_RC4_56,
    /**
     * RC4 40 bits
     *   The RC4 cipher with a 40 bit key.
     */
    RC4_40
  }
  /**
   * {@inheritDoc}
   */
  DigestMD5SASLBindRequest addControl(Control control)
  DigestMD5SASLBindRequest addAdditionalAuthParam(String name, String value)
      throws UnsupportedOperationException, NullPointerException;
@@ -133,11 +164,58 @@
  /**
   * {@inheritDoc}
   */
  @Override
  DigestMD5SASLBindRequest addControl(Control control)
      throws UnsupportedOperationException, NullPointerException;
  /**
   * Adds the provided quality of protection (QOP) values to the ordered list of
   * QOP values that the client is willing to accept. The order of the list
   * specifies the preference order, high to low. Authentication will fail if no
   * QOP values are recognized or accepted by the server.
   * <p>
   * By default the client will accept {@link #QOP_AUTH AUTH}.
   *
   * @param qopValues
   *          The quality of protection values that the client is willing to
   *          accept.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit QOP values to be added.
   * @throws NullPointerException
   *           If {@code qopValues} was {@code null}.
   * @see #QOP_AUTH
   * @see #QOP_AUTH_INT
   * @see #QOP_AUTH_CONF
   */
  DigestMD5SASLBindRequest addQOP(String... qopValues)
      throws UnsupportedOperationException, NullPointerException;
  /**
   * {@inheritDoc}
   */
  @Override
  BindClient createBindClient(String serverName) throws ErrorResultException;
  /**
   * Returns a map containing the provided additional authentication parameters
   * to be passed to the underlying mechanism implementation. This method is
   * provided in order to allow for future extensions.
   *
   * @return A map containing the provided additional authentication parameters
   *         to be passed to the underlying mechanism implementation.
   */
  Map<String, String> getAdditionalAuthParams();
  /**
   * Returns the authentication ID of the user. The authentication ID usually
   * has the form "dn:" immediately followed by the distinguished name of the
   * user, or "u:" followed by a user ID string, but other forms are permitted.
@@ -154,6 +232,7 @@
   *
   * @return The authentication mechanism identifier.
   */
  @Override
  byte getAuthenticationType();
@@ -172,8 +251,26 @@
  /**
   * Returns the cipher name or strength that the client is willing to use when
   * connection encryption quality of protection, {@link #QOP_AUTH_CONF
   * AUTH-CONF}, is requested.
   * <p>
   * By default the client will accept connection encryption using the strongest
   * supported cipher, even if the strongest cipher is considered to be medium
   * or low strength. This is equivalent to {@link #CIPHER_LOW}.
   *
   * @return The cipher that the client is willing to use if connection
   *         encryption QOP is negotiated. May be {@code null}, indicating that
   *         the default cipher should be used.
   */
  String getCipher();
  /**
   * {@inheritDoc}
   */
  @Override
  <C extends Control> C getControl(ControlDecoder<C> decoder,
      DecodeOptions options) throws NullPointerException, DecodeException;
@@ -182,16 +279,40 @@
  /**
   * {@inheritDoc}
   */
  @Override
  List<Control> getControls();
  /**
   * Returns the maximum size of the receive buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * send buffer size. The default size is 65536.
   *
   * @return The maximum size of the receive buffer in bytes.
   */
  int getMaxReceiveBufferSize();
  /**
   * Returns the maximum size of the send buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * receive buffer size. The default size is 65536.
   *
   * @return The maximum size of the send buffer in bytes.
   */
  int getMaxSendBufferSize();
  /**
   * Returns the name of the Directory object that the client wishes to bind as,
   * which is always the empty string for SASL authentication.
   *
   * @return The name of the Directory object that the client wishes to bind as.
   */
  @Override
  String getName();
@@ -206,6 +327,22 @@
  /**
   * Returns the ordered list of quality of protection (QOP) values that the
   * client is willing to accept. The order of the list specifies the preference
   * order, high to low. Authentication will fail if no QOP values are
   * recognized or accepted by the server.
   * <p>
   * By default the client will accept {@link #QOP_AUTH AUTH}.
   *
   * @return The list of quality of protection values that the client is willing
   *         to accept. The returned list may be empty indicating that the
   *         default QOP will be accepted.
   */
  List<String> getQOPs();
  /**
   * Returns the optional realm containing the user's account.
   *
   * @return The name of the realm containing the user's account, which may be
@@ -218,65 +355,18 @@
  /**
   * {@inheritDoc}
   */
  @Override
  String getSASLMechanism();
  /**
   * Returns the quality-of-protection options to use.
   * The order of the list specifies the preference order.
   * Returns {@code true} if the server must authenticate to the client. The
   * default is {@code false}.
   *
   * @return The list of quality-of-protection options to use.
   * @return {@code true} if the server must authenticate to the client.
   */
  QOPOption[] getQOP();
  /**
   * Returns the ciphers to use with the optional security layer
   * offered by the {@code AUTH_CONF} quality-of-protection. The order
   * of the list specifies the preference order. When there is
   * more than one choice for a particular option, the cipher
   * selected depends on the availability of the ciphers in the
   * underlying platform.
   *
   * @return The list of cipher options to use.
   */
  CipherOption[] getCipher();
  /**
   * Returns whether the server must authenticate to the client.
   *
   * @return {@code true} if the server must authenticate
   *         to the client or {@code false} otherwise.
   */
  boolean getServerAuth();
  /**
   * Returns the maximum size of the receive buffer in bytes.
   * The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum send
   * buffer size.
   *
   * @return The maximum size of the receive buffer in bytes.
   */
  int getMaxReceiveBufferSize();
  /**
   * Returns the maximum size of the send buffer in bytes.
   * The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum receive
   * buffer size.
   *
   * @return The maximum size of the send buffer in bytes.
   */
  int getMaxSendBufferSize();
  boolean isServerAuth();
@@ -291,11 +381,15 @@
   * @throws LocalizedIllegalArgumentException
   *           If {@code authenticationID} was non-empty and did not contain a
   *           valid authorization ID type.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit the authentication ID to be
   *           set.
   * @throws NullPointerException
   *           If {@code authenticationID} was {@code null}.
   */
  DigestMD5SASLBindRequest setAuthenticationID(String authenticationID)
      throws LocalizedIllegalArgumentException, NullPointerException;
      throws LocalizedIllegalArgumentException, UnsupportedOperationException,
      NullPointerException;
@@ -312,9 +406,76 @@
   * @throws LocalizedIllegalArgumentException
   *           If {@code authorizationID} was non-empty and did not contain a
   *           valid authorization ID type.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit the authorization ID to be
   *           set.
   */
  DigestMD5SASLBindRequest setAuthorizationID(String authorizationID)
      throws LocalizedIllegalArgumentException;
      throws LocalizedIllegalArgumentException, UnsupportedOperationException;
  /**
   * Sets the cipher name or strength that the client is willing to use when
   * connection encryption quality of protection, {@link #QOP_AUTH_CONF
   * AUTH-CONF}, is requested.
   * <p>
   * By default the client will accept connection encryption using the strongest
   * supported cipher, even if the strongest cipher is considered to be medium
   * or low strength. This is equivalent to {@link #CIPHER_LOW}.
   *
   * @param cipher
   *          The cipher that the client is willing to use if connection
   *          encryption QOP is negotiated. May be {@code null}, indicating that
   *          the default cipher should be used.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit the cipher name or strength
   *           to be set.
   * @see #QOP_AUTH_CONF
   * @see #CIPHER_3DES
   * @see #CIPHER_RC4_128
   * @see #CIPHER_DES
   * @see #CIPHER_RC4_56
   * @see #CIPHER_RC4_40
   * @see #CIPHER_HIGH
   * @see #CIPHER_MEDIUM
   * @see #CIPHER_LOW
   */
  DigestMD5SASLBindRequest setCipher(String cipher)
      throws UnsupportedOperationException;
  /**
   * Sets the maximum size of the receive buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * send buffer size. The default size is 65536.
   *
   * @param size
   *          The maximum size of the receive buffer in bytes.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit the buffer size to be set.
   */
  DigestMD5SASLBindRequest setMaxReceiveBufferSize(int size)
      throws UnsupportedOperationException;
  /**
   * Sets the maximum size of the send buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * receive buffer size. The default size is 65536.
   *
   * @param size
   *          The maximum size of the send buffer in bytes.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit the buffer size to be set.
   */
  DigestMD5SASLBindRequest setMaxSendBufferSize(int size)
      throws UnsupportedOperationException;
@@ -370,64 +531,16 @@
  /**
   * Specifies the quality-of-protection options to use.
   * The order of the list specifies the preference order.
   * Specifies whether or not the server must authenticate to the client. The
   * default is {@code false}.
   *
   * @param qopOptions The list of quality-of-protection options to
   *                   use.
   * @param serverAuth
   *          {@code true} if the server must authenticate to the client or
   *          {@code false} otherwise.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit server auth to be set.
   */
  DigestMD5SASLBindRequest setQOP(QOPOption... qopOptions);
  /**
   * Specifies the ciphers to use with the optional security layer
   * offered by the {@code AUTH_CONF} quality-of-protection. The order
   * of the list specifies the preference order. When there is
   * more than one choice for a particular option, the cipher
   * selected depends on the availability of the ciphers in the
   * underlying platform.
   *
   * @param cipherOptions The list of cipher options to use.
   * @return his bind request.
   */
  DigestMD5SASLBindRequest setCipher(CipherOption... cipherOptions);
  /**
   * Specifies whether the server must authenticate to the client.
   *
   * @param serverAuth {@code true} if the server must authenticate
   *                   to the client or {@code false} otherwise.
   * @return This bind request.
   */
  DigestMD5SASLBindRequest setServerAuth(boolean serverAuth);
  /**
   * Specifies the maximum size of the receive buffer in bytes.
   * The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum send
   * buffer size.
   *
   * @param maxBuffer The maximum size of the receive buffer in bytes.
   * @return This bind request.
   */
  DigestMD5SASLBindRequest setMaxReceiveBufferSize(int maxBuffer);
  /**
   * Specifies the maximum size of the send buffer in bytes.
   * The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum receive
   * buffer size.
   *
   * @param maxBuffer The maximum size of the send buffer in bytes.
   * @return This bind request.
   */
  DigestMD5SASLBindRequest setMaxSendBufferSize(int maxBuffer);
  DigestMD5SASLBindRequest setServerAuth(boolean serverAuth)
      throws UnsupportedOperationException;
}
opendj-sdk/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java
@@ -31,9 +31,9 @@
import static com.sun.opends.sdk.messages.Messages.ERR_SASL_PROTOCOL_ERROR;
import static com.sun.opends.sdk.util.StaticUtils.getExceptionMessage;
import static com.sun.opends.sdk.util.StaticUtils.joinCollection;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
@@ -76,15 +76,69 @@
      this.password = initialBindRequest.getPassword();
      this.realm = initialBindRequest.getRealm();
      // Create property map containing all the parameters.
      final Map<String, String> props = new HashMap<String, String>();
      props.put(Sasl.QOP, "auth-conf,auth-int,auth");
      final List<String> qopValues = initialBindRequest.getQOPs();
      if (!qopValues.isEmpty())
      {
        props.put(Sasl.QOP, joinCollection(qopValues, ","));
      }
      final String cipher = initialBindRequest.getCipher();
      if (cipher != null)
      {
        if (cipher.equalsIgnoreCase(CIPHER_LOW))
        {
          props.put(Sasl.STRENGTH, "high,medium,low");
        }
        else if (cipher.equalsIgnoreCase(CIPHER_MEDIUM))
        {
          props.put(Sasl.STRENGTH, "high,medium");
        }
        else if (cipher.equalsIgnoreCase(CIPHER_HIGH))
        {
          props.put(Sasl.STRENGTH, "high");
        }
        else
        {
          // Default strength allows all ciphers, so specifying a single cipher
          // cannot be incompatible with the strength.
          props.put("com.sun.security.sasl.digest.cipher", cipher);
        }
      }
      final Boolean serverAuth = initialBindRequest.isServerAuth();
      if (serverAuth != null)
      {
        props.put(Sasl.SERVER_AUTH, String.valueOf(serverAuth));
      }
      Integer size = initialBindRequest.getMaxReceiveBufferSize();
      if (size != null)
      {
        props.put(Sasl.MAX_BUFFER, String.valueOf(size));
      }
      size = initialBindRequest.getMaxSendBufferSize();
      if (size != null)
      {
        props.put("javax.security.sasl.sendmaxbuffer", String.valueOf(size));
      }
      for (final Map.Entry<String, String> e : initialBindRequest
          .getAdditionalAuthParams().entrySet())
      {
        props.put(e.getKey(), e.getValue());
      }
      // Now create the client.
      try
      {
        saslClient = Sasl.createSaslClient(
            new String[] { SASL_MECHANISM_NAME }, initialBindRequest
                .getAuthorizationID(), SASL_DEFAULT_PROTOCOL, serverName,
            props, this);
            new String[] { SASL_MECHANISM_NAME },
            initialBindRequest.getAuthorizationID(), SASL_DEFAULT_PROTOCOL,
            serverName, props, this);
        if (saslClient.hasInitialResponse())
        {
          setNextSASLCredentials(saslClient.evaluateChallenge(new byte[0]));
@@ -125,17 +179,18 @@
      try
      {
        setNextSASLCredentials(saslClient.evaluateChallenge(result
            .getServerSASLCredentials() == null ? new byte[0] :
            result.getServerSASLCredentials().toByteArray()));
            .getServerSASLCredentials() == null ? new byte[0] : result
            .getServerSASLCredentials().toByteArray()));
        return saslClient.isComplete();
      }
      catch (final SaslException e)
      {
        // FIXME: I18N need to have a better error message.
        // FIXME: Is this the best result code?
        throw ErrorResultException.wrap(Responses.newResult(
            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
            "An error occurred during multi-stage authentication")
        throw ErrorResultException.wrap(Responses
            .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
            .setDiagnosticMessage(
                "An error occurred during multi-stage authentication")
            .setCause(e));
      }
    }
@@ -170,9 +225,9 @@
      {
        final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
            SASL_MECHANISM_NAME, getExceptionMessage(e));
        throw ErrorResultException.wrap(Responses.newResult(
            ResultCode.CLIENT_SIDE_DECODING_ERROR).setDiagnosticMessage(
            msg.toString()).setCause(e));
        throw ErrorResultException.wrap(Responses
            .newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
            .setDiagnosticMessage(msg.toString()).setCause(e));
      }
    }
@@ -190,9 +245,9 @@
      {
        final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
            SASL_MECHANISM_NAME, getExceptionMessage(e));
        throw ErrorResultException.wrap(Responses.newResult(
            ResultCode.CLIENT_SIDE_ENCODING_ERROR).setDiagnosticMessage(
            msg.toString()).setCause(e));
        throw ErrorResultException.wrap(Responses
            .newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR)
            .setDiagnosticMessage(msg.toString()).setCause(e));
      }
    }
@@ -234,10 +289,19 @@
  private final Map<String, String> additionalAuthParams = new LinkedHashMap<String, String>();
  private final List<String> qopValues = new LinkedList<String>();
  private String cipher = null;
  // Don't use primitives for these so that we can distinguish between default
  // settings (null) and values set by the caller.
  private Boolean serverAuth = null;
  private Integer maxReceiveBufferSize = null;
  private Integer maxSendBufferSize = null;
  private String authenticationID;
  private String authorizationID = null;
  private ByteString password;
  private String realm = null;
@@ -255,6 +319,38 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest addAdditionalAuthParam(final String name,
      final String value) throws UnsupportedOperationException,
      NullPointerException
  {
    Validator.ensureNotNull(name, value);
    additionalAuthParams.put(name, value);
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest addQOP(final String... qopValues)
      throws UnsupportedOperationException, NullPointerException
  {
    for (final String qopValue : qopValues)
    {
      this.qopValues.add(Validator.ensureNotNull(qopValue));
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public BindClient createBindClient(final String serverName)
      throws ErrorResultException
  {
@@ -266,6 +362,18 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public Map<String, String> getAdditionalAuthParams()
  {
    return additionalAuthParams;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String getAuthenticationID()
  {
    return authenticationID;
@@ -276,6 +384,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public String getAuthorizationID()
  {
    return authorizationID;
@@ -286,6 +395,40 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public String getCipher()
  {
    return cipher;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public int getMaxReceiveBufferSize()
  {
    return maxReceiveBufferSize == null ? 65536 : maxReceiveBufferSize;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public int getMaxSendBufferSize()
  {
    return maxSendBufferSize == null ? 65536 : maxSendBufferSize;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ByteString getPassword()
  {
    return password;
@@ -296,6 +439,18 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public List<String> getQOPs()
  {
    return qopValues;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String getRealm()
  {
    return realm;
@@ -306,6 +461,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public String getSASLMechanism()
  {
    return SASL_MECHANISM_NAME;
@@ -316,6 +472,18 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isServerAuth()
  {
    return serverAuth == null ? false : serverAuth;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setAuthenticationID(
      final String authenticationID) throws NullPointerException
  {
@@ -329,6 +497,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setAuthorizationID(
      final String authorizationID)
  {
@@ -341,6 +510,46 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setCipher(final String cipher)
      throws UnsupportedOperationException
  {
    this.cipher = cipher;
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setMaxReceiveBufferSize(final int size)
      throws UnsupportedOperationException
  {
    maxReceiveBufferSize = size;
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setMaxSendBufferSize(final int size)
      throws UnsupportedOperationException
  {
    maxSendBufferSize = size;
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setPassword(final ByteString password)
      throws NullPointerException
  {
@@ -354,6 +563,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setPassword(final String password)
      throws NullPointerException
  {
@@ -367,6 +577,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public DigestMD5SASLBindRequest setRealm(final String realm)
  {
    this.realm = realm;
@@ -378,216 +589,11 @@
  /**
   * {@inheritDoc}
   */
  public QOPOption[] getQOP() {
    String value = System.getProperty(Sasl.QOP);
    if(value == null || value.length() == 0)
    {
      return new QOPOption[]{QOPOption.AUTH};
    }
    String[] values = value.split(",");
    QOPOption[] options = new QOPOption[values.length];
    for(int i = 0; i < values.length; i++)
    {
      String v = values[i].trim();
      if(v.equalsIgnoreCase("auth"))
      {
        options[i] = QOPOption.AUTH;
      }
      else if(v.equalsIgnoreCase("auth-int"))
      {
        options[i] = QOPOption.AUTH_INT;
      }
      else if(v.equalsIgnoreCase("auth-conf"))
      {
        options[i] = QOPOption.AUTH_CONF;
      }
    }
    return options;
  }
  /**
   * {@inheritDoc}
   */
  public CipherOption[] getCipher() {
    String value = System.getProperty(Sasl.STRENGTH);
    if(value == null || value.length() == 0)
    {
      return new CipherOption[]{CipherOption.TRIPLE_DES_RC4,
          CipherOption.DES_RC4_56, CipherOption.RC4_40};
    }
    String[] values = value.split(",");
    CipherOption[] options = new CipherOption[values.length];
    for(int i = 0; i < values.length; i++)
    {
      String v = values[i].trim();
      if(v.equalsIgnoreCase("high"))
      {
        options[i] = CipherOption.TRIPLE_DES_RC4;
      }
      else if(v.equalsIgnoreCase("medium"))
      {
        options[i] = CipherOption.DES_RC4_56;
      }
      else if(v.equalsIgnoreCase("low"))
      {
        options[i] = CipherOption.RC4_40;
      }
    }
    return options;
  }
  /**
   * {@inheritDoc}
   */
  public boolean getServerAuth() {
    String value = System.getProperty(Sasl.SERVER_AUTH);
    return !(value == null || value.length() == 0) &&
        value.equalsIgnoreCase("true");
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxReceiveBufferSize() {
    String value = System.getProperty(Sasl.MAX_BUFFER);
    if(value == null || value.length() == 0)
    {
      return 65536;
    }
    return Integer.parseInt(value);
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxSendBufferSize() {
    String value = System.getProperty("javax.security.sasl.sendmaxbuffer");
    if(value == null || value.length() == 0)
    {
      return 65536;
    }
    return Integer.parseInt(value);
  }
  /**
   * {@inheritDoc}
   */
  public DigestMD5SASLBindRequest setQOP(QOPOption... qopOptions) {
    String values = null;
    for(QOPOption option : qopOptions)
    {
      String value = null;
      if(option == QOPOption.AUTH)
      {
        value = "auth";
      }
      else if(option == QOPOption.AUTH_INT)
      {
        value = "auth-int";
      }
      else if(option == QOPOption.AUTH_CONF)
      {
        value = "auth-conf";
      }
      if(value != null)
      {
        if(values == null)
        {
          values = value;
        }
        else
        {
          values += (", " + value);
        }
      }
    }
    System.setProperty(Sasl.QOP, values);
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public DigestMD5SASLBindRequest setCipher(CipherOption... cipherOptions) {
    String values = null;
    for(CipherOption option : cipherOptions)
    {
      String value = null;
      if(option == CipherOption.TRIPLE_DES_RC4)
      {
        value = "high";
      }
      else if(option == CipherOption.DES_RC4_56)
      {
        value = "medium";
      }
      else if(option == CipherOption.RC4_40)
      {
        value = "low";
      }
      if(value != null)
      {
        if(values == null)
        {
          values = value;
        }
        else
        {
          values += (", " + value);
        }
      }
    }
    System.setProperty(Sasl.STRENGTH, values);
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public DigestMD5SASLBindRequest setServerAuth(boolean serverAuth) {
    System.setProperty(Sasl.SERVER_AUTH, String.valueOf(serverAuth));
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public DigestMD5SASLBindRequest setMaxReceiveBufferSize(int maxBuffer) {
    System.setProperty(Sasl.MAX_BUFFER, String.valueOf(maxBuffer));
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public DigestMD5SASLBindRequest setMaxSendBufferSize(int maxBuffer) {
    System.setProperty("javax.security.sasl.sendmaxbuffer",
        String.valueOf(maxBuffer));
  @Override
  public DigestMD5SASLBindRequest setServerAuth(final boolean serverAuth)
      throws UnsupportedOperationException
  {
    this.serverAuth = serverAuth;
    return this;
  }
opendj-sdk/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequest.java
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import javax.security.auth.Subject;
@@ -63,42 +64,101 @@
   */
  public static final String SASL_MECHANISM_NAME = "GSSAPI";
  /**
   * Indicates that the client will accept authentication only. More
   * specifically, the underlying connection will not be protected using
   * integrity protection or encryption, unless previously established using
   * SSL/TLS. This is the default if no QOP option is present in the bind
   * request.
   */
  public static final String QOP_AUTH = "auth";
  /**
   * Indicates that the client will accept authentication with connection
   * integrity protection. More specifically, the underlying connection will not
   * be encrypted, unless previously established using SSL/TLS.
   */
  public static final String QOP_AUTH_INT = "auth-int";
  /**
   * Indicates that the client will accept authentication with connection
   * integrity protection and encryption.
   */
  public static final String QOP_AUTH_CONF = "auth-conf";
  /**
   * Supported quality-of-protection options.
   * Adds the provided additional authentication parameter to the list of
   * parameters to be passed to the underlying mechanism implementation. This
   * method is provided in order to allow for future extensions.
   *
   * @param name
   *          The name of the additional authentication parameter.
   * @param value
   *          The value of the additional authentication parameter.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit additional authentication
   *           parameters to be added.
   * @throws NullPointerException
   *           If {@code name} or {@code value} was {@code null}.
   */
  public static enum QOPOption
  {
    /**
     * Authentication only.
     */
    AUTH,
  GSSAPISASLBindRequest addAdditionalAuthParam(String name, String value)
      throws UnsupportedOperationException, NullPointerException;
    /**
     * Authentication plus integrity protection.
     */
    AUTH_INT,
    /**
     * Authentication plus integrity and confidentiality protection.
     */
    AUTH_CONF
  }
  /**
   * Returns a map containing the provided additional authentication parameters
   * to be passed to the underlying mechanism implementation. This method is
   * provided in order to allow for future extensions.
   *
   * @return A map containing the provided additional authentication parameters
   *         to be passed to the underlying mechanism implementation.
   */
  Map<String, String> getAdditionalAuthParams();
  /**
   * {@inheritDoc}
   */
  @Override
  GSSAPISASLBindRequest addControl(Control control)
      throws UnsupportedOperationException, NullPointerException;
  /**
   * Adds the provided quality of protection (QOP) values to the ordered list of
   * QOP values that the client is willing to accept. The order of the list
   * specifies the preference order, high to low. Authentication will fail if no
   * QOP values are recognized or accepted by the server.
   * <p>
   * By default the client will accept {@link #QOP_AUTH AUTH}.
   *
   * @param qopValues
   *          The quality of protection values that the client is willing to
   *          accept.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit QOP values to be added.
   * @throws NullPointerException
   *           If {@code qopValues} was {@code null}.
   * @see #QOP_AUTH
   * @see #QOP_AUTH_INT
   * @see #QOP_AUTH_CONF
   */
  GSSAPISASLBindRequest addQOP(String... qopValues)
      throws UnsupportedOperationException, NullPointerException;
  /**
   * {@inheritDoc}
   */
  @Override
  BindClient createBindClient(String serverName) throws ErrorResultException;
@@ -123,6 +183,7 @@
   *
   * @return The authentication mechanism identifier.
   */
  @Override
  byte getAuthenticationType();
@@ -143,6 +204,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  <C extends Control> C getControl(ControlDecoder<C> decoder,
      DecodeOptions options) throws NullPointerException, DecodeException;
@@ -151,6 +213,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  List<Control> getControls();
@@ -168,11 +231,34 @@
  /**
   * Returns the maximum size of the receive buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * send buffer size. The default size is 65536.
   *
   * @return The maximum size of the receive buffer in bytes.
   */
  int getMaxReceiveBufferSize();
  /**
   * Returns the maximum size of the send buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * receive buffer size. The default size is 65536.
   *
   * @return The maximum size of the send buffer in bytes.
   */
  int getMaxSendBufferSize();
  /**
   * Returns the name of the Directory object that the client wishes to bind as,
   * which is always the empty string for SASL authentication.
   *
   * @return The name of the Directory object that the client wishes to bind as.
   */
  @Override
  String getName();
@@ -189,6 +275,22 @@
  /**
   * Returns the ordered list of quality of protection (QOP) values that the
   * client is willing to accept. The order of the list specifies the preference
   * order, high to low. Authentication will fail if no QOP values are
   * recognized or accepted by the server.
   * <p>
   * By default the client will accept {@link #QOP_AUTH AUTH}.
   *
   * @return The list of quality of protection values that the client is willing
   *         to accept. The returned list may be empty indicating that the
   *         default QOP will be accepted.
   */
  List<String> getQOPs();
  /**
   * Returns the optional realm containing the user's account.
   * <p>
   * <b>NOTE</b>: this will not be used if a {@code Subject} is specified.
@@ -203,6 +305,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  String getSASLMechanism();
@@ -220,48 +323,12 @@
  /**
   * Returns the quality-of-protection options to use.
   * The order of the list specifies the preference order.
   * Returns {@code true} if the server must authenticate to the client. The
   * default is {@code false}.
   *
   * @return The list of quality-of-protection options to use.
   * @return {@code true} if the server must authenticate to the client.
   */
  QOPOption[] getQOP();
  /**
   * Returns whether the server must authenticate to the client.
   * The default is {@code false}.
   *
   * @return {@code true} if the server must authenticate
   *         to the client or {@code false} otherwise.
   */
  boolean getServerAuth();
  /**
   * Returns the maximum size of the receive buffer in bytes. The
   * default is 65536. The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum send
   * buffer size.
   *
   * @return The maximum size of the receive buffer in bytes.
   */
  int getMaxReceiveBufferSize();
  /**
   * Returns the maximum size of the send buffer in bytes. The
   * default is 65536. The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum receive
   * buffer size.
   *
   * @return The maximum size of the send buffer in bytes.
   */
  int getMaxSendBufferSize();
  boolean isServerAuth();
@@ -326,6 +393,38 @@
  /**
   * Sets the maximum size of the receive buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * send buffer size. The default size is 65536.
   *
   * @param size
   *          The maximum size of the receive buffer in bytes.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit the buffer size to be set.
   */
  GSSAPISASLBindRequest setMaxReceiveBufferSize(int size)
      throws UnsupportedOperationException;
  /**
   * Sets the maximum size of the send buffer in bytes. The actual maximum
   * number of bytes will be the minimum of this number and the peer's maximum
   * receive buffer size. The default size is 65536.
   *
   * @param size
   *          The maximum size of the send buffer in bytes.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit the buffer size to be set.
   */
  GSSAPISASLBindRequest setMaxSendBufferSize(int size)
      throws UnsupportedOperationException;
  /**
   * Sets the password of the user that the client wishes to bind as.
   * <p>
   * <b>NOTE</b>: this will not be used if a {@code Subject} is specified.
@@ -383,6 +482,22 @@
  /**
   * Specifies whether or not the server must authenticate to the client. The
   * default is {@code false}.
   *
   * @param serverAuth
   *          {@code true} if the server must authenticate to the client or
   *          {@code false} otherwise.
   * @return This bind request.
   * @throws UnsupportedOperationException
   *           If this bind request does not permit server auth to be set.
   */
  GSSAPISASLBindRequest setServerAuth(boolean serverAuth)
      throws UnsupportedOperationException;
  /**
   * Sets the Kerberos subject of the user to be authenticated.
   * <p>
   * <b>NOTE</b>: if a {@code Subject} is specified then the authentication ID,
@@ -395,54 +510,4 @@
   *           If {@code subject} was {@code null}.
   */
  GSSAPISASLBindRequest setSubject(Subject subject) throws NullPointerException;
  /**
   * Specifies the quality-of-protection options to use.
   * The order of the list specifies the preference order.
   *
   * @param qopOptions The list of quality-of-protection options to
   *                   use.
   * @return This bind request.
   */
  GSSAPISASLBindRequest setQOP(QOPOption... qopOptions);
  /**
   * Specifies whether the server must authenticate to the client.
   *
   * @param serverAuth {@code true} if the server must authenticate
   *                   to the client or {@code false} otherwise.
   * @return This bind request.
   */
  GSSAPISASLBindRequest setServerAuth(boolean serverAuth);
  /**
   * Specifies the maximum size of the receive buffer in bytes.
   * The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum send
   * buffer size.
   *
   * @param maxBuffer The maximum size of the receive buffer in bytes.
   * @return This bind request.
   */
  GSSAPISASLBindRequest setMaxReceiveBufferSize(int maxBuffer);
  /**
   * Specifies the maximum size of the send buffer in bytes.
   *  The actual maximum number of bytes will
   * be the minimum of this number and the peer's maximum receive
   * buffer size.
   *
   * @param maxBuffer The maximum size of the send buffer in bytes.
   * @return This bind request.
   */
  GSSAPISASLBindRequest setMaxSendBufferSize(int maxBuffer);
}
opendj-sdk/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequestImpl.java
@@ -33,11 +33,11 @@
import static com.sun.opends.sdk.messages.Messages.ERR_SASL_CONTEXT_CREATE_ERROR;
import static com.sun.opends.sdk.messages.Messages.ERR_SASL_PROTOCOL_ERROR;
import static com.sun.opends.sdk.util.StaticUtils.getExceptionMessage;
import static com.sun.opends.sdk.util.StaticUtils.joinCollection;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
@@ -118,9 +118,9 @@
        // FIXME: Is this the best result code?
        final LocalizableMessage message = ERR_LDAPAUTH_GSSAPI_LOCAL_AUTHENTICATION_FAILED
            .get(StaticUtils.getExceptionMessage(e));
        throw ErrorResultException.wrap(Responses.newResult(
            ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
            message.toString()).setCause(e));
        throw ErrorResultException.wrap(Responses
            .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
            .setDiagnosticMessage(message.toString()).setCause(e));
      }
      return subject;
    }
@@ -128,7 +128,6 @@
    private final SaslClient saslClient;
    private final String serverName;
    private final String authorizationID;
    private final Subject subject;
@@ -137,6 +136,7 @@
    private final PrivilegedExceptionAction<Boolean> evaluateAction =
      new PrivilegedExceptionAction<Boolean>()
    {
      @Override
      public Boolean run() throws ErrorResultException
      {
        if (lastResult.getResultCode() == ResultCode.SASL_BIND_IN_PROGRESS
@@ -152,9 +152,10 @@
          {
            // FIXME: I18N need to have a better error message.
            // FIXME: Is this the best result code?
            throw ErrorResultException.wrap(Responses.newResult(
                ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
                "An error occurred during multi-stage authentication")
            throw ErrorResultException.wrap(Responses
                .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
                .setDiagnosticMessage(
                    "An error occurred during multi-stage authentication")
                .setCause(e));
          }
        }
@@ -162,37 +163,6 @@
      }
    };
    private final PrivilegedExceptionAction<SaslClient> invokeAction =
      new PrivilegedExceptionAction<SaslClient>()
    {
      public SaslClient run() throws ErrorResultException
      {
        final Map<String, String> props = new HashMap<String, String>();
        props.put(Sasl.QOP, "auth-conf,auth-int,auth");
        try
        {
          final SaslClient saslClient = Sasl.createSaslClient(
              new String[] { SASL_MECHANISM_NAME }, authorizationID,
              SASL_DEFAULT_PROTOCOL, serverName, props, Client.this);
          if (saslClient.hasInitialResponse())
          {
            setNextSASLCredentials(saslClient.evaluateChallenge(new byte[0]));
          }
          else
          {
            setNextSASLCredentials((ByteString) null);
          }
        }
        catch (final SaslException e)
        {
          throw ErrorResultException.wrap(Responses.newResult(
              ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e));
        }
        return saslClient;
      }
    };
    private Client(final GSSAPISASLBindRequestImpl initialBindRequest,
@@ -211,11 +181,72 @@
            initialBindRequest.getPassword(), initialBindRequest.getRealm(),
            initialBindRequest.getKDCAddress());
      }
      this.serverName = serverName;
      try
      {
        this.saslClient = Subject.doAs(subject, invokeAction);
        this.saslClient = Subject.doAs(subject,
            new PrivilegedExceptionAction<SaslClient>()
            {
              @Override
              public SaslClient run() throws ErrorResultException
              {
                // Create property map containing all the parameters.
                final Map<String, String> props = new HashMap<String, String>();
                final List<String> qopValues = initialBindRequest.getQOPs();
                if (!qopValues.isEmpty())
                {
                  props.put(Sasl.QOP, joinCollection(qopValues, ","));
                }
                final Boolean serverAuth = initialBindRequest.isServerAuth();
                if (serverAuth != null)
                {
                  props.put(Sasl.SERVER_AUTH, String.valueOf(serverAuth));
                }
                Integer size = initialBindRequest.getMaxReceiveBufferSize();
                if (size != null)
                {
                  props.put(Sasl.MAX_BUFFER, String.valueOf(size));
                }
                size = initialBindRequest.getMaxSendBufferSize();
                if (size != null)
                {
                  props.put("javax.security.sasl.sendmaxbuffer",
                      String.valueOf(size));
                }
                for (final Map.Entry<String, String> e : initialBindRequest
                    .getAdditionalAuthParams().entrySet())
                {
                  props.put(e.getKey(), e.getValue());
                }
                try
                {
                  final SaslClient saslClient = Sasl.createSaslClient(
                      new String[] { SASL_MECHANISM_NAME }, authorizationID,
                      SASL_DEFAULT_PROTOCOL, serverName, props, Client.this);
                  if (saslClient.hasInitialResponse())
                  {
                    setNextSASLCredentials(saslClient
                        .evaluateChallenge(new byte[0]));
                  }
                  else
                  {
                    setNextSASLCredentials((ByteString) null);
                  }
                }
                catch (final SaslException e)
                {
                  throw ErrorResultException.wrap(Responses.newResult(
                      ResultCode.CLIENT_SIDE_LOCAL_ERROR).setCause(e));
                }
                return saslClient;
              }
            });
      }
      catch (final PrivilegedActionException e)
      {
@@ -228,9 +259,9 @@
          // This should not happen. Must be a bug.
          final LocalizableMessage msg = ERR_SASL_CONTEXT_CREATE_ERROR.get(
              SASL_MECHANISM_NAME, getExceptionMessage(e));
          throw ErrorResultException.wrap(Responses.newResult(
              ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
              msg.toString()).setCause(e));
          throw ErrorResultException.wrap(Responses
              .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
              .setDiagnosticMessage(msg.toString()).setCause(e));
        }
      }
    }
@@ -272,9 +303,9 @@
          // This should not happen. Must be a bug.
          final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
              SASL_MECHANISM_NAME, getExceptionMessage(e));
          throw ErrorResultException.wrap(Responses.newResult(
              ResultCode.CLIENT_SIDE_LOCAL_ERROR).setDiagnosticMessage(
              msg.toString()).setCause(e));
          throw ErrorResultException.wrap(Responses
              .newResult(ResultCode.CLIENT_SIDE_LOCAL_ERROR)
              .setDiagnosticMessage(msg.toString()).setCause(e));
        }
      }
    }
@@ -309,9 +340,9 @@
      {
        final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
            SASL_MECHANISM_NAME, getExceptionMessage(e));
        throw ErrorResultException.wrap(Responses.newResult(
            ResultCode.CLIENT_SIDE_DECODING_ERROR).setDiagnosticMessage(
            msg.toString()).setCause(e));
        throw ErrorResultException.wrap(Responses
            .newResult(ResultCode.CLIENT_SIDE_DECODING_ERROR)
            .setDiagnosticMessage(msg.toString()).setCause(e));
      }
    }
@@ -329,9 +360,9 @@
      {
        final LocalizableMessage msg = ERR_SASL_PROTOCOL_ERROR.get(
            SASL_MECHANISM_NAME, getExceptionMessage(e));
        throw ErrorResultException.wrap(Responses.newResult(
            ResultCode.CLIENT_SIDE_ENCODING_ERROR).setDiagnosticMessage(
            msg.toString()).setCause(e));
        throw ErrorResultException.wrap(Responses
            .newResult(ResultCode.CLIENT_SIDE_ENCODING_ERROR)
            .setDiagnosticMessage(msg.toString()).setCause(e));
      }
    }
@@ -341,6 +372,7 @@
  // If null then authenticationID and password must be present.
  private Subject subject = null;
  // Ignored if subject is non-null.
  private String authenticationID = null;
  private ByteString password = null;
@@ -351,6 +383,15 @@
  // Optional authorization ID.
  private String authorizationID = null;
  private final Map<String, String> additionalAuthParams = new LinkedHashMap<String, String>();
  private final List<String> qopValues = new LinkedList<String>();
  // Don't use primitives for these so that we can distinguish between default
  // settings (null) and values set by the caller.
  private Boolean serverAuth = null;
  private Integer maxReceiveBufferSize = null;
  private Integer maxSendBufferSize = null;
  GSSAPISASLBindRequestImpl(final String authenticationID,
@@ -374,6 +415,38 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest addAdditionalAuthParam(final String name,
      final String value) throws UnsupportedOperationException,
      NullPointerException
  {
    Validator.ensureNotNull(name, value);
    additionalAuthParams.put(name, value);
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest addQOP(final String... qopValues)
      throws UnsupportedOperationException, NullPointerException
  {
    for (final String qopValue : qopValues)
    {
      this.qopValues.add(Validator.ensureNotNull(qopValue));
    }
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public BindClient createBindClient(final String serverName)
      throws ErrorResultException
  {
@@ -385,6 +458,18 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public Map<String, String> getAdditionalAuthParams()
  {
    return additionalAuthParams;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String getAuthenticationID()
  {
    return authenticationID;
@@ -395,6 +480,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public String getAuthorizationID()
  {
    return authorizationID;
@@ -405,6 +491,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public String getKDCAddress()
  {
    return kdcAddress;
@@ -415,6 +502,29 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public int getMaxReceiveBufferSize()
  {
    return maxReceiveBufferSize == null ? 65536 : maxReceiveBufferSize;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public int getMaxSendBufferSize()
  {
    return maxSendBufferSize == null ? 65536 : maxSendBufferSize;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public ByteString getPassword()
  {
    return password;
@@ -425,6 +535,18 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public List<String> getQOPs()
  {
    return qopValues;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String getRealm()
  {
    return realm;
@@ -435,6 +557,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public String getSASLMechanism()
  {
    return SASL_MECHANISM_NAME;
@@ -445,6 +568,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public Subject getSubject()
  {
    return subject;
@@ -455,6 +579,18 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean isServerAuth()
  {
    return serverAuth == null ? false : serverAuth;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setAuthenticationID(final String authenticationID)
      throws NullPointerException
  {
@@ -468,6 +604,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setAuthorizationID(final String authorizationID)
  {
    this.authorizationID = authorizationID;
@@ -479,6 +616,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setKDCAddress(final String address)
  {
    this.kdcAddress = address;
@@ -490,6 +628,33 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setMaxReceiveBufferSize(final int size)
      throws UnsupportedOperationException
  {
    maxReceiveBufferSize = size;
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setMaxSendBufferSize(final int size)
      throws UnsupportedOperationException
  {
    maxSendBufferSize = size;
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setPassword(final ByteString password)
      throws NullPointerException
  {
@@ -503,6 +668,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setPassword(final String password)
      throws NullPointerException
  {
@@ -516,6 +682,7 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setRealm(final String realm)
  {
    this.realm = realm;
@@ -527,6 +694,20 @@
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setServerAuth(final boolean serverAuth)
      throws UnsupportedOperationException
  {
    this.serverAuth = serverAuth;
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public GSSAPISASLBindRequest setSubject(final Subject subject)
      throws NullPointerException
  {
@@ -539,150 +720,6 @@
  /**
   * {@inheritDoc}
   */
  public QOPOption[] getQOP() {
    String value = System.getProperty(Sasl.QOP);
    if(value == null || value.length() == 0)
    {
      return new QOPOption[]{QOPOption.AUTH};
    }
    String[] values = value.split(",");
    QOPOption[] options = new QOPOption[values.length];
    for(int i = 0; i < values.length; i++)
    {
      String v = values[i].trim();
      if(v.equalsIgnoreCase("auth"))
      {
        options[i] = QOPOption.AUTH;
      }
      else if(v.equalsIgnoreCase("auth-int"))
      {
        options[i] = QOPOption.AUTH_INT;
      }
      else if(v.equalsIgnoreCase("auth-conf"))
      {
        options[i] = QOPOption.AUTH_CONF;
      }
    }
    return options;
  }
  /**
   * {@inheritDoc}
   */
  public boolean getServerAuth() {
    String value = System.getProperty(Sasl.SERVER_AUTH);
    return !(value == null || value.length() == 0) &&
        value.equalsIgnoreCase("true");
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxReceiveBufferSize() {
    String value = System.getProperty(Sasl.MAX_BUFFER);
    if(value == null || value.length() == 0)
    {
      return 65536;
    }
    return Integer.parseInt(value);
  }
  /**
   * {@inheritDoc}
   */
  public int getMaxSendBufferSize() {
    String value = System.getProperty("javax.security.sasl.sendmaxbuffer");
    if(value == null || value.length() == 0)
    {
      return 65536;
    }
    return Integer.parseInt(value);
  }
  /**
   * {@inheritDoc}
   */
  public GSSAPISASLBindRequest setQOP(QOPOption... qopOptions) {
    String values = null;
    for(QOPOption option : qopOptions)
    {
      String value = null;
      if(option == QOPOption.AUTH)
      {
        value = "auth";
      }
      else if(option == QOPOption.AUTH_INT)
      {
        value = "auth-int";
      }
      else if(option == QOPOption.AUTH_CONF)
      {
        value = "auth-conf";
      }
      if(value != null)
      {
        if(values == null)
        {
          values = value;
        }
        else
        {
          values += (", " + value);
        }
      }
    }
    System.setProperty(Sasl.QOP, values);
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public GSSAPISASLBindRequest setServerAuth(boolean serverAuth) {
    System.setProperty(Sasl.SERVER_AUTH, String.valueOf(serverAuth));
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public GSSAPISASLBindRequest setMaxReceiveBufferSize(int maxBuffer) {
    System.setProperty(Sasl.MAX_BUFFER, String.valueOf(maxBuffer));
    return this;
  }
  /**
   * {@inheritDoc}
   */
  public GSSAPISASLBindRequest setMaxSendBufferSize(int maxBuffer) {
    System.setProperty("javax.security.sasl.sendmaxbuffer",
        String.valueOf(maxBuffer));
    return this;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString()
  {
opendj-sdk/sdk/src/org/opends/sdk/requests/GenericExtendedRequestImpl.java
@@ -22,22 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.requests;
import org.opends.sdk.ByteString;
import org.opends.sdk.DecodeException;
import org.opends.sdk.DecodeOptions;
import org.opends.sdk.ResultCode;
import org.opends.sdk.*;
import org.opends.sdk.controls.Control;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
import org.opends.sdk.responses.GenericExtendedResult;
import org.opends.sdk.responses.Responses;
import org.opends.sdk.responses.*;
import com.sun.opends.sdk.util.StaticUtils;
import com.sun.opends.sdk.util.Validator;
@@ -80,11 +74,11 @@
  private static final class GenericExtendedResultDecoder implements
      ExtendedResultDecoder<GenericExtendedResult>
  private static final class GenericExtendedResultDecoder extends
      AbstractExtendedResultDecoder<GenericExtendedResult>
  {
    public GenericExtendedResult adaptExtendedErrorResult(
    public GenericExtendedResult newExtendedErrorResult(
        final ResultCode resultCode, final String matchedDN,
        final String diagnosticMessage)
    {
opendj-sdk/sdk/src/org/opends/sdk/requests/PasswordModifyExtendedRequestImpl.java
@@ -39,10 +39,7 @@
import org.opends.sdk.asn1.ASN1Reader;
import org.opends.sdk.asn1.ASN1Writer;
import org.opends.sdk.controls.Control;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
import org.opends.sdk.responses.PasswordModifyExtendedResult;
import org.opends.sdk.responses.Responses;
import org.opends.sdk.responses.*;
@@ -105,10 +102,10 @@
  private static final class ResultDecoder implements
      ExtendedResultDecoder<PasswordModifyExtendedResult>
  private static final class ResultDecoder extends
      AbstractExtendedResultDecoder<PasswordModifyExtendedResult>
  {
    public PasswordModifyExtendedResult adaptExtendedErrorResult(
    public PasswordModifyExtendedResult newExtendedErrorResult(
        final ResultCode resultCode, final String matchedDN,
        final String diagnosticMessage)
    {
opendj-sdk/sdk/src/org/opends/sdk/requests/Request.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.requests;
@@ -41,8 +41,6 @@
/**
 * The base class of all Requests provides methods for querying and manipulating
 * the set of Controls included with a Request.
 * <p>
 * TODO: added complete description including sub-types.
 */
public interface Request
{
opendj-sdk/sdk/src/org/opends/sdk/requests/Requests.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.requests;
@@ -59,13 +59,13 @@
  /**
   * Creates a new abandon request using the provided message ID.
   *
   * @param messageID
   *          The message ID of the request to be abandoned.
   * @param requestID
   *          The request ID of the request to be abandoned.
   * @return The new abandon request.
   */
  public static AbandonRequest newAbandonRequest(final int messageID)
  public static AbandonRequest newAbandonRequest(final int requestID)
  {
    return new AbandonRequestImpl(messageID);
    return new AbandonRequestImpl(requestID);
  }
@@ -187,14 +187,14 @@
  /**
   * Creates a new cancel extended request using the provided message ID.
   *
   * @param messageID
   *          The message ID of the request to be abandoned.
   * @param requestID
   *          The request ID of the request to be abandoned.
   * @return The new cancel extended request.
   */
  public static CancelExtendedRequest newCancelExtendedRequest(
      final int messageID)
      final int requestID)
  {
    return new CancelExtendedRequestImpl(messageID);
    return new CancelExtendedRequestImpl(requestID);
  }
@@ -589,6 +589,42 @@
  /**
   * Creates a new modify request containing a list of modifications which can
   * be used to transform {@code fromEntry} into entry {@code toEntry}.
   * <p>
   * The modify request is reversible: it will contain only modifications of
   * type {@link ModificationType#ADD ADD} and {@link ModificationType#DELETE
   * DELETE}.
   * <p>
   * Finally, the modify request will use the distinguished name taken from
   * {@code fromEntry}. Moreover, this method will not check to see if both
   * {@code fromEntry} and {@code toEntry} have the same distinguished name.
   * <p>
   * This method is equivalent to:
   *
   * <pre>
   * ModifyRequest request = Entries.diffEntries(fromEntry, toEntry);
   * </pre>
   *
   * @param fromEntry
   *          The source entry.
   * @param toEntry
   *          The destination entry.
   * @return A modify request containing a list of modifications which can be
   *         used to transform {@code fromEntry} into entry {@code toEntry}.
   * @throws NullPointerException
   *           If {@code fromEntry} or {@code toEntry} were {@code null}.
   * @see Entries#diffEntries(Entry, Entry)
   */
  public static final ModifyRequest newModifyRequest(Entry fromEntry,
      Entry toEntry) throws NullPointerException
  {
    return Entries.diffEntries(fromEntry, toEntry);
  }
  /**
   * Creates a new modify request using the provided distinguished name decoded
   * using the default schema.
   *
opendj-sdk/sdk/src/org/opends/sdk/requests/SearchRequest.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.requests;
@@ -47,20 +47,20 @@
public interface SearchRequest extends Request
{
  /**
   * Adds the provided attribute name to the list of attributes to be included
   * with each entry that matches the search criteria. Attributes that are
   * sub-types of listed attributes are implicitly included.
   * Adds the provided attribute name(s) to the list of attributes to be
   * included with each entry that matches the search criteria. Attributes that
   * are sub-types of listed attributes are implicitly included.
   *
   * @param attributeDescription
   *          The name of the attribute to be included with each entry.
   * @param attributeDescriptions
   *          The name(s) of the attribute to be included with each entry.
   * @return This search request.
   * @throws UnsupportedOperationException
   *           If this search request does not permit attribute names to be
   *           added.
   * @throws NullPointerException
   *           If {@code attributeDescription} was {@code null}.
   *           If {@code attributeDescriptions} was {@code null}.
   */
  SearchRequest addAttribute(String attributeDescription)
  SearchRequest addAttribute(String... attributeDescriptions)
      throws UnsupportedOperationException, NullPointerException;
opendj-sdk/sdk/src/org/opends/sdk/requests/SearchRequestImpl.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.requests;
@@ -94,11 +94,13 @@
  /**
   * {@inheritDoc}
   */
  public SearchRequest addAttribute(final String attributeDescription)
  public SearchRequest addAttribute(final String... attributeDescriptions)
      throws NullPointerException
  {
    Validator.ensureNotNull(attributeDescription);
    attributes.add(attributeDescription);
    for (String attributeDescription : attributeDescriptions)
    {
      attributes.add(Validator.ensureNotNull(attributeDescription));
    }
    return this;
  }
opendj-sdk/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java
@@ -31,15 +31,9 @@
import javax.net.ssl.SSLContext;
import org.opends.sdk.ByteString;
import org.opends.sdk.DecodeException;
import org.opends.sdk.DecodeOptions;
import org.opends.sdk.ResultCode;
import org.opends.sdk.*;
import org.opends.sdk.controls.Control;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
import org.opends.sdk.responses.GenericExtendedResult;
import org.opends.sdk.responses.Responses;
import org.opends.sdk.responses.*;
import com.sun.opends.sdk.util.Validator;
@@ -72,10 +66,10 @@
  private static final class ResultDecoder implements
      ExtendedResultDecoder<ExtendedResult>
  private static final class ResultDecoder extends
      AbstractExtendedResultDecoder<ExtendedResult>
  {
    public GenericExtendedResult adaptExtendedErrorResult(
    public GenericExtendedResult newExtendedErrorResult(
        final ResultCode resultCode, final String matchedDN,
        final String diagnosticMessage)
    {
opendj-sdk/sdk/src/org/opends/sdk/requests/WhoAmIExtendedRequestImpl.java
@@ -31,10 +31,7 @@
import org.opends.sdk.*;
import org.opends.sdk.controls.Control;
import org.opends.sdk.responses.ExtendedResult;
import org.opends.sdk.responses.ExtendedResultDecoder;
import org.opends.sdk.responses.Responses;
import org.opends.sdk.responses.WhoAmIExtendedResult;
import org.opends.sdk.responses.*;
@@ -66,10 +63,10 @@
  private static final class ResultDecoder implements
      ExtendedResultDecoder<WhoAmIExtendedResult>
  private static final class ResultDecoder extends
      AbstractExtendedResultDecoder<WhoAmIExtendedResult>
  {
    public WhoAmIExtendedResult adaptExtendedErrorResult(
    public WhoAmIExtendedResult newExtendedErrorResult(
        final ResultCode resultCode, final String matchedDN,
        final String diagnosticMessage)
    {
opendj-sdk/sdk/src/org/opends/sdk/responses/AbstractExtendedResultDecoder.java
New file
@@ -0,0 +1,137 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.responses;
import org.opends.sdk.*;
import org.opends.sdk.requests.ExtendedRequest;
/**
 * This class provides a skeletal implementation of the
 * {@code ExtendedResultDecoder} interface, to minimize the effort required to
 * implement this interface.
 *
 * @param <S>
 *          The type of result.
 */
public abstract class AbstractExtendedResultDecoder<S extends ExtendedResult>
    implements ExtendedResultDecoder<S>
{
  /**
   * Creates a new abstract extended result decoder.
   */
  protected AbstractExtendedResultDecoder()
  {
    // Nothing to do.
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public S adaptDecodeException(final DecodeException exception)
      throws NullPointerException
  {
    final S adaptedResult = newExtendedErrorResult(ResultCode.PROTOCOL_ERROR,
        "", exception.getMessage());
    adaptedResult.setCause(exception.getCause());
    return adaptedResult;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public <R extends ExtendedResult> ResultHandler<S> adaptExtendedResultHandler(
      final ExtendedRequest<R> request,
      final ResultHandler<? super R> resultHandler, final DecodeOptions options)
  {
    return new ResultHandler<S>()
    {
      @Override
      public void handleErrorResult(final ErrorResultException error)
      {
        final Result result = error.getResult();
        final R adaptedResult = request.getResultDecoder()
            .newExtendedErrorResult(result.getResultCode(),
                result.getMatchedDN(), result.getDiagnosticMessage());
        adaptedResult.setCause(result.getCause());
        resultHandler.handleErrorResult(ErrorResultException
            .wrap(adaptedResult));
      }
      @Override
      public void handleResult(final S result)
      {
        try
        {
          final R adaptedResult = request.getResultDecoder()
              .decodeExtendedResult(result, options);
          resultHandler.handleResult(adaptedResult);
        }
        catch (final DecodeException e)
        {
          final R adaptedResult = request.getResultDecoder()
              .adaptDecodeException(e);
          resultHandler.handleErrorResult(ErrorResultException
              .wrap(adaptedResult));
        }
      }
    };
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public abstract S decodeExtendedResult(ExtendedResult result,
      DecodeOptions options) throws DecodeException;
  /**
   * {@inheritDoc}
   */
  @Override
  public abstract S newExtendedErrorResult(ResultCode resultCode,
      String matchedDN, String diagnosticMessage) throws NullPointerException;
}
opendj-sdk/sdk/src/org/opends/sdk/responses/ExtendedResultDecoder.java
@@ -32,6 +32,8 @@
import org.opends.sdk.DecodeException;
import org.opends.sdk.DecodeOptions;
import org.opends.sdk.ResultCode;
import org.opends.sdk.ResultHandler;
import org.opends.sdk.requests.ExtendedRequest;
@@ -44,25 +46,46 @@
 */
public interface ExtendedResultDecoder<S extends ExtendedResult>
{
  /**
   * Adapts the provided error result parameters to an extended operation
   * result. This method is called when a generic failure occurs, such as a
   * connection failure, and the error result needs to be converted to a {@code
   * Result} of type {@code S}.
   * Creates a new extended operation error result using the provided decoding
   * exception. This method should be used to adapt {@code DecodeException}
   * encountered while decoding an extended request or result. The returned
   * error result will have the result code {@link ResultCode#PROTOCOL_ERROR}.
   *
   * @param resultCode
   *          The result code.
   * @param matchedDN
   *          The matched DN, which may be empty if none was provided.
   * @param diagnosticMessage
   *          The diagnostic message, which may be empty if none was provided.
   * @return The decoded extended operation error result.
   * @param exception
   *          The decoding exception to be adapted.
   * @return An extended operation error result representing the decoding
   *         exception.
   * @throws NullPointerException
   *           If {@code resultCode}, {@code matchedDN}, or {@code
   *           diagnosticMessage} were {@code null}.
   *           If {@code exception} was {@code null}.
   */
  S adaptExtendedErrorResult(ResultCode resultCode, String matchedDN,
      String diagnosticMessage) throws NullPointerException;
  S adaptDecodeException(DecodeException exception) throws NullPointerException;
  /**
   * Adapts the provided extended result handler into a result handler which is
   * compatible with this extended result decoder. Extended results handled by
   * the returned handler will be automatically converted and passed to the
   * provided result handler. Decoding errors encountered while decoding the
   * extended result will be converted into protocol errors.
   *
   * @param <R>
   *          The type of result handler to be adapted.
   * @param request
   *          The extended request whose result handler is to be adapted.
   * @param resultHandler
   *          The extended result handler which is to be adapted.
   * @param options
   *          The set of decode options which should be used when decoding the
   *          extended operation result.
   * @return A result handler which is compatible with this extended result
   *         decoder.
   */
  <R extends ExtendedResult> ResultHandler<S> adaptExtendedResultHandler(
      final ExtendedRequest<R> request,
      final ResultHandler<? super R> resultHandler, DecodeOptions options);
@@ -86,4 +109,26 @@
  S decodeExtendedResult(ExtendedResult result, DecodeOptions options)
      throws DecodeException;
  /**
   * Creates a new extended error result using the provided result code, matched
   * DN, and diagnostic message. This method is called when a generic failure
   * occurs, such as a connection failure, and the error result needs to be
   * converted to a {@code Result} of type {@code S}.
   *
   * @param resultCode
   *          The result code.
   * @param matchedDN
   *          The matched DN, which may be empty if none was provided.
   * @param diagnosticMessage
   *          The diagnostic message, which may be empty if none was provided.
   * @return The decoded extended operation error result.
   * @throws NullPointerException
   *           If {@code resultCode}, {@code matchedDN}, or
   *           {@code diagnosticMessage} were {@code null}.
   */
  S newExtendedErrorResult(ResultCode resultCode, String matchedDN,
      String diagnosticMessage) throws NullPointerException;
}
opendj-sdk/sdk/src/overview.html
New file
@@ -0,0 +1,103 @@
<html>
<body>
    The OpenDS SDK for Java provides a high performance easy to use
    library of classes and interfaces for accessing and implementing
    LDAP Directory Services as defined in <a
      href="http://tools.ietf.org/html/rfc4510">RFC 4510</a>.
    <br>
    <h1>Getting started</h1>
    The following example shows how the OpenDS SDK may be used to
    connect to a Directory Server, authenticate, and then perform a
    search. The search results are output as LDIF to the standard
    output:
    <br>
    <table bgcolor="#cccccc" border="0" cellpadding="2" cellspacing="2"
      width="100%">
      <tbody>
        <tr>
          <pre>    // Create an LDIF writer which will write the search results to stdout.
    final LDIFEntryWriter writer = new LDIFEntryWriter(System.out);
    Connection connection = null;
    try
    {
      // Connect and bind to the server.
      final LDAPConnectionFactory factory = new LDAPConnectionFactory("localhost", 1389);
      connection = factory.getConnection();
      connection.bind(userName, password);
      // Read the entries and output them as LDIF.
      final ConnectionEntryReader reader = connection.search(baseDN, scope, filter, attributes);
      while (reader.hasNext())
      {
        if (!reader.isReference())
        {
          // Got an entry.
          final SearchResultEntry entry = reader.readEntry();
          writer.writeComment("Search result entry: " + entry.getName().toString());
          writer.writeEntry(entry);
        }
        else
        {
          // Got a continuation reference.
          final SearchResultReference ref = reader.readReference();
          writer.writeComment("Search result reference: " + ref.getURIs().toString());
        }
      }
      writer.flush();
    }
    catch (final Exception e)
    {
      // Handle exceptions...
      System.err.println(e.getMessage());
    }
    finally
    {
      if (connection != null)
      {
        connection.close();
      }
    }</pre>
        </tr>
      </tbody>
    </table>
    <br>
    Additional examples can be found in the file examples.zip which is
    included with this SDK.
    <br>
    <h1>Creating connections</h1>
    The following classes can be used to create and manage connections to
    LDAP Directory Servers:
    <ul>
      <li>{@link org.opends.sdk.LDAPConnectionFactory}</li>
      <li>{@link org.opends.sdk.Connection}</li>
      <li>{@link org.opends.sdk.Connections}</li>
    </ul>
    <br>
    <h1>Creating requests</h1>
    The following classes can be used to create LDAP requests:
    <ul>
      <li>{@link org.opends.sdk.requests.Requests}</li>
      <li>{@link org.opends.sdk.requests.Request}</li>
    </ul>
    <br>
    <h1>Using controls</h1>
    Common LDAP control implementations can be found in
    {@link org.opends.sdk.controls}.
    <br>
    <br>
    <h1>Core types</h1>
    The following classes and interfaces represent core types:
    <ul>
      <li>{@link org.opends.sdk.AttributeDescription}</li>
      <li>{@link org.opends.sdk.Attribute}</li>
      <li>{@link org.opends.sdk.DN}</li>
      <li>{@link org.opends.sdk.Entry}</li>
      <li>{@link org.opends.sdk.Filter}</li>
    </ul>
    <br>
@see <a href="http://tools.ietf.org/html/rfc4511">RFC 4511 - Lightweight
      Directory Access Protocol (LDAP): The Protocol </a>
@see org.opends.sdk
</body>
</html>
opendj-sdk/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferReaderTestCase.java
@@ -35,8 +35,8 @@
import org.opends.sdk.asn1.ASN1Reader;
import org.opends.sdk.asn1.ASN1ReaderTestCase;
import com.sun.grizzly.memory.ByteBufferWrapper;
import com.sun.grizzly.memory.DefaultMemoryManager;
import org.glassfish.grizzly.memory.ByteBufferWrapper;
import org.glassfish.grizzly.memory.DefaultMemoryManager;
opendj-sdk/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferWriterTestCase.java
@@ -37,8 +37,8 @@
import org.opends.sdk.asn1.ASN1Writer;
import org.opends.sdk.asn1.ASN1WriterTestCase;
import com.sun.grizzly.Buffer;
import com.sun.grizzly.memory.ByteBufferWrapper;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.memory.ByteBufferWrapper;
opendj-sdk/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/GlobalTransportFactoryTestCase.java
@@ -36,8 +36,8 @@
import org.testng.annotations.Test;
import com.sun.grizzly.TransportFactory;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.TransportFactory;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java
@@ -32,12 +32,15 @@
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.opends.sdk.requests.DigestMD5SASLBindRequest;
import org.opends.sdk.requests.Requests;
import org.opends.sdk.requests.SearchRequest;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
@@ -45,6 +48,7 @@
import javax.net.ssl.SSLContext;
/**
 * Tests the connectionfactory classes.
 */
@@ -55,7 +59,7 @@
    // latch.
    private final CountDownLatch latch;
    // invalid flag.
    private volatile boolean invalid;
    private volatile ErrorResultException error;
@@ -69,7 +73,7 @@
    public void handleErrorResult(final ErrorResultException error)
    {
      // came here.
      invalid = true;
      this.error = error;
      latch.countDown();
    }
@@ -96,66 +100,125 @@
    TestCaseUtils.startServer();
  }
  @DataProvider(name = "connectionFactories")
  public Object[][] getModifyDNRequests() throws Exception
  public Object[][] getConnectyionFactories() throws Exception
  {
    Object[][] factories = new Object[7][1];
    Object[][] factories = new Object[21][1];
    // HeartBeatConnectionFactory
    // Use custom search request.
    SearchRequest request = Requests.newSearchRequest(
        "uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT, "objectclass=*",
        "cn");
        "uid=user.0,ou=people,o=test", SearchScope.BASE_OBJECT,
        "objectclass=*", "cn");
    factories[0][0] = new HeartBeatConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
        1000, TimeUnit.MILLISECONDS, request);
    factories[0][0] = new HeartBeatConnectionFactory(new LDAPConnectionFactory(
        "localhost", TestCaseUtils.getLdapPort()), 1000, TimeUnit.MILLISECONDS,
        request);
    // InternalConnectionFactory
    factories[1][0] = Connections
      .newInternalConnectionFactory(LDAPServer.getInstance(), null);
    factories[1][0] = Connections.newInternalConnectionFactory(
        LDAPServer.getInstance(), null);
    // AuthenticatedConnectionFactory
    factories[2][0] = new AuthenticatedConnectionFactory(
      new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
      Requests.newSimpleBindRequest("", ""));
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
        Requests.newSimpleBindRequest("", ""));
    // AuthenticatedConnectionFactory with multi-stage SASL
    factories[3][0] = new AuthenticatedConnectionFactory(
      new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
      Requests.newCRAMMD5SASLBindRequest("id:user",
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
        Requests.newCRAMMD5SASLBindRequest("id:user",
            ByteString.valueOf("password")));
    // LDAPConnectionFactory with default options
    factories[4][0] = new LDAPConnectionFactory(
      "localhost", TestCaseUtils.getLdapPort());
    factories[4][0] = new LDAPConnectionFactory("localhost",
        TestCaseUtils.getLdapPort());
    // LDAPConnectionFactory with startTLS
    SSLContext sslContext = new SSLContextBuilder().
        setTrustManager(TrustManagers.trustAll()).getSSLContext();
    LDAPOptions options = new LDAPOptions().setSSLContext(sslContext).
        setUseStartTLS(true).setEnabledCipherSuites(
        new String[]{"SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
                         "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                         "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
                         "SSL_DH_anon_WITH_DES_CBC_SHA",
                         "SSL_DH_anon_WITH_RC4_128_MD5",
                         "TLS_DH_anon_WITH_AES_128_CBC_SHA",
                         "TLS_DH_anon_WITH_AES_256_CBC_SHA"});
    factories[5][0] = new LDAPConnectionFactory(
        "localhost", TestCaseUtils.getLdapPort(), options);
    SSLContext sslContext = new SSLContextBuilder().setTrustManager(
        TrustManagers.trustAll()).getSSLContext();
    LDAPOptions options = new LDAPOptions()
        .setSSLContext(sslContext)
        .setUseStartTLS(true)
        .setEnabledCipherSuites(
            new String[] { "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
                "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
                "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
                "SSL_DH_anon_WITH_DES_CBC_SHA", "SSL_DH_anon_WITH_RC4_128_MD5",
                "TLS_DH_anon_WITH_AES_128_CBC_SHA",
                "TLS_DH_anon_WITH_AES_256_CBC_SHA" });
    factories[5][0] = new LDAPConnectionFactory("localhost",
        TestCaseUtils.getLdapPort(), options);
    // startTLS + SASL confidentiality
    // Use IP address here so that DIGEST-MD5 host verification works if local
    // host name is not localhost (e.g. on some machines it might be
    // localhost.localdomain).
    factories[6][0] = new AuthenticatedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort(),
            options), Requests.newDigestMD5SASLBindRequest("id:user",
            ByteString.valueOf("password")).setQOP(
            DigestMD5SASLBindRequest.QOPOption.AUTH_CONF).setCipher(
            DigestMD5SASLBindRequest.CipherOption.TRIPLE_DES_RC4));
        new LDAPConnectionFactory(new InetSocketAddress("127.0.0.1",
            TestCaseUtils.getLdapPort()), options), Requests
            .newDigestMD5SASLBindRequest("id:user",
                ByteString.valueOf("password"))
            .addQOP(DigestMD5SASLBindRequest.QOP_AUTH_CONF)
            .setCipher(DigestMD5SASLBindRequest.CIPHER_LOW));
    // Connection pool and load balancing tests.
    ConnectionFactory offlineServer1 = Connections.newNamedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.findFreePort()),
        "offline1");
    ConnectionFactory offlineServer2 = Connections.newNamedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.findFreePort()),
        "offline2");
    ConnectionFactory onlineServer = Connections.newNamedConnectionFactory(
        new LDAPConnectionFactory("localhost", TestCaseUtils.getLdapPort()),
        "online");
    // Connection pools.
    factories[7][0] = Connections.newConnectionPool(onlineServer, 10);
    // Round robin.
    factories[8][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
            onlineServer, offlineServer1)));
    factories[9][0] = factories[8][0];
    factories[10][0] = factories[8][0];
    factories[11][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, onlineServer)));
    factories[12][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, offlineServer2, onlineServer)));
    factories[13][0] = Connections
        .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
            Connections.newConnectionPool(offlineServer1, 10),
            Connections.newConnectionPool(onlineServer, 10))));
    // Fail-over.
    factories[14][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
            onlineServer, offlineServer1)));
    factories[15][0] = factories[14][0];
    factories[16][0] = factories[14][0];
    factories[17][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, onlineServer)));
    factories[18][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
            offlineServer1, offlineServer2, onlineServer)));
    factories[19][0] = Connections
        .newLoadBalancer(new FailoverLoadBalancingAlgorithm(Arrays.asList(
            Connections.newConnectionPool(offlineServer1, 10),
            Connections.newConnectionPool(onlineServer, 10))));
    factories[20][0] = Connections.newConnectionPool(onlineServer, 10);
    return factories;
  }
  /**
   * Tests the async connection in the blocking mode. This is not fully async as
   * it blocks on the future.
@@ -166,10 +229,10 @@
  public void testBlockingFutureNoHandler(ConnectionFactory factory)
      throws Exception
  {
    final FutureResult<AsynchronousConnection> future =
        factory.getAsynchronousConnection(null);
    final FutureResult<AsynchronousConnection> future = factory
        .getAsynchronousConnection(null);
    final AsynchronousConnection con = future.get();
    // quickly check if iit is a valid connection.
    // quickly check if it is a valid connection.
    // Don't use a result handler.
    assertNotNull(con.readRootDSE(null).get());
    con.close();
@@ -189,15 +252,14 @@
    // Use the handler to get the result asynchronously.
    final CountDownLatch latch = new CountDownLatch(1);
    final MyResultHandler handler = new MyResultHandler(latch);
    final FutureResult<AsynchronousConnection> future =
        factory.getAsynchronousConnection(handler);
    final FutureResult<AsynchronousConnection> future = factory
        .getAsynchronousConnection(handler);
    // Since we don't have anything to do, we would rather
    // be notified by the latch when the other thread calls our handler.
    latch.await(); // should do a timed wait rather?
    if (handler.invalid)
    if (handler.error != null)
    {
      // There was an error.
      throw new Exception();
      throw handler.error;
    }
  }
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/DNTestCase.java
@@ -188,11 +188,16 @@
        { "cn=aaaa,dc=com", "cn=AAA,dc=com", 1 },
        { "cn=aaab,dc=com", "cn=aaaa,dc=com", 1 },
        { "cn=aaaa,dc=com", "cn=aaab,dc=com", -1 },
        { "dc=aaa,dc=aaa", "dc=bbb", -1 }, { "dc=bbb,dc=aaa", "dc=bbb", -1 },
        { "dc=ccc,dc=aaa", "dc=bbb", -1 }, { "dc=aaa,dc=bbb", "dc=bbb", 1 },
        { "dc=bbb,dc=bbb", "dc=bbb", 1 }, { "dc=ccc,dc=bbb", "dc=bbb", 1 },
        { "dc=aaa,dc=ccc", "dc=bbb", 1 }, { "dc=bbb,dc=ccc", "dc=bbb", 1 },
        { "dc=ccc,dc=ccc", "dc=bbb", 1 }, { "", "dc=bbb", -1 },
        { "dc=aaa,dc=aaa", "dc=bbb", -1 },
        { "dc=bbb,dc=aaa", "dc=bbb", -1 },
        { "dc=ccc,dc=aaa", "dc=bbb", -1 },
        { "dc=aaa,dc=bbb", "dc=bbb", 1 },
        { "dc=bbb,dc=bbb", "dc=bbb", 1 },
        { "dc=ccc,dc=bbb", "dc=bbb", 1 },
        { "dc=aaa,dc=ccc", "dc=bbb", 1 },
        { "dc=bbb,dc=ccc", "dc=bbb", 1 },
        { "dc=ccc,dc=ccc", "dc=bbb", 1 },
        { "", "dc=bbb", -1 },
        { "dc=bbb", "", 1 } };
  }
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntriesTestCase.java
New file
@@ -0,0 +1,229 @@
/*
 * 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-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
import java.util.Iterator;
import org.opends.sdk.requests.ModifyRequest;
import org.opends.sdk.requests.Requests;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
 * Test {@code Entries}.
 */
public final class EntriesTestCase extends SdkTestCase
{
  /**
   * Creates test data for {@link #testDiffEntries}.
   *
   * @return The test data.
   */
  @DataProvider(name = "createTestDiffEntriesData")
  public Object[][] createTestDiffEntriesData()
  {
    // @formatter:off
    Entry empty = new LinkedHashMapEntry(
        "dn: cn=test",
        "objectClass: top",
        "objectClass: test"
    );
    Entry from = new LinkedHashMapEntry(
        "dn: cn=test",
        "objectClass: top",
        "objectClass: test",
        "fromOnly: fromOnlyValue",
        "bothSame: one",
        "bothSame: two",
        "bothSame: three",
        "bothDifferentDeletes: common",
        "bothDifferentDeletes: fromOnly1",
        "bothDifferentDeletes: fromOnly2",
        "bothDifferentAdds: common",
        "bothDifferentAddsAndDeletes: common",
        "bothDifferentAddsAndDeletes: fromOnly",
        "bothDifferentReplace: fromOnly1",
        "bothDifferentReplace: fromOnly2"
    );
    Entry to = new LinkedHashMapEntry(
        "dn: cn=test",
        "objectClass: top",
        "objectClass: test",
        "toOnly: toOnlyValue",
        "bothSame: one",
        "bothSame: two",
        "bothSame: three",
        "bothDifferentDeletes: common",
        "bothDifferentAdds: common",
        "bothDifferentAdds: toOnly1",
        "bothDifferentAdds: toOnly2",
        "bothDifferentAddsAndDeletes: common",
        "bothDifferentAddsAndDeletes: toOnly",
        "bothDifferentReplace: toOnly1",
        "bothDifferentReplace: toOnly2"
    );
    ModifyRequest diffFromEmpty = Requests.newModifyRequest(
        "dn: cn=test",
        "changetype: modify",
        "delete: bothDifferentAdds",
        "bothDifferentAdds: common",
        "-",
        "delete: bothDifferentAddsAndDeletes",
        "bothDifferentAddsAndDeletes: common",
        "bothDifferentAddsAndDeletes: fromOnly",
        "-",
        "delete: bothDifferentDeletes",
        "bothDifferentDeletes: common",
        "bothDifferentDeletes: fromOnly1",
        "bothDifferentDeletes: fromOnly2",
        "-",
        "delete: bothDifferentReplace",
        "bothDifferentReplace: fromOnly1",
        "bothDifferentReplace: fromOnly2",
        "-",
        "delete: bothSame",
        "bothSame: one",
        "bothSame: two",
        "bothSame: three",
        "-",
        "delete: fromOnly",
        "fromOnly: fromOnlyValue"
    );
    ModifyRequest diffEmptyTo = Requests.newModifyRequest(
        "dn: cn=test",
        "changetype: modify",
        "add: bothDifferentAdds",
        "bothDifferentAdds: common",
        "bothDifferentAdds: toOnly1",
        "bothDifferentAdds: toOnly2",
        "-",
        "add: bothDifferentAddsAndDeletes",
        "bothDifferentAddsAndDeletes: common",
        "bothDifferentAddsAndDeletes: toOnly",
        "-",
        "add: bothDifferentDeletes",
        "bothDifferentDeletes: common",
        "-",
        "add: bothDifferentReplace",
        "bothDifferentReplace: toOnly1",
        "bothDifferentReplace: toOnly2",
        "-",
        "add: bothSame",
        "bothSame: one",
        "bothSame: two",
        "bothSame: three",
        "-",
        "add: toOnly",
        "toOnly: toOnlyValue"
    );
    ModifyRequest diffFromTo = Requests.newModifyRequest(
        "dn: cn=test",
        "changetype: modify",
        "add: bothDifferentAdds",
        "bothDifferentAdds: toOnly1",
        "bothDifferentAdds: toOnly2",
        "-",
        "add: bothDifferentAddsAndDeletes",
        "bothDifferentAddsAndDeletes: toOnly",
        "-",
        "delete: bothDifferentAddsAndDeletes",
        "bothDifferentAddsAndDeletes: fromOnly",
        "-",
        "delete: bothDifferentDeletes",
        "bothDifferentDeletes: fromOnly1",
        "bothDifferentDeletes: fromOnly2",
        "-",
        "add: bothDifferentReplace",
        "bothDifferentReplace: toOnly1",
        "bothDifferentReplace: toOnly2",
        "-",
        "delete: bothDifferentReplace",
        "bothDifferentReplace: fromOnly1",
        "bothDifferentReplace: fromOnly2",
        "-",
        "delete: fromOnly",
        "fromOnly: fromOnlyValue",
        "-",
        "add: toOnly",
        "toOnly: toOnlyValue"
    );
    // From, to, diff.
    return new Object[][]
    {
      { from,  empty, diffFromEmpty },
      { empty, to,    diffEmptyTo   },
      { from,  to,    diffFromTo    }
    };
    // @formatter:on
  }
  /**
   * Tests {@link Entries#diffEntries(Entry, Entry)}.
   *
   * @param from
   *          Source entry.
   * @param to
   *          Destination entry.
   * @param expected
   *          Expected modifications.
   */
  @Test(dataProvider = "createTestDiffEntriesData")
  public void testDiffEntries(final Entry from, final Entry to,
      final ModifyRequest expected)
  {
    ModifyRequest actual = Entries.diffEntries(from, to);
    Assert.assertEquals(from.getName(), actual.getName());
    Assert.assertEquals(actual.getModifications().size(), expected
        .getModifications().size());
    Iterator<Modification> i1 = actual.getModifications().iterator();
    Iterator<Modification> i2 = expected.getModifications().iterator();
    while (i1.hasNext())
    {
      Modification m1 = i1.next();
      Modification m2 = i2.next();
      Assert.assertEquals(m1.getModificationType(), m2.getModificationType());
      Assert.assertEquals(m1.getAttribute(), m2.getAttribute());
    }
  }
}
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntryTestCase.java
@@ -36,7 +36,7 @@
/**
 * Test {@code BasicAttribute}.
 * Test {@code Entry}.
 */
public final class EntryTestCase extends SdkTestCase
{
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPListenerTestCase.java
New file
@@ -0,0 +1,776 @@
/*
 * 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 2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.opends.sdk.requests.*;
import org.opends.sdk.responses.*;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
 * Tests the LDAPListener class.
 */
public class LDAPListenerTestCase extends SdkTestCase
{
  private static class MockServerConnection implements
      ServerConnection<Integer>
  {
    volatile LDAPClientContext context = null;
    volatile boolean isConnected = false;
    final CountDownLatch isClosed = new CountDownLatch(1);
    MockServerConnection()
    {
      // Do nothing.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleAbandon(final Integer requestContext,
        final AbandonRequest request) throws UnsupportedOperationException
    {
      // Do nothing.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleAdd(final Integer requestContext,
        final AddRequest request,
        final ResultHandler<? super Result> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleBind(final Integer requestContext, final int version,
        final BindRequest request,
        final ResultHandler<? super BindResult> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler.handleResult(Responses.newBindResult(ResultCode.SUCCESS));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleCompare(final Integer requestContext,
        final CompareRequest request,
        final ResultHandler<? super CompareResult> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler
          .handleResult(Responses.newCompareResult(ResultCode.SUCCESS));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleConnectionClosed(final Integer requestContext,
        final UnbindRequest request)
    {
      isClosed.countDown();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleConnectionDisconnected(final ResultCode resultCode,
        final String message)
    {
      // Do nothing.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleConnectionError(final Throwable error)
    {
      // Do nothing.
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleDelete(final Integer requestContext,
        final DeleteRequest request,
        final ResultHandler<? super Result> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public <R extends ExtendedResult> void handleExtendedRequest(
        final Integer requestContext, final ExtendedRequest<R> request,
        final ResultHandler<? super R> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler
          .handleErrorResult(ErrorResultException.wrap(request
              .getResultDecoder().newExtendedErrorResult(
                  ResultCode.PROTOCOL_ERROR, "",
                  "Extended operation " + request.getOID() + " not supported")));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModify(final Integer requestContext,
        final ModifyRequest request,
        final ResultHandler<? super Result> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleModifyDN(final Integer requestContext,
        final ModifyDNRequest request,
        final ResultHandler<? super Result> resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void handleSearch(final Integer requestContext,
        final SearchRequest request, final SearchResultHandler resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
      resultHandler.handleResult(Responses.newResult(ResultCode.SUCCESS));
    }
  }
  private static class MockServerConnectionFactory implements
      ServerConnectionFactory<LDAPClientContext, Integer>
  {
    private final MockServerConnection serverConnection;
    private MockServerConnectionFactory(
        final MockServerConnection serverConnection)
    {
      this.serverConnection = serverConnection;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public ServerConnection<Integer> handleAccept(
        final LDAPClientContext clientContext) throws ErrorResultException
    {
      serverConnection.context = clientContext;
      serverConnection.isConnected = true;
      return serverConnection;
    }
  }
  /**
   * Tests basic LDAP listener functionality.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test
  public void testLDAPListenerBasic() throws Exception
  {
    final MockServerConnection serverConnection = new MockServerConnection();
    final MockServerConnectionFactory serverConnectionFactory = new MockServerConnectionFactory(
        serverConnection);
    final LDAPListener listener = new LDAPListener("localhost",
        TestCaseUtils.findFreePort(), serverConnectionFactory);
    try
    {
      // Connect and close.
      new LDAPConnectionFactory(listener.getSocketAddress()).getConnection()
          .close();
      Assert.assertTrue(serverConnection.isConnected);
      Assert.assertTrue(serverConnection.isClosed.await(10, TimeUnit.SECONDS));
    }
    finally
    {
      listener.close();
    }
  }
  /**
   * Tests LDAP listener which attempts to open a connection to a remote offline
   * server at the point when the listener accepts the client connection.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(enabled = false)
  public void testLDAPListenerLoadBalanceDuringHandleAccept() throws Exception
  {
    // Online server listener.
    final int onlineServerPort = TestCaseUtils.findFreePort();
    final MockServerConnection onlineServerConnection = new MockServerConnection();
    final MockServerConnectionFactory onlineServerConnectionFactory = new MockServerConnectionFactory(
        onlineServerConnection);
    final LDAPListener onlineServerListener = new LDAPListener("localhost",
        onlineServerPort, onlineServerConnectionFactory);
    try
    {
      // Connection pool and load balancing tests.
      final ConnectionFactory offlineServer1 = Connections
          .newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
              TestCaseUtils.findFreePort()), "offline1");
      final ConnectionFactory offlineServer2 = Connections
          .newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
              TestCaseUtils.findFreePort()), "offline2");
      final ConnectionFactory onlineServer = Connections
          .newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
              onlineServerPort), "online");
      // Round robin.
      final ConnectionFactory loadBalancer = Connections
          .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
              Connections.newConnectionPool(offlineServer1, 10),
              Connections.newConnectionPool(offlineServer2, 10),
              Connections.newConnectionPool(onlineServer, 10))));
      final MockServerConnection proxyServerConnection = new MockServerConnection();
      final MockServerConnectionFactory proxyServerConnectionFactory = new MockServerConnectionFactory(
          proxyServerConnection)
      {
        @Override
        public ServerConnection<Integer> handleAccept(
            final LDAPClientContext clientContext) throws ErrorResultException
        {
          // Get connection from load balancer, this should fail over twice
          // before getting connection to online server.
          try
          {
            loadBalancer.getConnection().close();
          }
          catch (final InterruptedException e)
          {
            // Unexpected.
            throw ErrorResultException.newErrorResult(ResultCode.OTHER,
                "Unexpected exception when connecting to online server", e);
          }
          return super.handleAccept(clientContext);
        }
      };
      final LDAPListener proxyListener = new LDAPListener("localhost",
          TestCaseUtils.findFreePort(), proxyServerConnectionFactory);
      try
      {
        // Connect and close.
        new LDAPConnectionFactory(proxyListener.getSocketAddress())
            .getConnection().close();
        // Wait for connect/close to complete.
        proxyServerConnection.isClosed.await();
        Assert.assertTrue(proxyServerConnection.isConnected);
        Assert.assertTrue(onlineServerConnection.isConnected);
      }
      finally
      {
        proxyListener.close();
      }
    }
    finally
    {
      onlineServerListener.close();
    }
  }
  /**
   * Tests LDAP listener which attempts to open a connection to a load balancing
   * pool at the point when the listener handles a bind request.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test
  public void testLDAPListenerLoadBalanceDuringHandleBind() throws Exception
  {
    // Online server listener.
    final int onlineServerPort = TestCaseUtils.findFreePort();
    final MockServerConnection onlineServerConnection = new MockServerConnection();
    final MockServerConnectionFactory onlineServerConnectionFactory = new MockServerConnectionFactory(
        onlineServerConnection);
    final LDAPListener onlineServerListener = new LDAPListener("localhost",
        onlineServerPort, onlineServerConnectionFactory);
    try
    {
      // Connection pool and load balancing tests.
      final ConnectionFactory offlineServer1 = Connections
          .newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
              TestCaseUtils.findFreePort()), "offline1");
      final ConnectionFactory offlineServer2 = Connections
          .newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
              TestCaseUtils.findFreePort()), "offline2");
      final ConnectionFactory onlineServer = Connections
          .newNamedConnectionFactory(new LDAPConnectionFactory("localhost",
              onlineServerPort), "online");
      // Round robin.
      final ConnectionFactory loadBalancer = Connections
          .newLoadBalancer(new RoundRobinLoadBalancingAlgorithm(Arrays.asList(
              Connections.newConnectionPool(offlineServer1, 10),
              Connections.newConnectionPool(offlineServer2, 10),
              Connections.newConnectionPool(onlineServer, 10))));
      final MockServerConnection proxyServerConnection = new MockServerConnection()
      {
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleBind(final Integer requestContext, final int version,
            final BindRequest request,
            final ResultHandler<? super BindResult> resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler)
            throws UnsupportedOperationException
        {
          // Get connection from load balancer, this should fail over twice
          // before getting connection to online server.
          try
          {
            loadBalancer.getConnection().close();
            resultHandler.handleResult(Responses
                .newBindResult(ResultCode.SUCCESS));
          }
          catch (final Exception e)
          {
            // Unexpected.
            resultHandler
                .handleErrorResult(ErrorResultException.newErrorResult(
                    ResultCode.OTHER,
                    "Unexpected exception when connecting to load balancer", e));
          }
        }
      };
      final MockServerConnectionFactory proxyServerConnectionFactory = new MockServerConnectionFactory(
          proxyServerConnection);
      final LDAPListener proxyListener = new LDAPListener("localhost",
          TestCaseUtils.findFreePort(), proxyServerConnectionFactory);
      try
      {
        // Connect, bind, and close.
        final Connection connection = new LDAPConnectionFactory(
            proxyListener.getSocketAddress()).getConnection();
        try
        {
          connection.bind("cn=test", "password");
        }
        finally
        {
          connection.close();
        }
        // Wait for connect/close to complete.
        proxyServerConnection.isClosed.await();
        Assert.assertTrue(proxyServerConnection.isConnected);
        Assert.assertTrue(onlineServerConnection.isConnected);
      }
      finally
      {
        proxyListener.close();
      }
    }
    finally
    {
      onlineServerListener.close();
    }
  }
  /**
   * Tests LDAP listener which attempts to open a connection to a remote offline
   * server at the point when the listener accepts the client connection.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test(enabled = false)
  public void testLDAPListenerProxyDuringHandleAccept() throws Exception
  {
    final MockServerConnection onlineServerConnection = new MockServerConnection();
    final MockServerConnectionFactory onlineServerConnectionFactory = new MockServerConnectionFactory(
        onlineServerConnection);
    final LDAPListener onlineServerListener = new LDAPListener("localhost",
        TestCaseUtils.findFreePort(), onlineServerConnectionFactory);
    try
    {
      final int offlineServerPort = TestCaseUtils.findFreePort();
      final MockServerConnection proxyServerConnection = new MockServerConnection();
      final MockServerConnectionFactory proxyServerConnectionFactory = new MockServerConnectionFactory(
          proxyServerConnection)
      {
        @Override
        public ServerConnection<Integer> handleAccept(
            final LDAPClientContext clientContext) throws ErrorResultException
        {
          // First attempt offline server.
          LDAPConnectionFactory lcf = new LDAPConnectionFactory("localhost",
              offlineServerPort);
          try
          {
            // This is expected to fail.
            lcf.getConnection().close();
            throw ErrorResultException.newErrorResult(ResultCode.OTHER,
                "Connection to offline server succeeded unexpectedly");
          }
          catch (final ConnectionException ce)
          {
            // This is expected - so go to online server.
            try
            {
              lcf = new LDAPConnectionFactory(
                  onlineServerListener.getSocketAddress());
              lcf.getConnection().close();
            }
            catch (final Exception e)
            {
              // Unexpected.
              throw ErrorResultException.newErrorResult(ResultCode.OTHER,
                  "Unexpected exception when connecting to online server", e);
            }
          }
          catch (final Exception e)
          {
            // Unexpected.
            throw ErrorResultException.newErrorResult(ResultCode.OTHER,
                "Unexpected exception when connecting to offline server", e);
          }
          return super.handleAccept(clientContext);
        }
      };
      final LDAPListener proxyListener = new LDAPListener("localhost",
          TestCaseUtils.findFreePort(), proxyServerConnectionFactory);
      try
      {
        // Connect and close.
        new LDAPConnectionFactory(proxyListener.getSocketAddress())
            .getConnection().close();
        // Wait for connect/close to complete.
        proxyServerConnection.isClosed.await();
        Assert.assertTrue(proxyServerConnection.isConnected);
        Assert.assertTrue(onlineServerConnection.isConnected);
      }
      finally
      {
        proxyListener.close();
      }
    }
    finally
    {
      onlineServerListener.close();
    }
  }
  /**
   * Tests LDAP listener which attempts to open a connection to a remote offline
   * server at the point when the listener handles a bind request.
   *
   * @throws Exception
   *           If an unexpected exception occurred.
   */
  @Test
  public void testLDAPListenerProxyDuringHandleBind() throws Exception
  {
    final MockServerConnection onlineServerConnection = new MockServerConnection();
    final MockServerConnectionFactory onlineServerConnectionFactory = new MockServerConnectionFactory(
        onlineServerConnection);
    final LDAPListener onlineServerListener = new LDAPListener("localhost",
        TestCaseUtils.findFreePort(), onlineServerConnectionFactory);
    try
    {
      final int offlineServerPort = TestCaseUtils.findFreePort();
      final MockServerConnection proxyServerConnection = new MockServerConnection()
      {
        /**
         * {@inheritDoc}
         */
        @Override
        public void handleBind(final Integer requestContext, final int version,
            final BindRequest request,
            final ResultHandler<? super BindResult> resultHandler,
            final IntermediateResponseHandler intermediateResponseHandler)
            throws UnsupportedOperationException
        {
          // First attempt offline server.
          LDAPConnectionFactory lcf = new LDAPConnectionFactory("localhost",
              offlineServerPort);
          try
          {
            // This is expected to fail.
            lcf.getConnection().close();
            resultHandler.handleErrorResult(ErrorResultException
                .newErrorResult(ResultCode.OTHER,
                    "Connection to offline server succeeded unexpectedly"));
          }
          catch (final ConnectionException ce)
          {
            // This is expected - so go to online server.
            try
            {
              lcf = new LDAPConnectionFactory(
                  onlineServerListener.getSocketAddress());
              lcf.getConnection().close();
              resultHandler.handleResult(Responses
                  .newBindResult(ResultCode.SUCCESS));
            }
            catch (final Exception e)
            {
              // Unexpected.
              resultHandler.handleErrorResult(ErrorResultException
                  .newErrorResult(ResultCode.OTHER,
                      "Unexpected exception when connecting to online server",
                      e));
            }
          }
          catch (final Exception e)
          {
            // Unexpected.
            resultHandler
                .handleErrorResult(ErrorResultException
                    .newErrorResult(
                        ResultCode.OTHER,
                        "Unexpected exception when connecting to offline server",
                        e));
          }
        }
      };
      final MockServerConnectionFactory proxyServerConnectionFactory = new MockServerConnectionFactory(
          proxyServerConnection);
      final LDAPListener proxyListener = new LDAPListener("localhost",
          TestCaseUtils.findFreePort(), proxyServerConnectionFactory);
      try
      {
        // Connect, bind, and close.
        final Connection connection = new LDAPConnectionFactory(
            proxyListener.getSocketAddress()).getConnection();
        try
        {
          connection.bind("cn=test", "password");
        }
        finally
        {
          connection.close();
        }
        // Wait for connect/close to complete.
        proxyServerConnection.isClosed.await();
        Assert.assertTrue(proxyServerConnection.isConnected);
        Assert.assertTrue(onlineServerConnection.isConnected);
      }
      finally
      {
        proxyListener.close();
      }
    }
    finally
    {
      onlineServerListener.close();
    }
  }
  /**
   * Tests server-side disconnection.
   *
   * @throws Exception
   *           If an unexpected error occurred.
   */
  @Test
  public void testServerDisconnect() throws Exception
  {
    final MockServerConnection onlineServerConnection = new MockServerConnection();
    final MockServerConnectionFactory onlineServerConnectionFactory = new MockServerConnectionFactory(
        onlineServerConnection);
    final LDAPListener onlineServerListener = new LDAPListener("localhost",
        TestCaseUtils.findFreePort(), onlineServerConnectionFactory);
    final Connection connection;
    try
    {
      // Connect and bind.
      connection = new LDAPConnectionFactory(
          onlineServerListener.getSocketAddress()).getConnection();
      try
      {
        connection.bind("cn=test", "password");
      }
      catch (ErrorResultException e)
      {
        connection.close();
        throw e;
      }
    }
    finally
    {
      onlineServerConnection.context.disconnect();
      onlineServerListener.close();
    }
    try
    {
      // Connect and bind.
      final Connection failedConnection = new LDAPConnectionFactory(
          onlineServerListener.getSocketAddress()).getConnection();
      failedConnection.close();
      connection.close();
      Assert
          .fail("Connection attempt to closed listener succeeded unexpectedly");
    }
    catch (ConnectionException e)
    {
      // Expected.
    }
    try
    {
      connection.bind("cn=test", "password");
      Assert.fail("Bind attempt on closed connection succeeded unexpectedly");
    }
    catch (ErrorResultException e)
    {
      // Expected.
      Assert.assertFalse(connection.isValid());
      Assert.assertFalse(connection.isClosed());
    }
    finally
    {
      connection.close();
      Assert.assertFalse(connection.isValid());
      Assert.assertTrue(connection.isClosed());
    }
  }
}
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java
@@ -47,8 +47,8 @@
import org.opends.sdk.requests.*;
import org.opends.sdk.responses.*;
import com.sun.grizzly.TransportFactory;
import com.sun.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.TransportFactory;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import com.sun.opends.sdk.controls.AccountUsabilityRequestControl;
import com.sun.opends.sdk.controls.AccountUsabilityResponseControl;
import com.sun.opends.sdk.ldap.GrizzlyLDAPListenerOptions;
@@ -138,7 +138,8 @@
  private class LDAPServerConnection implements ServerConnection<Integer>
  private class LDAPServerConnection implements
      ServerConnection<Integer>
  {
    private final LDAPClientContext clientContext;
@@ -164,7 +165,8 @@
        final AbandonRequest request) throws UnsupportedOperationException
    {
      // Check if we have any concurrent operation with this message id.
      final AbandonableRequest req = requestsInProgress.get(context);
      final AbandonableRequest req = requestsInProgress.get(request
          .getRequestID());
      if (req == null)
      {
        // Nothing to do here.
@@ -186,8 +188,8 @@
     * @param intermediateResponseHandler
     * @throws UnsupportedOperationException
     */
    public void handleAdd(final Integer context, final AddRequest request,
        final ResultHandler<? super Result> handler,
    public void handleAdd(final Integer context,
        final AddRequest request, final ResultHandler<? super Result> handler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
@@ -399,12 +401,18 @@
    /**
     * @param context
     * @param request
     * {@inheritDoc}
     */
    public void handleConnectionClosed(final Integer context,
        final UnbindRequest request)
    {
      close();
    }
    private void close()
    {
      if (saslServer != null)
      {
        try
@@ -421,11 +429,22 @@
    /**
     * @param error
     * {@inheritDoc}
     */
    public void handleConnectionException(final Throwable error)
    public void handleConnectionDisconnected(ResultCode resultCode,
        String message)
    {
      close();
    }
    /**
     * {@inheritDoc}
     */
    public void handleConnectionError(final Throwable error)
    {
      close();
    }
@@ -549,7 +568,7 @@
    {
      if (request.getOID().equals(StartTLSExtendedRequest.OID))
      {
        final R result = request.getResultDecoder().adaptExtendedErrorResult(
        final R result = request.getResultDecoder().newExtendedErrorResult(
            ResultCode.SUCCESS, "", "");
        resultHandler.handleResult(result);
        clientContext.startTLS(sslContext, null, sslContext.getSocketFactory()
@@ -599,14 +618,11 @@
     * @param context
     * @param request
     * @param resultHandler
     * @param searchResulthandler
     * @param intermediateResponseHandler
     * @throws UnsupportedOperationException
     */
    public void handleSearch(final Integer context,
        final SearchRequest request,
        final ResultHandler<? super Result> resultHandler,
        final SearchResultHandler searchResulthandler,
        final SearchRequest request, final SearchResultHandler resultHandler,
        final IntermediateResponseHandler intermediateResponseHandler)
        throws UnsupportedOperationException
    {
@@ -648,7 +664,7 @@
              false, 10, false, 0));
        }
      }
      searchResulthandler.handleEntry(e);
      resultHandler.handleEntry(e);
      result = Responses.newResult(ResultCode.SUCCESS);
      resultHandler.handleResult(result);
      requestsInProgress.remove(context);
@@ -707,7 +723,7 @@
   * @param context
   * @return
   */
  public ServerConnection<Integer> accept(final LDAPClientContext context)
  public ServerConnection<Integer> handleAccept(final LDAPClientContext context)
  {
    return new LDAPServerConnection(context);
  }
@@ -759,14 +775,7 @@
    {
      return;
    }
    try
    {
      listener.close();
    }
    catch (final IOException e)
    {
      e.printStackTrace();
    }
    listener.close();
    try
    {
      transport.stop();
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/LinkedAttributeTestCase.java
@@ -29,6 +29,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.opends.sdk.schema.Schema;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -66,4 +71,416 @@
    Assert.assertTrue(attribute.remove(ByteString.valueOf("another value")));
    Assert.assertEquals(attribute.size(), 0);
  }
  @Test
  public void testAdd()
  {
    Attribute a = new LinkedAttribute("test");
    Assert.assertTrue(a.add(ByteString.valueOf("value1")));
    Assert.assertFalse(a.add(ByteString.valueOf("value1")));
    Assert.assertTrue(a.add(ByteString.valueOf("value2")));
    Assert.assertFalse(a.add(ByteString.valueOf("value2")));
    Assert.assertTrue(a.add(ByteString.valueOf("value3")));
    Assert.assertFalse(a.add(ByteString.valueOf("value3")));
    Assert.assertEquals(a.size(), 3);
    Iterator<ByteString> i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value3"));
    Assert.assertFalse(i.hasNext());
  }
  @Test
  public void testAddAll()
  {
    // addAll to an empty attribute.
    Attribute a = new LinkedAttribute("test");
    Assert.assertFalse(a.addAll(Collections.<ByteString> emptyList(), null));
    Iterator<ByteString> i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test");
    Assert.assertTrue(a.addAll(Arrays.asList(ByteString.valueOf("value1")),
        null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test");
    Assert.assertTrue(a.addAll(
        Arrays.asList(ByteString.valueOf("value1"),
            ByteString.valueOf("value2")), null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertFalse(i.hasNext());
    // addAll to a single-valued attribute.
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertFalse(a.addAll(Collections.<ByteString> emptyList(), null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertTrue(a.addAll(Arrays.asList(ByteString.valueOf("value2")),
        null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertTrue(a.addAll(
        Arrays.asList(ByteString.valueOf("value2"),
            ByteString.valueOf("value3")), null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value3"));
    Assert.assertFalse(i.hasNext());
    // addAll to a multi-valued attribute.
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"));
    Assert.assertFalse(a.addAll(Collections.<ByteString> emptyList(), null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"));
    Assert.assertTrue(a.addAll(Arrays.asList(ByteString.valueOf("value3")),
        null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value3"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"));
    Assert.assertTrue(a.addAll(
        Arrays.asList(ByteString.valueOf("value3"),
            ByteString.valueOf("value4")), null));
    i = a.iterator();
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value3"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value4"));
    Assert.assertFalse(i.hasNext());
  }
  @Test
  public void testClear()
  {
    Attribute a = new LinkedAttribute("test");
    Assert.assertTrue(a.isEmpty());
    Assert.assertEquals(a.size(), 0);
    a.clear();
    Assert.assertTrue(a.isEmpty());
    Assert.assertEquals(a.size(), 0);
    a.add(ByteString.valueOf("value1"));
    Assert.assertFalse(a.isEmpty());
    Assert.assertEquals(a.size(), 1);
    a.clear();
    Assert.assertTrue(a.isEmpty());
    Assert.assertEquals(a.size(), 0);
    a.add(ByteString.valueOf("value1"));
    a.add(ByteString.valueOf("value2"));
    Assert.assertFalse(a.isEmpty());
    Assert.assertEquals(a.size(), 2);
    a.clear();
    Assert.assertTrue(a.isEmpty());
    Assert.assertEquals(a.size(), 0);
    a.add(ByteString.valueOf("value1"));
    a.add(ByteString.valueOf("value2"));
    a.add(ByteString.valueOf("value3"));
    Assert.assertFalse(a.isEmpty());
    Assert.assertEquals(a.size(), 3);
    a.clear();
    Assert.assertTrue(a.isEmpty());
    Assert.assertEquals(a.size(), 0);
  }
  @Test
  public void testContains()
  {
    Attribute a = new LinkedAttribute("test");
    Assert.assertFalse(a.contains(ByteString.valueOf("value4")));
    a.add(ByteString.valueOf("value1"));
    Assert.assertTrue(a.contains(ByteString.valueOf("value1")));
    Assert.assertFalse(a.contains(ByteString.valueOf("value4")));
    a.add(ByteString.valueOf("value2"));
    Assert.assertTrue(a.contains(ByteString.valueOf("value1")));
    Assert.assertTrue(a.contains(ByteString.valueOf("value2")));
    Assert.assertFalse(a.contains(ByteString.valueOf("value4")));
    a.add(ByteString.valueOf("value3"));
    Assert.assertTrue(a.contains(ByteString.valueOf("value1")));
    Assert.assertTrue(a.contains(ByteString.valueOf("value2")));
    Assert.assertTrue(a.contains(ByteString.valueOf("value3")));
    Assert.assertFalse(a.contains(ByteString.valueOf("value4")));
  }
  @Test
  public void testContainsAll()
  {
    Attribute a = new LinkedAttribute("test");
    Assert.assertTrue(a.containsAll(Collections.<ByteString> emptyList()));
    Assert
        .assertFalse(a.containsAll(Arrays.asList(ByteString.valueOf("value1"))));
    Assert.assertFalse(a.containsAll(Arrays.asList(
        ByteString.valueOf("value1"), ByteString.valueOf("value2"))));
    Assert.assertFalse(a.containsAll(Arrays.asList(
        ByteString.valueOf("value1"), ByteString.valueOf("value2"),
        ByteString.valueOf("value3"))));
    a.add(ByteString.valueOf("value1"));
    Assert.assertTrue(a.containsAll(Collections.<ByteString> emptyList()));
    Assert
        .assertTrue(a.containsAll(Arrays.asList(ByteString.valueOf("value1"))));
    Assert.assertFalse(a.containsAll(Arrays.asList(
        ByteString.valueOf("value1"), ByteString.valueOf("value2"))));
    Assert.assertFalse(a.containsAll(Arrays.asList(
        ByteString.valueOf("value1"), ByteString.valueOf("value2"),
        ByteString.valueOf("value3"))));
    a.add(ByteString.valueOf("value2"));
    Assert.assertTrue(a.containsAll(Collections.<ByteString> emptyList()));
    Assert
        .assertTrue(a.containsAll(Arrays.asList(ByteString.valueOf("value1"))));
    Assert.assertTrue(a.containsAll(Arrays.asList(ByteString.valueOf("value1"),
        ByteString.valueOf("value2"))));
    Assert.assertFalse(a.containsAll(Arrays.asList(
        ByteString.valueOf("value1"), ByteString.valueOf("value2"),
        ByteString.valueOf("value3"))));
  }
  @Test
  public void testFirstValue()
  {
    Attribute a = new LinkedAttribute("test");
    try
    {
      a.firstValue();
      Assert.fail("Expected NoSuchElementException");
    }
    catch (NoSuchElementException e)
    {
      // Expected.
    }
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertEquals(a.firstValue(), ByteString.valueOf("value1"));
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"));
    Assert.assertEquals(a.firstValue(), ByteString.valueOf("value1"));
    a = new LinkedAttribute("test", ByteString.valueOf("value2"),
        ByteString.valueOf("value1"));
    Assert.assertEquals(a.firstValue(), ByteString.valueOf("value2"));
  }
  @Test
  public void testGetAttributeDescription()
  {
    AttributeDescription ad = AttributeDescription.valueOf("test");
    Attribute a = new LinkedAttribute(ad);
    Assert.assertEquals(a.getAttributeDescription(), ad);
  }
  @Test
  public void testIterator()
  {
    Attribute a = new LinkedAttribute("test");
    Iterator<ByteString> i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertFalse(i.hasNext());
  }
  @Test
  public void testRemove()
  {
    Attribute a = new LinkedAttribute("test");
    Assert.assertFalse(a.remove(ByteString.valueOf("value1")));
    Iterator<ByteString> i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertFalse(a.remove(ByteString.valueOf("value2")));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertFalse(i.hasNext());
    Assert.assertTrue(a.remove(ByteString.valueOf("value1")));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"));
    Assert.assertFalse(a.remove(ByteString.valueOf("value3")));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertFalse(i.hasNext());
    Assert.assertTrue(a.remove(ByteString.valueOf("value1")));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertFalse(i.hasNext());
    Assert.assertTrue(a.remove(ByteString.valueOf("value2")));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
  }
  @Test
  public void testRemoveAll()
  {
    // removeAll from an empty attribute.
    Attribute a = new LinkedAttribute("test");
    Assert.assertFalse(a.removeAll(Collections.<ByteString> emptyList(), null));
    Iterator<ByteString> i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test");
    Assert.assertFalse(a.removeAll(Arrays.asList(ByteString.valueOf("value1")),
        null));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test");
    Assert.assertFalse(a.removeAll(Arrays.asList(ByteString.valueOf("value1"),
        ByteString.valueOf("value2"))));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
    // removeAll from single-valued attribute.
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertFalse(a.removeAll(Collections.<ByteString> emptyList(), null));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertTrue(a.removeAll(Arrays.asList(ByteString.valueOf("value1")),
        null));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"));
    Assert.assertTrue(a.removeAll(Arrays.asList(ByteString.valueOf("value1"),
        ByteString.valueOf("value2"))));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
    // removeAll from multi-valued attribute.
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4"));
    Assert.assertFalse(a.removeAll(Collections.<ByteString> emptyList(), null));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value1"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value3"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value4"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4"));
    Assert.assertTrue(a.removeAll(Arrays.asList(ByteString.valueOf("value1")),
        null));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value2"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value3"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value4"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4"));
    Assert.assertTrue(a.removeAll(
        Arrays.asList(ByteString.valueOf("value1"),
            ByteString.valueOf("value2")), null));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value3"));
    Assert.assertEquals(i.next(), ByteString.valueOf("value4"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4"));
    Assert.assertTrue(a.removeAll(
        Arrays.asList(ByteString.valueOf("value1"),
            ByteString.valueOf("value2"), ByteString.valueOf("value3")), null));
    i = a.iterator();
    Assert.assertTrue(i.hasNext());
    Assert.assertEquals(i.next(), ByteString.valueOf("value4"));
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4"));
    Assert.assertTrue(a.removeAll(Arrays.asList(ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4")), null));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
    a = new LinkedAttribute("test", ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4"));
    Assert.assertTrue(a.removeAll(Arrays.asList(ByteString.valueOf("value1"),
        ByteString.valueOf("value2"), ByteString.valueOf("value3"),
        ByteString.valueOf("value4"), ByteString.valueOf("value5")), null));
    i = a.iterator();
    Assert.assertFalse(i.hasNext());
  }
}
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/TestCaseUtils.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk;
@@ -31,6 +31,9 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
@@ -47,10 +50,10 @@
  public static final String PROPERTY_LDAP_PORT = "org.opends.server.LdapPort";
  /**
   * Port number that's used by the server. Need to be used by the testcases to
   * Port number that's used by the server. Need to be used by the test cases to
   * create connections.
   */
  public static int port = 11389;
  public static int port;
  static
  {
@@ -59,6 +62,10 @@
    {
      port = Integer.valueOf(ldapPort);
    }
    else
    {
      port = findFreePort();
    }
  }
@@ -67,6 +74,8 @@
   * Creates a temporary text file with the specified contents. It will be
   * marked for automatic deletion when the JVM exits.
   *
   * @param lines
   *          The file contents.
   * @return The absolute path to the file that was created.
   * @throws Exception
   *           If an unexpected problem occurs.
@@ -90,6 +99,31 @@
  /**
   * Finds a free server socket port on the local host.
   *
   * @return The free port.
   */
  public static int findFreePort()
  {
    int port;
    try
    {
      ServerSocket serverLdapSocket = new ServerSocket();
      serverLdapSocket.setReuseAddress(true);
      serverLdapSocket.bind(new InetSocketAddress("127.0.0.1", 0));
      port = serverLdapSocket.getLocalPort();
      serverLdapSocket.close();
    }
    catch (IOException e)
    {
      throw new RuntimeException(e);
    }
    return port;
  }
  /**
   * Returns an internal client connection to the running ldap server.
   *
   * @return The internal client connection.
@@ -122,10 +156,10 @@
   * Starts the test ldap server.
   *
   * @throws Exception
   *           If an error occurs when starting the server.
   */
  public static void startServer() throws Exception
  {
    // TODO:Try a couple of random ports before throwing exception.
    LDAPServer.getInstance().start(port);
  }
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/DigestMD5SASLBindRequestTestCase.java
@@ -35,7 +35,6 @@
import java.util.Arrays;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertEquals;
@@ -76,37 +75,29 @@
  @Test(dataProvider = "DigestMD5SASLBindRequests")
  public void testQOP(DigestMD5SASLBindRequest request) throws Exception
  {
    DigestMD5SASLBindRequest.QOPOption [] options =
        new DigestMD5SASLBindRequest.QOPOption[]{
            DigestMD5SASLBindRequest.QOPOption.AUTH,
            DigestMD5SASLBindRequest.QOPOption.AUTH_INT,
            DigestMD5SASLBindRequest.QOPOption.AUTH_CONF};
    request.setQOP(options);
    DigestMD5SASLBindRequest.QOPOption [] results = request.getQOP();
    for(int i = 0; i < options.length; i++)
    {
      assertEquals(options[i], results[i]);
    }
    String[] options = new String[] {
        DigestMD5SASLBindRequest.QOP_AUTH,
        DigestMD5SASLBindRequest.QOP_AUTH_INT,
        DigestMD5SASLBindRequest.QOP_AUTH_CONF };
    request.addQOP(options);
    assertEquals(request.getQOPs(), Arrays.asList(options));
  }
  @Test(dataProvider = "DigestMD5SASLBindRequests" )
  public void testStrength(DigestMD5SASLBindRequest request) throws Exception
  {
    DigestMD5SASLBindRequest.CipherOption[] options =
        new DigestMD5SASLBindRequest.CipherOption[]{
            DigestMD5SASLBindRequest.CipherOption.RC4_40,
            DigestMD5SASLBindRequest.CipherOption.TRIPLE_DES_RC4,
            DigestMD5SASLBindRequest.CipherOption.DES_RC4_56};
    request.setCipher(options);
    assertTrue(Arrays.deepEquals(options, request.getCipher()));
    request.setCipher(DigestMD5SASLBindRequest.CIPHER_3DES);
    assertEquals(request.getCipher(), DigestMD5SASLBindRequest.CIPHER_3DES);
    request.setCipher(DigestMD5SASLBindRequest.CIPHER_MEDIUM);
    assertEquals(request.getCipher(), DigestMD5SASLBindRequest.CIPHER_MEDIUM);
  }
  @Test(dataProvider = "DigestMD5SASLBindRequests")
  public void testServerAuth(DigestMD5SASLBindRequest request) throws Exception
  {
    request.setServerAuth(true);
    assertEquals(request.getServerAuth(), true);
    assertEquals(request.isServerAuth(), true);
  }
  @Test(dataProvider = "DigestMD5SASLBindRequests")
opendj-sdk/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/GSSAPISASLBindRequestTestCase.java
@@ -36,7 +36,6 @@
import java.util.Arrays;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
 * Tests GSSAPI SASL Bind requests.
@@ -81,20 +80,19 @@
  @Test(dataProvider = "GSSAPISASLBindRequests")
  public void testQOP(GSSAPISASLBindRequest request) throws Exception
  {
    GSSAPISASLBindRequest.QOPOption [] options =
        new GSSAPISASLBindRequest.QOPOption[]{
            GSSAPISASLBindRequest.QOPOption.AUTH,
            GSSAPISASLBindRequest.QOPOption.AUTH_INT,
            GSSAPISASLBindRequest.QOPOption.AUTH_CONF};
    request.setQOP(options);
    assertTrue(Arrays.deepEquals(options, request.getQOP()));
    String[] options = new String[] {
        GSSAPISASLBindRequest.QOP_AUTH,
        GSSAPISASLBindRequest.QOP_AUTH_INT,
        GSSAPISASLBindRequest.QOP_AUTH_CONF };
    request.addQOP(options);
    assertEquals(request.getQOPs(), Arrays.asList(options));
  }
  @Test(dataProvider = "GSSAPISASLBindRequests")
  public void testServerAuth(GSSAPISASLBindRequest request) throws Exception
  {
    request.setServerAuth(true);
    assertEquals(request.getServerAuth(), true);
    assertEquals(request.isServerAuth(), true);
  }
  @Test(dataProvider = "GSSAPISASLBindRequests")