From 3ab4c7a0bade5b970e432922b6e5f337e87a457f Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 01 Feb 2013 14:57:43 +0000
Subject: [PATCH] Pull EntryContainer into LDAPCollectionResourceProvider until layering is better understood.
---
/dev/null | 238 -----------------
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java | 32 +-
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java | 8
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java | 2
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java | 247 +++++++++++++++--
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java | 197 +++++++++++++
opendj-sdk/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java | 64 ++--
7 files changed, 452 insertions(+), 336 deletions(-)
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
index 9d7f367..c5dfa31 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
@@ -91,10 +91,10 @@
if (f == null) {
// No mapping so remove.
i.remove();
- } else if (f == c.getConfig().getFalseFilter()) {
- return c.getConfig().getFalseFilter();
- } else if (f == c.getConfig().getTrueFilter()) {
- return c.getConfig().getTrueFilter();
+ } else if (f == c.getConfig().falseFilter()) {
+ return c.getConfig().falseFilter();
+ } else if (f == c.getConfig().trueFilter()) {
+ return c.getConfig().trueFilter();
}
}
switch (value.size()) {
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
index b36b195..41f16eb 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
@@ -15,6 +15,9 @@
*/
package org.forgerock.opendj.rest2ldap;
+import static org.forgerock.opendj.rest2ldap.Config.ReadOnUpdatePolicy.USE_READ_ENTRY_CONTROLS;
+import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull;
+
import org.forgerock.opendj.ldap.Filter;
/**
@@ -22,24 +25,200 @@
*/
public final class Config {
- private final Filter trueFilter = Filter.objectClassPresent();
- private final Filter falseFilter = Filter.present("1.1");
+ /**
+ * An interface for incrementally constructing common configuration options.
+ */
+ public static final class Builder {
+ private Filter falseFilter;
+ private ReadOnUpdatePolicy readOnUpdatePolicy;
+ private Filter trueFilter;
+
+ private Builder() {
+ // Nothing to do.
+ }
+
+ /**
+ * Returns a new configuration based on the current state of this
+ * builder.
+ *
+ * @return A new configuration based on the current state of this
+ * builder.
+ */
+ public Config build() {
+ return new Config(trueFilter, falseFilter, readOnUpdatePolicy);
+ }
+
+ /**
+ * Sets the absolute false filter which should be used when querying the
+ * LDAP server.
+ *
+ * @param filter
+ * The absolute false filter.
+ * @return A reference to this builder.
+ */
+ public Builder falseFilter(final Filter filter) {
+ this.trueFilter = ensureNotNull(filter);
+ return this;
+ }
+
+ /**
+ * Sets the policy which should be used in order to read an entry before
+ * it is deleted, or after it is added or modified.
+ *
+ * @param policy
+ * The policy which should be used in order to read an entry
+ * before it is deleted, or after it is added or modified.
+ * @return A reference to this builder.
+ */
+ public Builder readOnUpdatePolicy(final ReadOnUpdatePolicy policy) {
+ this.readOnUpdatePolicy = ensureNotNull(policy);
+ return this;
+ }
+
+ /**
+ * Sets the absolute true filter which should be used when querying the
+ * LDAP server.
+ *
+ * @param filter
+ * The absolute true filter.
+ * @return A reference to this builder.
+ */
+ public Builder trueFilter(final Filter filter) {
+ this.trueFilter = ensureNotNull(filter);
+ return this;
+ }
+ };
/**
- * Returns the absolute true filter.
- *
- * @return The absolute true filter.
+ * The policy which should be used in order to read an entry before it is
+ * deleted, or after it is added or modified.
*/
- public Filter getTrueFilter() {
- return trueFilter;
+ public static enum ReadOnUpdatePolicy {
+ /**
+ * The LDAP entry will not be read when an update is performed. More
+ * specifically, the REST resource will not be returned as part of a
+ * create, delete, patch, or update request.
+ */
+ DISABLED,
+
+ /**
+ * The LDAP entry will be read atomically using the RFC 4527 read-entry
+ * controls. More specifically, the REST resource will be returned as
+ * part of a create, delete, patch, or update request, and it will
+ * reflect the state of the resource at the time the update was
+ * performed. This policy requires that the LDAP server supports RFC
+ * 4527.
+ */
+ USE_READ_ENTRY_CONTROLS,
+
+ /**
+ * The LDAP entry will be read non-atomically using an LDAP search when
+ * an update is performed. More specifically, the REST resource will be
+ * returned as part of a create, delete, patch, or update request, but
+ * it may not reflect the state of the resource at the time the update
+ * was performed.
+ */
+ USE_SEARCH;
+ }
+
+ private static final Config DEFAULT = new Builder().trueFilter(Filter.objectClassPresent())
+ .falseFilter(Filter.present("1.1")).readOnUpdatePolicy(USE_READ_ENTRY_CONTROLS).build();
+
+ /**
+ * Returns a new builder which can be used for incrementally constructing
+ * common configuration options. The builder will initially have
+ * {@link #defaultConfig() default} settings.
+ *
+ * @return The new builder.
+ */
+ public static Builder builder() {
+ return builder(DEFAULT);
}
/**
- * Returns the absolute false filter.
+ * Returns a new builder which can be used for incrementally constructing
+ * common configuration options. The builder will initially have the same
+ * settings as the provided configuration.
+ *
+ * @param config
+ * The initial settings.
+ * @return The new builder.
+ */
+ public static Builder builder(final Config config) {
+ return new Builder().trueFilter(config.trueFilter()).falseFilter(config.falseFilter())
+ .readOnUpdatePolicy(config.readOnUpdatePolicy());
+ }
+
+ /**
+ * Returns the default configuration having the following settings:
+ * <ul>
+ * <li>the absolute true filter {@code (objectClass=*)}
+ * <li>the absolute false filter {@code (1.1=*)}
+ * <li>the read on update policy
+ * {@link ReadOnUpdatePolicy#USE_READ_ENTRY_CONTROLS}.
+ * </ul>
+ *
+ * @return The default configuration.
+ */
+ public static Config defaultConfig() {
+ return DEFAULT;
+ }
+
+ private final Filter falseFilter;
+
+ private final ReadOnUpdatePolicy readOnUpdatePolicy;
+ private final Filter trueFilter;
+
+ private Config(final Filter trueFilter, final Filter falseFilter,
+ final ReadOnUpdatePolicy readOnUpdatePolicy) {
+ this.trueFilter = trueFilter;
+ this.falseFilter = falseFilter;
+ this.readOnUpdatePolicy = readOnUpdatePolicy;
+ }
+
+ /**
+ * Returns the absolute false filter which should be used when querying the
+ * LDAP server.
*
* @return The absolute false filter.
*/
- public Filter getFalseFilter() {
+ public Filter falseFilter() {
return falseFilter;
}
+
+ /**
+ * Returns the policy which should be used in order to read an entry before
+ * it is deleted, or after it is added or modified.
+ *
+ * @return The policy which should be used in order to read an entry before
+ * it is deleted, or after it is added or modified.
+ */
+ public ReadOnUpdatePolicy readOnUpdatePolicy() {
+ return readOnUpdatePolicy;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("trueFilter=");
+ builder.append(trueFilter);
+ builder.append(", falseFilter=");
+ builder.append(falseFilter);
+ builder.append(", readOnUpdatePolicy=");
+ builder.append(readOnUpdatePolicy);
+ return builder.toString();
+ }
+
+ /**
+ * Returns the absolute true filter which should be used when querying the
+ * LDAP server.
+ *
+ * @return The absolute true filter.
+ */
+ public Filter trueFilter() {
+ return trueFilter;
+ }
}
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
index 5fc4644..fec587e 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
@@ -69,20 +69,20 @@
if (jsonAttribute.size() == 1 && jsonAttribute.get(0).equalsIgnoreCase(jsonAttributeName)) {
final Filter filter;
if (type == FilterType.PRESENT) {
- filter = c.getConfig().getTrueFilter();
+ filter = c.getConfig().trueFilter();
} else if (jsonAttributeValue instanceof String && valueAssertion instanceof String) {
final String v1 = toLowerCase((String) jsonAttributeValue);
final String v2 = toLowerCase((String) valueAssertion);
switch (type) {
case CONTAINS:
filter =
- v1.contains(v2) ? c.getConfig().getTrueFilter() : c.getConfig()
- .getFalseFilter();
+ v1.contains(v2) ? c.getConfig().trueFilter() : c.getConfig()
+ .falseFilter();
break;
case STARTS_WITH:
filter =
- v1.startsWith(v2) ? c.getConfig().getTrueFilter() : c.getConfig()
- .getFalseFilter();
+ v1.startsWith(v2) ? c.getConfig().trueFilter() : c.getConfig()
+ .falseFilter();
break;
default:
filter = compare(c, type, v1, v2);
@@ -98,7 +98,7 @@
filter = compare(c, type, v1, v2);
} else {
// This attribute mapper is a candidate but it does not match.
- filter = c.getConfig().getFalseFilter();
+ filter = c.getConfig().falseFilter();
}
h.handleResult(filter);
} else {
@@ -130,30 +130,30 @@
final Filter filter;
switch (type) {
case EQUAL_TO:
- filter = v1.equals(v2) ? c.getConfig().getTrueFilter() : c.getConfig().getFalseFilter();
+ filter = v1.equals(v2) ? c.getConfig().trueFilter() : c.getConfig().falseFilter();
break;
case GREATER_THAN:
filter =
- v1.compareTo(v2) > 0 ? c.getConfig().getTrueFilter() : c.getConfig()
- .getFalseFilter();
+ v1.compareTo(v2) > 0 ? c.getConfig().trueFilter() : c.getConfig()
+ .falseFilter();
break;
case GREATER_THAN_OR_EQUAL_TO:
filter =
- v1.compareTo(v2) >= 0 ? c.getConfig().getTrueFilter() : c.getConfig()
- .getFalseFilter();
+ v1.compareTo(v2) >= 0 ? c.getConfig().trueFilter() : c.getConfig()
+ .falseFilter();
break;
case LESS_THAN:
filter =
- v1.compareTo(v2) < 0 ? c.getConfig().getTrueFilter() : c.getConfig()
- .getFalseFilter();
+ v1.compareTo(v2) < 0 ? c.getConfig().trueFilter() : c.getConfig()
+ .falseFilter();
break;
case LESS_THAN_OR_EQUAL_TO:
filter =
- v1.compareTo(v2) <= 0 ? c.getConfig().getTrueFilter() : c.getConfig()
- .getFalseFilter();
+ v1.compareTo(v2) <= 0 ? c.getConfig().trueFilter() : c.getConfig()
+ .falseFilter();
break;
default:
- filter = c.getConfig().getFalseFilter(); // Not supported.
+ filter = c.getConfig().falseFilter(); // Not supported.
break;
}
return filter;
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
deleted file mode 100644
index f6916de..0000000
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * The contents of this file are subject to the terms of the Common Development and
- * Distribution License (the License). You may not use this file except in compliance with the
- * License.
- *
- * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
- * specific language governing permission and limitations under the License.
- *
- * When distributing Covered Software, include this CDDL Header Notice in each file and include
- * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
- * Header, with the fields enclosed by brackets [] replaced by your own identifying
- * information: "Portions Copyright [year] [name of copyright owner]".
- *
- * Copyright 2012-2013 ForgeRock AS.
- */
-package org.forgerock.opendj.rest2ldap;
-
-import java.util.Collection;
-
-import org.forgerock.opendj.ldap.Connection;
-import org.forgerock.opendj.ldap.ConnectionFactory;
-import org.forgerock.opendj.ldap.DN;
-import org.forgerock.opendj.ldap.Entry;
-import org.forgerock.opendj.ldap.ErrorResultException;
-import org.forgerock.opendj.ldap.Filter;
-import org.forgerock.opendj.ldap.ResultHandler;
-import org.forgerock.opendj.ldap.SearchResultHandler;
-import org.forgerock.opendj.ldap.SearchScope;
-import org.forgerock.opendj.ldap.requests.Requests;
-import org.forgerock.opendj.ldap.requests.SearchRequest;
-import org.forgerock.opendj.ldap.responses.Result;
-import org.forgerock.opendj.ldap.responses.SearchResultEntry;
-import org.forgerock.opendj.ldap.responses.SearchResultReference;
-
-/**
- * An entry container.
- */
-public final class EntryContainer {
- private abstract class AbstractRequestCompletionHandler<R, H extends ResultHandler<? super R>>
- implements ResultHandler<R> {
- final Connection connection;
- final H resultHandler;
-
- AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
- this.connection = connection;
- this.resultHandler = resultHandler;
- }
-
- @Override
- public final void handleErrorResult(final ErrorResultException error) {
- connection.close();
- resultHandler.handleErrorResult(error);
- }
-
- @Override
- public final void handleResult(final R result) {
- connection.close();
- resultHandler.handleResult(result);
- }
-
- }
-
- private abstract class ConnectionCompletionHandler<R> implements ResultHandler<Connection> {
- private final ResultHandler<? super R> resultHandler;
-
- ConnectionCompletionHandler(final ResultHandler<? super R> resultHandler) {
- this.resultHandler = resultHandler;
- }
-
- @Override
- public final void handleErrorResult(final ErrorResultException error) {
- resultHandler.handleErrorResult(error);
- }
-
- @Override
- public abstract void handleResult(Connection connection);
-
- }
-
- private final class RequestCompletionHandler<R> extends
- AbstractRequestCompletionHandler<R, ResultHandler<? super R>> {
- RequestCompletionHandler(final Connection connection,
- final ResultHandler<? super R> resultHandler) {
- super(connection, resultHandler);
- }
- }
-
- private final class SearchRequestCompletionHandler extends
- AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
- SearchResultHandler {
-
- SearchRequestCompletionHandler(final Connection connection,
- final SearchResultHandler resultHandler) {
- super(connection, resultHandler);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public final boolean handleEntry(final SearchResultEntry entry) {
- return resultHandler.handleEntry(entry);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public final boolean handleReference(final SearchResultReference reference) {
- return resultHandler.handleReference(reference);
- }
-
- }
-
- // FIXME: make this configurable.
- private static final String ETAG_ATTRIBUTE = "etag";
-
- // FIXME: make this configurable, also allow use of DN.
- private static final String UUID_ATTRIBUTE = "entryUUID";
-
- // TODO: support template variables.
- private final DN baseDN;
-
- private final ConnectionFactory factory;
-
- /**
- * Creates a new entry container for the provided base DN and LDAP
- * connection factory.
- *
- * @param baseDN
- * The base DN.
- * @param factory
- * The LDAP connection factory.
- */
- public EntryContainer(final DN baseDN, final ConnectionFactory factory) {
- this.baseDN = baseDN;
- this.factory = factory;
- }
-
- /**
- * Returns the ETag for the provided entry.
- *
- * @param entry
- * The entry.
- * @return The ETag.
- */
- public String getEtagFromEntry(final Entry entry) {
- return entry.parseAttribute(ETAG_ATTRIBUTE).asString();
- }
-
- /**
- * Returns the resource ID for the provided entry.
- *
- * @param entry
- * The entry.
- * @return The resource ID.
- */
- public String getIDFromEntry(final Entry entry) {
- return entry.parseAttribute(UUID_ATTRIBUTE).asString();
- }
-
- /**
- * Lists the entries contained in this container.
- *
- * @param context
- * The request context.
- * @param filter
- * The LDAP search filter.
- * @param attributes
- * The list of LDAP attributes to be returned.
- * @param handler
- * The search result handler.
- */
- public void listEntries(final Context context, final Filter filter,
- final Collection<String> attributes, final SearchResultHandler handler) {
- final String[] tmp = getSearchAttributes(attributes);
- final ConnectionCompletionHandler<Result> outerHandler =
- new ConnectionCompletionHandler<Result>(handler) {
-
- @Override
- public void handleResult(final Connection connection) {
- final SearchRequestCompletionHandler innerHandler =
- new SearchRequestCompletionHandler(connection, handler);
- final SearchRequest request =
- Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, filter,
- tmp);
- connection.searchAsync(request, null, innerHandler);
- }
-
- };
-
- factory.getConnectionAsync(outerHandler);
- }
-
- /**
- * Reads the entry having the specified resource ID.
- *
- * @param c
- * The request context.
- * @param id
- * The resource ID.
- * @param attributes
- * The list of LDAP attributes to be returned.
- * @param handler
- * The search result handler.
- */
- public void readEntry(final Context c, final String id, final Collection<String> attributes,
- final ResultHandler<SearchResultEntry> handler) {
- final String[] tmp = getSearchAttributes(attributes);
- // @Checkstyle:off
- final ConnectionCompletionHandler<SearchResultEntry> outerHandler =
- new ConnectionCompletionHandler<SearchResultEntry>(handler) {
-
- @Override
- public void handleResult(final Connection connection) {
- final RequestCompletionHandler<SearchResultEntry> innerHandler =
- new RequestCompletionHandler<SearchResultEntry>(connection, handler);
- final SearchRequest request =
- Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter
- .equality(UUID_ATTRIBUTE, id), tmp);
- connection.searchSingleEntryAsync(request, innerHandler);
- }
-
- };
- // @Checkstyle:on
- factory.getConnectionAsync(outerHandler);
- }
-
- private String[] getSearchAttributes(final Collection<String> attributes) {
- // FIXME: who is responsible for adding the UUID and etag attributes to
- // this search?
- final String[] tmp = attributes.toArray(new String[attributes.size() + 2]);
- tmp[tmp.length - 2] = UUID_ATTRIBUTE;
- tmp[tmp.length - 1] = ETAG_ATTRIBUTE;
- return tmp;
- }
-
-}
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
index 5ef4c76..dd3b78f 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
@@ -51,14 +51,21 @@
import org.forgerock.opendj.ldap.AssertionFailureException;
import org.forgerock.opendj.ldap.AuthenticationException;
import org.forgerock.opendj.ldap.AuthorizationException;
+import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionException;
+import org.forgerock.opendj.ldap.ConnectionFactory;
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.EntryNotFoundException;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.Function;
import org.forgerock.opendj.ldap.MultipleEntriesFoundException;
import org.forgerock.opendj.ldap.SearchResultHandler;
+import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.TimeoutResultException;
+import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
@@ -68,25 +75,120 @@
* resource collection to LDAP entries beneath a base DN.
*/
public class LDAPCollectionResourceProvider implements CollectionResourceProvider {
+
+ private abstract class AbstractRequestCompletionHandler<R,
+ H extends org.forgerock.opendj.ldap.ResultHandler<? super R>>
+ implements org.forgerock.opendj.ldap.ResultHandler<R> {
+ final Connection connection;
+ final H resultHandler;
+
+ AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
+ this.connection = connection;
+ this.resultHandler = resultHandler;
+ }
+
+ @Override
+ public final void handleErrorResult(final ErrorResultException error) {
+ connection.close();
+ resultHandler.handleErrorResult(error);
+ }
+
+ @Override
+ public final void handleResult(final R result) {
+ connection.close();
+ resultHandler.handleResult(result);
+ }
+
+ }
+
+ private abstract class ConnectionCompletionHandler<R> implements
+ org.forgerock.opendj.ldap.ResultHandler<Connection> {
+ private final org.forgerock.opendj.ldap.ResultHandler<? super R> resultHandler;
+
+ ConnectionCompletionHandler(
+ final org.forgerock.opendj.ldap.ResultHandler<? super R> resultHandler) {
+ this.resultHandler = resultHandler;
+ }
+
+ @Override
+ public final void handleErrorResult(final ErrorResultException error) {
+ resultHandler.handleErrorResult(error);
+ }
+
+ @Override
+ public abstract void handleResult(Connection connection);
+
+ }
+
+ private final class RequestCompletionHandler<R> extends
+ AbstractRequestCompletionHandler<R, org.forgerock.opendj.ldap.ResultHandler<? super R>> {
+ RequestCompletionHandler(final Connection connection,
+ final org.forgerock.opendj.ldap.ResultHandler<? super R> resultHandler) {
+ super(connection, resultHandler);
+ }
+ }
+
+ private final class SearchRequestCompletionHandler extends
+ AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
+ SearchResultHandler {
+
+ SearchRequestCompletionHandler(final Connection connection,
+ final SearchResultHandler resultHandler) {
+ super(connection, resultHandler);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean handleEntry(final SearchResultEntry entry) {
+ return resultHandler.handleEntry(entry);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public final boolean handleReference(final SearchResultReference reference) {
+ return resultHandler.handleReference(reference);
+ }
+
+ }
+
+ // FIXME: make this configurable.
+ private static final String ETAG_ATTRIBUTE = "etag";
+
// Dummy exception used for signalling search success.
private static final ResourceException SUCCESS = new UncategorizedException(0, null, null);
+
+ // FIXME: make this configurable, also allow use of DN.
+ private static final String UUID_ATTRIBUTE = "entryUUID";
+
private final AttributeMapper attributeMapper;
- private final EntryContainer entryContainer;
- private final Config config = new Config();
+ private final DN baseDN; // TODO: support template variables.
+ private final Config config;
+ private final ConnectionFactory factory;
/**
* Creates a new LDAP resource.
*
- * @param container
- * The LDAP entry container.
+ * @param baseDN
+ * The parent of all entries contained in this LDAP collection.
* @param mapper
* The attribute mapper which will be used for mapping LDAP
* attributes to JSON attributes.
+ * @param factory
+ * The LDAP connection factory which will be used for performing
+ * LDAP operations.
+ * @param config
+ * Common configuration options.
*/
- public LDAPCollectionResourceProvider(final EntryContainer container,
- final AttributeMapper mapper) {
- this.entryContainer = container;
+ public LDAPCollectionResourceProvider(final DN baseDN, final AttributeMapper mapper,
+ final ConnectionFactory factory, final Config config) {
+ this.baseDN = baseDN;
this.attributeMapper = mapper;
+ this.factory = factory;
+ this.config = config;
}
/**
@@ -140,8 +242,10 @@
@Override
public void queryCollection(final ServerContext context, final QueryRequest request,
final QueryResultHandler handler) {
- // List the entries.
final Context c = wrap(context);
+ final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
+
+ // The handler which will be invoked for each LDAP search result.
final SearchResultHandler searchHandler = new SearchResultHandler() {
private final AtomicInteger pendingResourceCount = new AtomicInteger();
private final AtomicReference<ResourceException> pendingResult =
@@ -161,8 +265,8 @@
// TODO: should the resource or the container define the ID
// mapping?
- final String id = entryContainer.getIDFromEntry(entry);
- final String revision = entryContainer.getEtagFromEntry(entry);
+ final String id = getIDFromEntry(entry);
+ final String revision = getEtagFromEntry(entry);
final ResultHandler<Map<String, Object>> mapHandler =
new ResultHandler<Map<String, Object>>() {
@Override
@@ -224,8 +328,8 @@
}
};
- final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
- getLDAPFilter(c, request.getQueryFilter(), new ResultHandler<Filter>() {
+ // The handler which will be invoked once the LDAP filter has been transformed.
+ final ResultHandler<Filter> filterHandler = new ResultHandler<Filter>() {
@Override
public void handleError(final ResourceException error) {
handler.handleError(error);
@@ -234,13 +338,32 @@
@Override
public void handleResult(final Filter ldapFilter) {
// Avoid performing a search if the filter could not be mapped or if it will never match.
- if (ldapFilter == null || ldapFilter == c.getConfig().getFalseFilter()) {
+ if (ldapFilter == null || ldapFilter == c.getConfig().falseFilter()) {
handler.handleResult(new QueryResult());
} else {
- entryContainer.listEntries(c, ldapFilter, ldapAttributes, searchHandler);
+ final String[] tmp = getSearchAttributes(ldapAttributes);
+ final ConnectionCompletionHandler<Result> outerHandler =
+ new ConnectionCompletionHandler<Result>(searchHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final SearchRequestCompletionHandler innerHandler =
+ new SearchRequestCompletionHandler(connection,
+ searchHandler);
+ final SearchRequest request =
+ Requests.newSearchRequest(baseDN,
+ SearchScope.SINGLE_LEVEL, ldapFilter, tmp);
+ connection.searchAsync(request, null, innerHandler);
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
}
}
- });
+ };
+
+ getLDAPFilter(c, request.getQueryFilter(), filterHandler);
}
/**
@@ -250,7 +373,10 @@
public void readInstance(final ServerContext context, final String resourceId,
final ReadRequest request, final ResultHandler<Resource> handler) {
final Context c = wrap(context);
- // @Checkstyle:off
+ final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
+ final String[] tmp = getSearchAttributes(ldapAttributes);
+
+ // The handler which will be invoked for the LDAP search result.
final org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> searchHandler =
new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
@Override
@@ -260,7 +386,7 @@
@Override
public void handleResult(final SearchResultEntry entry) {
- final String revision = entryContainer.getEtagFromEntry(entry);
+ final String revision = getEtagFromEntry(entry);
final ResultHandler<Map<String, Object>> mapHandler =
new ResultHandler<Map<String, Object>>() {
@Override
@@ -279,9 +405,25 @@
attributeMapper.toJSON(c, entry, mapHandler);
}
};
- // @Checkstyle:on
- final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
- entryContainer.readEntry(c, resourceId, ldapAttributes, searchHandler);
+
+ // The handler which will be invoked
+ final ConnectionCompletionHandler<SearchResultEntry> outerHandler =
+ new ConnectionCompletionHandler<SearchResultEntry>(searchHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<SearchResultEntry> innerHandler =
+ new RequestCompletionHandler<SearchResultEntry>(connection,
+ searchHandler);
+ final SearchRequest request =
+ Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter
+ .equality(UUID_ATTRIBUTE, resourceId), tmp);
+ connection.searchSingleEntryAsync(request, innerHandler);
+ }
+
+ };
+
+ factory.getConnectionAsync(outerHandler);
}
/**
@@ -325,6 +467,28 @@
}
/**
+ * Returns the ETag for the provided entry.
+ *
+ * @param entry
+ * The entry.
+ * @return The ETag.
+ */
+ private String getEtagFromEntry(final Entry entry) {
+ return entry.parseAttribute(ETAG_ATTRIBUTE).asString();
+ }
+
+ /**
+ * Returns the resource ID for the provided entry.
+ *
+ * @param entry
+ * The entry.
+ * @return The resource ID.
+ */
+ private String getIDFromEntry(final Entry entry) {
+ return entry.parseAttribute(UUID_ATTRIBUTE).asString();
+ }
+
+ /**
* Determines the set of LDAP attributes to request in an LDAP read (search,
* post-read), based on the provided list of JSON pointers.
*
@@ -369,16 +533,16 @@
final Filter f = i.next();
if (f == null) {
// Filter component did not match any attribute mappers.
- return c.getConfig().getFalseFilter();
- } else if (f == c.getConfig().getFalseFilter()) {
- return c.getConfig().getFalseFilter();
- } else if (f == c.getConfig().getTrueFilter()) {
+ return c.getConfig().falseFilter();
+ } else if (f == c.getConfig().falseFilter()) {
+ return c.getConfig().falseFilter();
+ } else if (f == c.getConfig().trueFilter()) {
i.remove();
}
}
switch (value.size()) {
case 0:
- return c.getConfig().getTrueFilter();
+ return c.getConfig().trueFilter();
case 1:
return value.get(0);
default:
@@ -395,8 +559,8 @@
@Override
public Void visitBooleanLiteralFilter(final ResultHandler<Filter> p,
final boolean value) {
- p.handleResult(value ? c.getConfig().getTrueFilter() : c.getConfig()
- .getFalseFilter());
+ p.handleResult(value ? c.getConfig().trueFilter() : c.getConfig()
+ .falseFilter());
return null;
}
@@ -465,11 +629,11 @@
public Filter apply(final Filter value, final Void p) {
if (value == null) {
// Filter component did not match any attribute mappers.
- return c.getConfig().getTrueFilter();
- } else if (value == c.getConfig().getFalseFilter()) {
- return c.getConfig().getTrueFilter();
- } else if (value == c.getConfig().getTrueFilter()) {
- return c.getConfig().getFalseFilter();
+ return c.getConfig().trueFilter();
+ } else if (value == c.getConfig().falseFilter()) {
+ return c.getConfig().trueFilter();
+ } else if (value == c.getConfig().trueFilter()) {
+ return c.getConfig().falseFilter();
} else {
return Filter.not(value);
}
@@ -494,15 +658,15 @@
if (f == null) {
// Filter component did not match any attribute mappers.
i.remove();
- } else if (f == c.getConfig().getFalseFilter()) {
+ } else if (f == c.getConfig().falseFilter()) {
i.remove();
- } else if (f == c.getConfig().getTrueFilter()) {
- return c.getConfig().getTrueFilter();
+ } else if (f == c.getConfig().trueFilter()) {
+ return c.getConfig().trueFilter();
}
}
switch (value.size()) {
case 0:
- return c.getConfig().getFalseFilter();
+ return c.getConfig().falseFilter();
case 1:
return value.get(0);
default:
@@ -536,7 +700,16 @@
queryFilter.accept(visitor, h);
}
- private Context wrap(ServerContext context) {
+ private String[] getSearchAttributes(final Collection<String> attributes) {
+ // FIXME: who is responsible for adding the UUID and etag attributes to
+ // this search?
+ final String[] tmp = attributes.toArray(new String[attributes.size() + 2]);
+ tmp[tmp.length - 2] = UUID_ATTRIBUTE;
+ tmp[tmp.length - 1] = ETAG_ATTRIBUTE;
+ return tmp;
+ }
+
+ private Context wrap(final ServerContext context) {
return new Context(config, context);
}
}
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
index 9a66c9c..7fa9bac 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -173,7 +173,7 @@
break;
case EXTENDED:
default:
- filter = c.getConfig().getFalseFilter(); // Not supported.
+ filter = c.getConfig().falseFilter(); // Not supported.
break;
}
return filter;
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
index 0ec2721..720ea47 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
@@ -50,50 +50,52 @@
*/
public static void main(final String[] args) throws Exception {
// All LDAP resources will use this connection factory.
- final ConnectionFactory ldapFactory = newAuthenticatedConnectionFactory(
- new LDAPConnectionFactory("localhost", 1389), Requests.newSimpleBindRequest(
- "cn=directory manager", "password".toCharArray()));
-
- // Create two entry containers whose members reference each other.
- final EntryContainer userContainer = new EntryContainer(DN
- .valueOf("ou=people,dc=example,dc=com"), ldapFactory);
- final EntryContainer groupContainer = new EntryContainer(DN
- .valueOf("ou=groups,dc=example,dc=com"), ldapFactory);
+ final ConnectionFactory ldapFactory =
+ newAuthenticatedConnectionFactory(new LDAPConnectionFactory("localhost", 1389),
+ Requests.newSimpleBindRequest("cn=directory manager", "password"
+ .toCharArray()));
// Create user resource.
- final AttributeMapper userMapper = new CompositeAttributeMapper().addMapper(
- new SimpleAttributeMapper("id", "entryUUID").singleValued(true)).addMapper(
- new DefaultAttributeMapper().includeAttribute("uid", "isMemberOf",
- "modifyTimestamp")).addMapper(
- new ComplexAttributeMapper("name", new DefaultAttributeMapper().includeAttribute(
- "cn", "sn", "givenName"))).addMapper(
- new ComplexAttributeMapper("contactInformation", new CompositeAttributeMapper()
- .addMapper(
- new SimpleAttributeMapper("telephoneNumber").decoder(
- Functions.byteStringToString()).singleValued(true))
- .addMapper(
- new SimpleAttributeMapper("emailAddress", "mail")
- .singleValued(true))));
- final LDAPCollectionResourceProvider userResource = new LDAPCollectionResourceProvider(
- userContainer, userMapper);
+ final AttributeMapper userMapper =
+ new CompositeAttributeMapper().addMapper(
+ new SimpleAttributeMapper("id", "entryUUID").singleValued(true)).addMapper(
+ new DefaultAttributeMapper().includeAttribute("uid", "isMemberOf",
+ "modifyTimestamp")).addMapper(
+ new ComplexAttributeMapper("name", new DefaultAttributeMapper()
+ .includeAttribute("cn", "sn", "givenName"))).addMapper(
+ new ComplexAttributeMapper("contactInformation",
+ new CompositeAttributeMapper().addMapper(
+ new SimpleAttributeMapper("telephoneNumber").decoder(
+ Functions.byteStringToString()).singleValued(true))
+ .addMapper(
+ new SimpleAttributeMapper("emailAddress", "mail")
+ .singleValued(true))));
+
+ final LDAPCollectionResourceProvider userResource =
+ new LDAPCollectionResourceProvider(DN.valueOf("ou=people,dc=example,dc=com"),
+ userMapper, ldapFactory, Config.defaultConfig());
// Create group resource.
- final AttributeMapper groupMapper = new DefaultAttributeMapper().includeAttribute("cn",
- "ou", "description", "uniquemember");
- final LDAPCollectionResourceProvider groupResource = new LDAPCollectionResourceProvider(
- groupContainer, groupMapper);
+ final AttributeMapper groupMapper =
+ new DefaultAttributeMapper().includeAttribute("cn", "ou", "description",
+ "uniquemember");
+
+ final LDAPCollectionResourceProvider groupResource =
+ new LDAPCollectionResourceProvider(DN.valueOf("ou=groups,dc=example,dc=com"),
+ groupMapper, ldapFactory, Config.defaultConfig());
// Create the router.
final Router router = new Router();
router.addRoute("/users", userResource);
router.addRoute("/groups", groupResource);
- final org.forgerock.json.resource.ConnectionFactory resourceFactory = newInternalConnectionFactory(router);
+ final org.forgerock.json.resource.ConnectionFactory resourceFactory =
+ newInternalConnectionFactory(router);
final HttpServer httpServer = HttpServer.createSimpleServer("./", PORT);
try {
final WebappContext ctx = new WebappContext("example", "/example");
- final ServletRegistration reg = ctx.addServlet("managed", new HttpServlet(
- resourceFactory));
+ final ServletRegistration reg =
+ ctx.addServlet("managed", new HttpServlet(resourceFactory));
reg.addMapping("/managed/*");
ctx.deploy(httpServer);
--
Gitblit v1.10.0