From 72650d4cc41c64136d064967d7fec3726d850fee Mon Sep 17 00:00:00 2001
From: Ludovic Poitou <ludovic.poitou@forgerock.com>
Date: Thu, 14 Oct 2010 11:52:28 +0000
Subject: [PATCH] Multiple enhancements and bug fixes to the SDK (update from OpenDS by matthew_swift):
---
sdk/src/org/opends/sdk/requests/SearchRequestImpl.java | 10
sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java | 118
sdk/src/org/opends/sdk/AbstractLoadBalancingAlgorithm.java | 428 ++
sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java | 10
sdk/src/com/sun/opends/sdk/util/AsynchronousFutureResult.java | 483 ++
sdk/examples/org/opends/sdk/examples/server/proxy/package-info.java | 34
sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java | 44
sdk/examples/org/opends/sdk/examples/client/asyncsearch/Main.java | 322 +
sdk/src/org/opends/sdk/AbstractConnection.java | 10
sdk/src/org/opends/sdk/LDAPConnectionFactory.java | 92
sdk/src/org/opends/sdk/ServerConnectionFactory.java | 17
sdk/examples/org/opends/sdk/examples/server/store/Main.java | 589 +++
sdk/src/com/sun/opends/sdk/ldap/AbstractLDAPFutureResultImpl.java | 27
sdk/src/org/opends/sdk/SynchronousConnection.java | 23
sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java | 48
sdk/src/org/opends/sdk/responses/ExtendedResultDecoder.java | 75
sdk/src/com/sun/opends/sdk/ldap/SASLDecoderTransformer.java | 10
sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java | 7
sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPListenerOptions.java | 2
sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferWriterTestCase.java | 4
sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferReaderTestCase.java | 4
sdk/src/org/opends/sdk/LDAPClientContext.java | 64
sdk/src/org/opends/sdk/requests/GenericExtendedRequestImpl.java | 18
sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java | 28
sdk/examples/org/opends/sdk/examples/client/asyncsearch/package-info.java | 35
sdk/src/com/sun/opends/sdk/ldap/ASN1BufferWriter.java | 8
sdk/nbproject/ide-targets.xml | 38
sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/DigestMD5SASLBindRequestTestCase.java | 33
sdk/src/com/sun/opends/sdk/ldap/SASLEncoderTransformer.java | 10
sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPOptions.java | 2
sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequest.java | 439 +
sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java | 69
sdk/src/org/opends/sdk/controls/package-info.java | 4
sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequest.java | 277 +
sdk/src/com/sun/opends/sdk/ldap/LDAPCompareFutureResultImpl.java | 9
sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java | 148
sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java | 8
sdk/src/com/sun/opends/sdk/extensions/PasswordPolicyStateExtendedRequest.java | 7
sdk/src/org/opends/sdk/requests/CancelExtendedRequestImpl.java | 27
sdk/tests/unit-tests-testng/src/org/opends/sdk/TestCaseUtils.java | 42
sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPListenerTestCase.java | 776 ++++
sdk/src/org/opends/sdk/requests/Requests.java | 54
sdk/tests/unit-tests-testng/src/org/opends/sdk/EntriesTestCase.java | 229 +
sdk/src/org/opends/sdk/requests/CancelExtendedRequest.java | 20
sdk/lib/gmbal-api-only.jar | 0
sdk/src/org/opends/sdk/requests/SearchRequest.java | 16
sdk/src/org/opends/sdk/package-info.java | 5
sdk/nbproject/ide-file-targets.xml | 218 +
sdk/examples/org/opends/sdk/examples/client/search/Main.java | 187 +
sdk/examples/org/opends/sdk/examples/server/proxy/Main.java | 515 +++
sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java | 14
sdk/src/overview.html | 103
sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java | 293 -
sdk/src/org/opends/sdk/Entry.java | 2
sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java | 272 -
sdk/nbproject/genfiles.properties | 5
sdk/examples/org/opends/sdk/examples/server/store/package-info.java | 35
sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java | 20
sdk/examples/org/opends/sdk/examples/client/search/package-info.java | 34
sdk/src/org/opends/sdk/ServerConnection.java | 43
sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/GSSAPISASLBindRequestTestCase.java | 16
sdk/src/com/sun/opends/sdk/ldap/LDAPWriter.java | 4
sdk/src/org/opends/sdk/AbstractConnectionFactory.java | 7
sdk/lib/grizzly.jar | 0
sdk/src/org/opends/sdk/requests/PasswordModifyExtendedRequestImpl.java | 11
sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java | 38
sdk/examples/org/opends/sdk/examples/client/modify/Main.java | 166
sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java | 34
sdk/src/org/opends/sdk/Entries.java | 182 +
sdk/src/org/opends/sdk/LoadBalancer.java | 100
sdk/nbproject/project.xml | 283 +
sdk/examples/org/opends/sdk/examples/client/modify/package-info.java | 36
sdk/src/org/opends/sdk/ErrorResultException.java | 55
sdk/src/com/sun/opends/sdk/extensions/GetConnectionIDExtendedRequest.java | 7
sdk/src/org/opends/sdk/LinkedAttribute.java | 235
sdk/src/org/opends/sdk/requests/WhoAmIExtendedRequestImpl.java | 11
sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java | 16
sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java | 16
sdk/tests/unit-tests-testng/src/org/opends/sdk/EntryTestCase.java | 2
sdk/build.xml | 36
sdk/src/org/opends/sdk/requests/AbandonRequest.java | 16
sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java | 4
sdk/src/org/opends/sdk/LoadBalancingAlgorithm.java | 12
sdk/src/org/opends/sdk/LDAPListener.java | 234
sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java | 7
sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/GlobalTransportFactoryTestCase.java | 4
sdk/src/com/sun/opends/sdk/ldap/LDAPListenerImpl.java | 64
sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java | 462 +-
sdk/src/org/opends/sdk/requests/Request.java | 4
sdk/src/com/sun/opends/sdk/ldap/ASN1BufferReader.java | 6
sdk/tests/unit-tests-testng/src/org/opends/sdk/DNTestCase.java | 15
sdk/resource/example-2000.ldif.zip | 0
sdk/src/org/opends/sdk/requests/AbandonRequestImpl.java | 22
sdk/tests/unit-tests-testng/src/org/opends/sdk/LinkedAttributeTestCase.java | 417 ++
sdk/src/org/opends/sdk/RoundRobinLoadBalancingAlgorithm.java | 178 +
sdk/src/com/sun/opends/sdk/extensions/GetSymmetricKeyExtendedRequest.java | 7
sdk/src/org/opends/sdk/Connections.java | 160
sdk/src/org/opends/sdk/responses/AbstractExtendedResultDecoder.java | 137
sdk/src/com/sun/opends/sdk/ldap/SASLFilter.java | 6
sdk/src/org/opends/sdk/ConnectionPool.java | 47
sdk/src/com/sun/opends/sdk/ldap/TimeoutChecker.java | 2
sdk/src/com/sun/opends/sdk/util/StaticUtils.java | 108
sdk/src/org/opends/sdk/InternalConnectionFactory.java | 34
sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequestImpl.java | 433 +-
sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java | 59
105 files changed, 8,238 insertions(+), 1,919 deletions(-)
diff --git a/sdk/build.xml b/sdk/build.xml
index 97fd7c8..ad5c080 100755
--- a/sdk/build.xml
+++ b/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"
diff --git a/sdk/examples/org/opends/sdk/examples/client/asyncsearch/Main.java b/sdk/examples/org/opends/sdk/examples/client/asyncsearch/Main.java
new file mode 100644
index 0000000..00b3f33
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/client/asyncsearch/Main.java
@@ -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>
+ * <host> <port> <username> <password>
+ * <baseDN> <scope> <filter> [<attibute> <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.
+ }
+}
diff --git a/sdk/examples/org/opends/sdk/examples/client/asyncsearch/package-info.java b/sdk/examples/org/opends/sdk/examples/client/asyncsearch/package-info.java
new file mode 100644
index 0000000..04adebb
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/client/asyncsearch/package-info.java
@@ -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;
+
+
+
diff --git a/sdk/examples/org/opends/sdk/examples/client/modify/Main.java b/sdk/examples/org/opends/sdk/examples/client/modify/Main.java
new file mode 100644
index 0000000..6968f5e
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/client/modify/Main.java
@@ -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>
+ * <host> <port> <username> <password> [<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.
+ }
+}
diff --git a/sdk/examples/org/opends/sdk/examples/client/modify/package-info.java b/sdk/examples/org/opends/sdk/examples/client/modify/package-info.java
new file mode 100644
index 0000000..4a47232
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/client/modify/package-info.java
@@ -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;
+
+
+
diff --git a/sdk/examples/org/opends/sdk/examples/client/search/Main.java b/sdk/examples/org/opends/sdk/examples/client/search/Main.java
new file mode 100644
index 0000000..8809bcd
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/client/search/Main.java
@@ -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>
+ * <host> <port> <username> <password>
+ * <baseDN> <scope> <filter> [<attibute> <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.
+ }
+}
diff --git a/sdk/examples/org/opends/sdk/examples/client/search/package-info.java b/sdk/examples/org/opends/sdk/examples/client/search/package-info.java
new file mode 100644
index 0000000..587d7bd
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/client/search/package-info.java
@@ -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;
+
+
+
diff --git a/sdk/examples/org/opends/sdk/examples/server/proxy/Main.java b/sdk/examples/org/opends/sdk/examples/server/proxy/Main.java
new file mode 100644
index 0000000..c95fddf
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/server/proxy/Main.java
@@ -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>
+ * <listenAddress> <listenPort> <remoteAddress1> <remotePort1>
+ * [<remoteAddress2> <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.
+ }
+}
diff --git a/sdk/examples/org/opends/sdk/examples/server/proxy/package-info.java b/sdk/examples/org/opends/sdk/examples/server/proxy/package-info.java
new file mode 100644
index 0000000..e940d72
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/server/proxy/package-info.java
@@ -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;
+
+
+
diff --git a/sdk/examples/org/opends/sdk/examples/server/store/Main.java b/sdk/examples/org/opends/sdk/examples/server/store/Main.java
new file mode 100644
index 0000000..cae70d8
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/server/store/Main.java
@@ -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>
+ * <listenAddress> <listenPort> [<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.
+ }
+}
diff --git a/sdk/examples/org/opends/sdk/examples/server/store/package-info.java b/sdk/examples/org/opends/sdk/examples/server/store/package-info.java
new file mode 100644
index 0000000..e4df616
--- /dev/null
+++ b/sdk/examples/org/opends/sdk/examples/server/store/package-info.java
@@ -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;
+
+
+
diff --git a/sdk/lib/gmbal-api-only.jar b/sdk/lib/gmbal-api-only.jar
new file mode 100644
index 0000000..a8f9750
--- /dev/null
+++ b/sdk/lib/gmbal-api-only.jar
Binary files differ
diff --git a/sdk/lib/grizzly.jar b/sdk/lib/grizzly.jar
index 137fb72..ab8e441 100644
--- a/sdk/lib/grizzly.jar
+++ b/sdk/lib/grizzly.jar
Binary files differ
diff --git a/sdk/nbproject/genfiles.properties b/sdk/nbproject/genfiles.properties
new file mode 100644
index 0000000..3c5d60b
--- /dev/null
+++ b/sdk/nbproject/genfiles.properties
@@ -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
diff --git a/sdk/nbproject/ide-file-targets.xml b/sdk/nbproject/ide-file-targets.xml
new file mode 100644
index 0000000..7ba9c68
--- /dev/null
+++ b/sdk/nbproject/ide-file-targets.xml
@@ -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>
diff --git a/sdk/nbproject/ide-targets.xml b/sdk/nbproject/ide-targets.xml
new file mode 100644
index 0000000..b733c70
--- /dev/null
+++ b/sdk/nbproject/ide-targets.xml
@@ -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>
diff --git a/sdk/nbproject/project.xml b/sdk/nbproject/project.xml
new file mode 100644
index 0000000..9f65884
--- /dev/null
+++ b/sdk/nbproject/project.xml
@@ -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>
diff --git a/sdk/resource/example-2000.ldif.zip b/sdk/resource/example-2000.ldif.zip
new file mode 100644
index 0000000..816619c
--- /dev/null
+++ b/sdk/resource/example-2000.ldif.zip
Binary files differ
diff --git a/sdk/src/com/sun/opends/sdk/extensions/GetConnectionIDExtendedRequest.java b/sdk/src/com/sun/opends/sdk/extensions/GetConnectionIDExtendedRequest.java
index 6166612..6b185e3 100644
--- a/sdk/src/com/sun/opends/sdk/extensions/GetConnectionIDExtendedRequest.java
+++ b/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)
{
diff --git a/sdk/src/com/sun/opends/sdk/extensions/GetSymmetricKeyExtendedRequest.java b/sdk/src/com/sun/opends/sdk/extensions/GetSymmetricKeyExtendedRequest.java
index 4056c87..30ad68f 100644
--- a/sdk/src/com/sun/opends/sdk/extensions/GetSymmetricKeyExtendedRequest.java
+++ b/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(
diff --git a/sdk/src/com/sun/opends/sdk/extensions/PasswordPolicyStateExtendedRequest.java b/sdk/src/com/sun/opends/sdk/extensions/PasswordPolicyStateExtendedRequest.java
index 59a06e1..c73adab 100644
--- a/sdk/src/com/sun/opends/sdk/extensions/PasswordPolicyStateExtendedRequest.java
+++ b/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)
{
diff --git a/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferReader.java b/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferReader.java
index 51999bd..78ff32d 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferReader.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferWriter.java b/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferWriter.java
index fcb4096..2f3e8e5 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/ASN1BufferWriter.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/ldap/AbstractLDAPFutureResultImpl.java b/sdk/src/com/sun/opends/sdk/ldap/AbstractLDAPFutureResultImpl.java
index 0cc8329..4881524 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/AbstractLDAPFutureResultImpl.java
+++ b/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);
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java b/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java
index 85d102d..a4d92bc 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/GlobalTransportFactory.java
+++ b/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();
}
+
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPListenerOptions.java b/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPListenerOptions.java
index ec3e223..ed2de71 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPListenerOptions.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPOptions.java b/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPOptions.java
index f75e634..d6f76ec 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/GrizzlyLDAPOptions.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java b/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java
index 00d07e7..99200ef 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/InternalConnection.java
+++ b/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();
+ }
+
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java
index 9d9b058..9d8d77e 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPBindFutureResultImpl.java
+++ b/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;
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java
index ea42e73..9a8b55f 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPClientFilter.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPCompareFutureResultImpl.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPCompareFutureResultImpl.java
index df491a0..193ddc8 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPCompareFutureResultImpl.java
+++ b/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;
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
index 25f7d84..dbe9ed3 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPConnection.java
+++ b/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);
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
index d56efdd..9b9c24f 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPConnectionFactoryImpl.java
+++ b/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);
}
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java
index 1c162a6..6005fa4 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPExtendedFutureResultImpl.java
+++ b/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);
}
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java
index 3733164..bcf8052 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPFutureResultImpl.java
+++ b/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);
}
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPListenerImpl.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPListenerImpl.java
index f534f28..90dd3a3 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPListenerImpl.java
+++ b/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();
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java
index 70a8e2d..a7c71ea 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPSearchFutureResultImpl.java
+++ b/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;
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java
index 3bd1034..2dbe61b 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPServerFilter.java
+++ b/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)
diff --git a/sdk/src/com/sun/opends/sdk/ldap/LDAPWriter.java b/sdk/src/com/sun/opends/sdk/ldap/LDAPWriter.java
index 4417714..feaabf7 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/LDAPWriter.java
+++ b/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);
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/SASLDecoderTransformer.java b/sdk/src/com/sun/opends/sdk/ldap/SASLDecoderTransformer.java
index 20e40cc..5e817ba 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/SASLDecoderTransformer.java
+++ b/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);
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/SASLEncoderTransformer.java b/sdk/src/com/sun/opends/sdk/ldap/SASLEncoderTransformer.java
index 3e182f9..4878ab7 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/SASLEncoderTransformer.java
+++ b/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);
}
diff --git a/sdk/src/com/sun/opends/sdk/ldap/SASLFilter.java b/sdk/src/com/sun/opends/sdk/ldap/SASLFilter.java
index 5676d44..c08fa1c 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/SASLFilter.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/ldap/TimeoutChecker.java b/sdk/src/com/sun/opends/sdk/ldap/TimeoutChecker.java
index 7139e0b..bdae4a6 100644
--- a/sdk/src/com/sun/opends/sdk/ldap/TimeoutChecker.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java b/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
index 78f7329..48b7a78 100644
--- a/sdk/src/com/sun/opends/sdk/tools/ArgumentParserConnectionFactory.java
+++ b/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;
diff --git a/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java b/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java
index a331e0e..f66b335 100644
--- a/sdk/src/com/sun/opends/sdk/tools/AuthenticatedConnectionFactory.java
+++ b/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();
+ }
+
}
diff --git a/sdk/src/com/sun/opends/sdk/util/AsynchronousFutureResult.java b/sdk/src/com/sun/opends/sdk/util/AsynchronousFutureResult.java
new file mode 100644
index 0000000..0fe5a68
--- /dev/null
+++ b/sdk/src/com/sun/opends/sdk/util/AsynchronousFutureResult.java
@@ -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);
+ }
+
+}
diff --git a/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java b/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java
index e719784..0857e0a 100644
--- a/sdk/src/com/sun/opends/sdk/util/RecursiveFutureResult.java
+++ b/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)
{
diff --git a/sdk/src/com/sun/opends/sdk/util/StaticUtils.java b/sdk/src/com/sun/opends/sdk/util/StaticUtils.java
index e40902c..64cdabb 100644
--- a/sdk/src/com/sun/opends/sdk/util/StaticUtils.java
+++ b/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.
*
diff --git a/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java b/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
index 56ec077..685567d 100644
--- a/sdk/src/org/opends/sdk/AbstractAsynchronousConnection.java
+++ b/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();
+
}
diff --git a/sdk/src/org/opends/sdk/AbstractConnection.java b/sdk/src/org/opends/sdk/AbstractConnection.java
index c56e848..d4c8ea7 100644
--- a/sdk/src/org/opends/sdk/AbstractConnection.java
+++ b/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();
+
}
diff --git a/sdk/src/org/opends/sdk/AbstractConnectionFactory.java b/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
index 7ae9ee2..a951488 100644
--- a/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
+++ b/sdk/src/org/opends/sdk/AbstractConnectionFactory.java
@@ -80,4 +80,11 @@
{
return getAsynchronousConnection(null).get().getSynchronousConnection();
}
+
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public abstract String toString();
}
diff --git a/sdk/src/org/opends/sdk/AbstractLoadBalancingAlgorithm.java b/sdk/src/org/opends/sdk/AbstractLoadBalancingAlgorithm.java
new file mode 100644
index 0000000..75826a1
--- /dev/null
+++ b/sdk/src/org/opends/sdk/AbstractLoadBalancingAlgorithm.java
@@ -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"));
+ }
+}
diff --git a/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java b/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
index 1027917..425e8e0 100644
--- a/sdk/src/org/opends/sdk/AuthenticatedConnectionFactory.java
+++ b/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();
+ }
+
}
diff --git a/sdk/src/org/opends/sdk/ConnectionPool.java b/sdk/src/org/opends/sdk/ConnectionPool.java
index 1789d1d..a7b5f75 100644
--- a/sdk/src/org/opends/sdk/ConnectionPool.java
+++ b/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();
}
diff --git a/sdk/src/org/opends/sdk/Connections.java b/sdk/src/org/opends/sdk/Connections.java
index 9d9fec6..74813f0 100644
--- a/sdk/src/org/opends/sdk/Connections.java
+++ b/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()
{
diff --git a/sdk/src/org/opends/sdk/Entries.java b/sdk/src/org/opends/sdk/Entries.java
index 14fbaae..55a17da 100644
--- a/sdk/src/org/opends/sdk/Entries.java
+++ b/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}.
diff --git a/sdk/src/org/opends/sdk/Entry.java b/sdk/src/org/opends/sdk/Entry.java
index e237739..9a573e9 100644
--- a/sdk/src/org/opends/sdk/Entry.java
+++ b/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
{
diff --git a/sdk/src/org/opends/sdk/ErrorResultException.java b/sdk/src/org/opends/sdk/ErrorResultException.java
index f64756f..6d57f82 100644
--- a/sdk/src/org/opends/sdk/ErrorResultException.java
+++ b/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.
diff --git a/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java b/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
index 4910e44..6ac3148 100644
--- a/sdk/src/org/opends/sdk/FailoverLoadBalancingAlgorithm.java
+++ b/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;
+ }
+
}
diff --git a/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java b/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
index ccf41df..381cc43 100644
--- a/sdk/src/org/opends/sdk/HeartBeatConnectionFactory.java
+++ b/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();
+ }
}
diff --git a/sdk/src/org/opends/sdk/InternalConnectionFactory.java b/sdk/src/org/opends/sdk/InternalConnectionFactory.java
index 8749d7a..90d248e 100644
--- a/sdk/src/org/opends/sdk/InternalConnectionFactory.java
+++ b/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();
+ }
}
diff --git a/sdk/src/org/opends/sdk/LDAPClientContext.java b/sdk/src/org/opends/sdk/LDAPClientContext.java
index 878e020..22a2f93 100644
--- a/sdk/src/org/opends/sdk/LDAPClientContext.java
+++ b/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
diff --git a/sdk/src/org/opends/sdk/LDAPConnectionFactory.java b/sdk/src/org/opends/sdk/LDAPConnectionFactory.java
index 5cbde4a..a632071 100644
--- a/sdk/src/org/opends/sdk/LDAPConnectionFactory.java
+++ b/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();
+ }
}
diff --git a/sdk/src/org/opends/sdk/LDAPListener.java b/sdk/src/org/opends/sdk/LDAPListener.java
index 6038cba..619abaa 100644
--- a/sdk/src/org/opends/sdk/LDAPListener.java
+++ b/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<Result> handler,
* IntermediateResponseHandler intermediateResponseHandler)
* throws UnsupportedOperationException
@@ -73,9 +88,10 @@
*
*
*
- * class MyServer implements ServerConnectionFactory<LDAPClientContext, Integer>
+ * class MyServer implements
+ * ServerConnectionFactory<LDAPClientContext, RequestContext>
* {
- * public ServerConnection<Integer> accept(LDAPClientContext context)
+ * public ServerConnection<RequestContext> accept(LDAPClientContext context)
* {
* System.out.println("Connection from: " + 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();
+ }
+
}
diff --git a/sdk/src/org/opends/sdk/LinkedAttribute.java b/sdk/src/org/opends/sdk/LinkedAttribute.java
index 89ba995..5fb320a 100644
--- a/sdk/src/org/opends/sdk/LinkedAttribute.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/LoadBalancer.java b/sdk/src/org/opends/sdk/LoadBalancer.java
new file mode 100644
index 0000000..4dd63d9
--- /dev/null
+++ b/sdk/src/org/opends/sdk/LoadBalancer.java
@@ -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();
+ }
+}
diff --git a/sdk/src/org/opends/sdk/LoadBalancingAlgorithm.java b/sdk/src/org/opends/sdk/LoadBalancingAlgorithm.java
index 6e9ffb0..fc81c3d 100644
--- a/sdk/src/org/opends/sdk/LoadBalancingAlgorithm.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/RoundRobinLoadBalancingAlgorithm.java b/sdk/src/org/opends/sdk/RoundRobinLoadBalancingAlgorithm.java
new file mode 100644
index 0000000..cf4ab46
--- /dev/null
+++ b/sdk/src/org/opends/sdk/RoundRobinLoadBalancingAlgorithm.java
@@ -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;
+ }
+
+}
diff --git a/sdk/src/org/opends/sdk/ServerConnection.java b/sdk/src/org/opends/sdk/ServerConnection.java
index 950f6dc..b7c560d 100644
--- a/sdk/src/org/opends/sdk/ServerConnection.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/ServerConnectionFactory.java b/sdk/src/org/opends/sdk/ServerConnectionFactory.java
index 72f3928..4985d08 100644
--- a/sdk/src/org/opends/sdk/ServerConnectionFactory.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/SynchronousConnection.java b/sdk/src/org/opends/sdk/SynchronousConnection.java
index b1bac79..b382b5c 100644
--- a/sdk/src/org/opends/sdk/SynchronousConnection.java
+++ b/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();
+ }
+
}
diff --git a/sdk/src/org/opends/sdk/controls/package-info.java b/sdk/src/org/opends/sdk/controls/package-info.java
index 7ec769a..da216de 100755
--- a/sdk/src/org/opends/sdk/controls/package-info.java
+++ b/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;
diff --git a/sdk/src/org/opends/sdk/package-info.java b/sdk/src/org/opends/sdk/package-info.java
index fdd33fd..6275f95 100755
--- a/sdk/src/org/opends/sdk/package-info.java
+++ b/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;
diff --git a/sdk/src/org/opends/sdk/requests/AbandonRequest.java b/sdk/src/org/opends/sdk/requests/AbandonRequest.java
index b8e705d..305c931 100644
--- a/sdk/src/org/opends/sdk/requests/AbandonRequest.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/requests/AbandonRequestImpl.java b/sdk/src/org/opends/sdk/requests/AbandonRequestImpl.java
index 21df3a9..b09275d 100644
--- a/sdk/src/org/opends/sdk/requests/AbandonRequestImpl.java
+++ b/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(")");
diff --git a/sdk/src/org/opends/sdk/requests/CancelExtendedRequest.java b/sdk/src/org/opends/sdk/requests/CancelExtendedRequest.java
index 7bae585..e8468a2 100644
--- a/sdk/src/org/opends/sdk/requests/CancelExtendedRequest.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/requests/CancelExtendedRequestImpl.java b/sdk/src/org/opends/sdk/requests/CancelExtendedRequestImpl.java
index 627fa69..51e89f2 100644
--- a/sdk/src/org/opends/sdk/requests/CancelExtendedRequestImpl.java
+++ b/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(")");
diff --git a/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequest.java b/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequest.java
index 53dc7e8..f5363e5 100644
--- a/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequest.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java b/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java
index 24b4148..cce0537 100644
--- a/sdk/src/org/opends/sdk/requests/DigestMD5SASLBindRequestImpl.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequest.java b/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequest.java
index e3d37f7..814efae 100644
--- a/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequest.java
+++ b/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);
-
}
diff --git a/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequestImpl.java b/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequestImpl.java
index 62ae2e0..afee8e3 100644
--- a/sdk/src/org/opends/sdk/requests/GSSAPISASLBindRequestImpl.java
+++ b/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()
{
diff --git a/sdk/src/org/opends/sdk/requests/GenericExtendedRequestImpl.java b/sdk/src/org/opends/sdk/requests/GenericExtendedRequestImpl.java
index cb3321d..b11f25d 100644
--- a/sdk/src/org/opends/sdk/requests/GenericExtendedRequestImpl.java
+++ b/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)
{
diff --git a/sdk/src/org/opends/sdk/requests/PasswordModifyExtendedRequestImpl.java b/sdk/src/org/opends/sdk/requests/PasswordModifyExtendedRequestImpl.java
index a32a7b6..ee19507 100644
--- a/sdk/src/org/opends/sdk/requests/PasswordModifyExtendedRequestImpl.java
+++ b/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)
{
diff --git a/sdk/src/org/opends/sdk/requests/Request.java b/sdk/src/org/opends/sdk/requests/Request.java
index 741fe77..8243a26 100644
--- a/sdk/src/org/opends/sdk/requests/Request.java
+++ b/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
{
diff --git a/sdk/src/org/opends/sdk/requests/Requests.java b/sdk/src/org/opends/sdk/requests/Requests.java
index 027b1eb..eab61a4 100644
--- a/sdk/src/org/opends/sdk/requests/Requests.java
+++ b/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.
*
diff --git a/sdk/src/org/opends/sdk/requests/SearchRequest.java b/sdk/src/org/opends/sdk/requests/SearchRequest.java
index 38e00dc..e0e965a 100644
--- a/sdk/src/org/opends/sdk/requests/SearchRequest.java
+++ b/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;
diff --git a/sdk/src/org/opends/sdk/requests/SearchRequestImpl.java b/sdk/src/org/opends/sdk/requests/SearchRequestImpl.java
index 2337021..a56b610 100644
--- a/sdk/src/org/opends/sdk/requests/SearchRequestImpl.java
+++ b/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;
}
diff --git a/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java b/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java
index 9032e55..513c75e 100644
--- a/sdk/src/org/opends/sdk/requests/StartTLSExtendedRequestImpl.java
+++ b/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)
{
diff --git a/sdk/src/org/opends/sdk/requests/WhoAmIExtendedRequestImpl.java b/sdk/src/org/opends/sdk/requests/WhoAmIExtendedRequestImpl.java
index bc91781..d1bb727 100644
--- a/sdk/src/org/opends/sdk/requests/WhoAmIExtendedRequestImpl.java
+++ b/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)
{
diff --git a/sdk/src/org/opends/sdk/responses/AbstractExtendedResultDecoder.java b/sdk/src/org/opends/sdk/responses/AbstractExtendedResultDecoder.java
new file mode 100644
index 0000000..d940ca9
--- /dev/null
+++ b/sdk/src/org/opends/sdk/responses/AbstractExtendedResultDecoder.java
@@ -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;
+
+}
diff --git a/sdk/src/org/opends/sdk/responses/ExtendedResultDecoder.java b/sdk/src/org/opends/sdk/responses/ExtendedResultDecoder.java
index db59197..5e4c637 100644
--- a/sdk/src/org/opends/sdk/responses/ExtendedResultDecoder.java
+++ b/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;
+
}
diff --git a/sdk/src/overview.html b/sdk/src/overview.html
new file mode 100644
index 0000000..abcc487
--- /dev/null
+++ b/sdk/src/overview.html
@@ -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>
diff --git a/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferReaderTestCase.java b/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferReaderTestCase.java
index 95ba625..eac55aa 100644
--- a/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferReaderTestCase.java
+++ b/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;
diff --git a/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferWriterTestCase.java b/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferWriterTestCase.java
index ac123cb..cc1255f 100644
--- a/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/ASN1BufferWriterTestCase.java
+++ b/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;
diff --git a/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/GlobalTransportFactoryTestCase.java b/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/GlobalTransportFactoryTestCase.java
index 1608679..598cb69 100644
--- a/sdk/tests/unit-tests-testng/src/com/sun/opends/sdk/ldap/GlobalTransportFactoryTestCase.java
+++ b/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;
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java
index a3a876f..3984c96 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/ConnectionFactoryTestCase.java
+++ b/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;
}
}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/DNTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/DNTestCase.java
index f4a7509..3e5d334 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/DNTestCase.java
+++ b/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 } };
}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntriesTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntriesTestCase.java
new file mode 100644
index 0000000..8b0ee61
--- /dev/null
+++ b/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntriesTestCase.java
@@ -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());
+ }
+ }
+}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntryTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntryTestCase.java
index 1303285..f1297e0 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/EntryTestCase.java
+++ b/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
{
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPListenerTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPListenerTestCase.java
new file mode 100644
index 0000000..6aec25d
--- /dev/null
+++ b/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPListenerTestCase.java
@@ -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());
+ }
+ }
+}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java
index c6695cc..a11c5d5 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/LDAPServer.java
+++ b/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();
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/LinkedAttributeTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/LinkedAttributeTestCase.java
index 9a84966..4ec0f6b 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/LinkedAttributeTestCase.java
+++ b/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());
+ }
}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/TestCaseUtils.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/TestCaseUtils.java
index f419fc3..4d8fc28 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/TestCaseUtils.java
+++ b/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);
}
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/DigestMD5SASLBindRequestTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/DigestMD5SASLBindRequestTestCase.java
index 530d4ae..52629a6 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/DigestMD5SASLBindRequestTestCase.java
+++ b/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")
diff --git a/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/GSSAPISASLBindRequestTestCase.java b/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/GSSAPISASLBindRequestTestCase.java
index 9241213..eadef95 100644
--- a/sdk/tests/unit-tests-testng/src/org/opends/sdk/requests/GSSAPISASLBindRequestTestCase.java
+++ b/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")
--
Gitblit v1.10.0