From bb7775ad525637d45e5192175b9b806dfff69e82 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Fri, 08 Feb 2013 18:23:37 +0000
Subject: [PATCH] Partial fix for OPENDJ-691: Implement add/create support
---
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/JSONConstantAttributeMapper.java | 15 -
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java | 6
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java | 26 ++
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java | 86 ++++++-
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java | 51 ++++
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPConstantAttributeMapper.java | 67 ++++++
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java | 154 ++++++++++---
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java | 177 ++-------------
opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReadOnUpdatePolicy.java | 48 ++++
9 files changed, 420 insertions(+), 210 deletions(-)
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
index 78a6672..a1ad982 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
@@ -99,7 +99,11 @@
@Override
void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
- // TODO Auto-generated method stub
+ if (v.isDefined(jsonAttributeName)) {
+ mapper.toLDAP(c, v.get(jsonAttributeName), h);
+ } else {
+ mapper.toLDAP(c, new JsonValue(Collections.emptyMap()), h);
+ }
}
private boolean matches(final JsonPointer jsonAttribute) {
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 ca359ed..cf77ea8 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
@@ -121,7 +121,31 @@
@Override
void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
- // TODO Auto-generated method stub
+ final ResultHandler<List<Attribute>> handler =
+ accumulate(attributeMappers.size(), transform(
+ new Function<List<List<Attribute>>, List<Attribute>, Void>() {
+ @Override
+ public List<Attribute> apply(final List<List<Attribute>> value,
+ final Void p) {
+ switch (value.size()) {
+ case 0:
+ return Collections.emptyList();
+ case 1:
+ return value.get(0) != null ? value.get(0) : Collections
+ .<Attribute> emptyList();
+ default:
+ List<Attribute> attributes =
+ new ArrayList<Attribute>(value.size());
+ for (List<Attribute> a : value) {
+ attributes.addAll(a);
+ }
+ return attributes;
+ }
+ }
+ }, h));
+ for (final AttributeMapper mapper : attributeMappers) {
+ mapper.toLDAP(c, v, handler);
+ }
}
@SuppressWarnings("unchecked")
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 41f16eb..53076d2 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,165 +15,50 @@
*/
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.DecodeOptions;
import org.forgerock.opendj.ldap.Filter;
+import org.forgerock.opendj.ldap.schema.Schema;
/**
* Common configuration options.
*/
-public final class Config {
-
- /**
- * 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;
- }
- };
-
- /**
- * The policy which should be used in order to read an entry before it is
- * deleted, or after it is added or modified.
- */
- 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 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;
- }
+final class Config {
private final Filter falseFilter;
-
+ private final Schema schema;
private final ReadOnUpdatePolicy readOnUpdatePolicy;
private final Filter trueFilter;
+ private final DecodeOptions options;
- private Config(final Filter trueFilter, final Filter falseFilter,
- final ReadOnUpdatePolicy readOnUpdatePolicy) {
+ Config(final Filter trueFilter, final Filter falseFilter,
+ final ReadOnUpdatePolicy readOnUpdatePolicy, final Schema schema) {
this.trueFilter = trueFilter;
this.falseFilter = falseFilter;
this.readOnUpdatePolicy = readOnUpdatePolicy;
+ this.schema = schema;
+ this.options = new DecodeOptions().setSchema(schema);
+ }
+
+ /**
+ * Returns the schema which should be used when attribute types and
+ * controls.
+ *
+ * @return The schema which should be used when attribute types and
+ * controls.
+ */
+ public Schema schema() {
+ return schema;
+ }
+
+ /**
+ * Returns the decoding options which should be used when decoding controls
+ * in responses.
+ *
+ * @return The decoding options which should be used when decoding controls
+ * in responses.
+ */
+ public DecodeOptions decodeOptions() {
+ return options;
}
/**
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
index 7c8fe43..030055b 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
@@ -20,6 +20,9 @@
import static org.forgerock.opendj.rest2ldap.Utils.toFilter;
import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -27,8 +30,11 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.json.resource.BadRequestException;
import org.forgerock.json.resource.ResultHandler;
import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.AttributeDescription;
+import org.forgerock.opendj.ldap.Attributes;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.Filter;
@@ -126,7 +132,50 @@
@Override
void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
- // TODO:
+ if (v.isMap()) {
+ List<Attribute> result = new ArrayList<Attribute>(v.size());
+ for (Map.Entry<String, Object> field : v.asMap().entrySet()) {
+ final AttributeDescription ad;
+ try {
+ ad = AttributeDescription.valueOf(field.getKey(), c.getConfig().schema());
+ } catch (Exception e) {
+ // FIXME: improve error message.
+ h.handleError(new BadRequestException("The field " + field.getKey()
+ + " is invalid"));
+ return;
+ }
+ Object value = field.getValue();
+ if (isJSONPrimitive(value)) {
+ result.add(Attributes.singletonAttribute(ad, value));
+ } else if (value instanceof Collection<?>) {
+ Attribute a =
+ c.getConfig().decodeOptions().getAttributeFactory().newAttribute(ad);
+ for (Object o : (Collection<?>) value) {
+ if (isJSONPrimitive(o)) {
+ a.add(o);
+ } else {
+ // FIXME: improve error message.
+ h.handleError(new BadRequestException("The field " + field.getKey()
+ + " is invalid"));
+ return;
+ }
+ }
+ result.add(a);
+ } else {
+ // FIXME: improve error message.
+ h.handleError(new BadRequestException("The field " + field.getKey()
+ + " is invalid"));
+ return;
+ }
+ }
+ h.handleResult(result);
+ } else {
+ h.handleResult(Collections.<Attribute> emptyList());
+ }
+ }
+
+ private boolean isJSONPrimitive(Object value) {
+ return value instanceof String || value instanceof Boolean || value instanceof Number;
}
private boolean isIncludedAttribute(final String name) {
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/JSONConstantAttributeMapper.java
similarity index 91%
rename from opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
rename to opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/JSONConstantAttributeMapper.java
index 6484101..fac2ae1 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/JSONConstantAttributeMapper.java
@@ -32,20 +32,11 @@
/**
* An attribute mapper which maps a single JSON attribute to a fixed value.
*/
-final class ConstantAttributeMapper extends AttributeMapper {
+final class JSONConstantAttributeMapper extends AttributeMapper {
private final String jsonAttributeName;
private final Object jsonAttributeValue;
- /**
- * Creates a new constant attribute mapper which maps a single JSON
- * attribute to a fixed value.
- *
- * @param attributeName
- * The name of the simple JSON attribute.
- * @param attributeValue
- * The value of the simple JSON attribute.
- */
- ConstantAttributeMapper(final String attributeName, final Object attributeValue) {
+ JSONConstantAttributeMapper(final String attributeName, final Object attributeValue) {
this.jsonAttributeName = attributeName;
this.jsonAttributeValue = attributeValue;
}
@@ -109,7 +100,7 @@
@Override
void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
- // TODO Auto-generated method stub
+ h.handleResult(Collections.<Attribute>emptyList());
}
private <T extends Comparable<T>> Filter compare(final Context c, final FilterType type,
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 8c4200b..1c6b825 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
@@ -15,10 +15,12 @@
*/
package org.forgerock.opendj.rest2ldap;
+import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.USE_READ_ENTRY_CONTROLS;
import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
import static org.forgerock.opendj.rest2ldap.Utils.transform;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -56,6 +58,8 @@
import org.forgerock.opendj.ldap.ConnectionException;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.DecodeException;
+import org.forgerock.opendj.ldap.Entry;
import org.forgerock.opendj.ldap.EntryNotFoundException;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.Filter;
@@ -64,7 +68,12 @@
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.TimeoutResultException;
+import org.forgerock.opendj.ldap.controls.PostReadRequestControl;
+import org.forgerock.opendj.ldap.controls.PostReadResponseControl;
+import org.forgerock.opendj.ldap.controls.PreReadResponseControl;
import org.forgerock.opendj.ldap.requests.AddRequest;
+import org.forgerock.opendj.ldap.requests.ModifyRequest;
+import org.forgerock.opendj.ldap.requests.Request;
import org.forgerock.opendj.ldap.requests.Requests;
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.Result;
@@ -161,8 +170,8 @@
private final NameStrategy nameStrategy;
LDAPCollectionResourceProvider(final DN baseDN, final AttributeMapper mapper,
- final ConnectionFactory factory, final Config config, final NameStrategy nameStrategy,
- final MVCCStrategy mvccStrategy) {
+ final ConnectionFactory factory, final NameStrategy nameStrategy,
+ final MVCCStrategy mvccStrategy, final Config config) {
this.baseDN = baseDN;
this.attributeMapper = mapper;
this.factory = factory;
@@ -186,25 +195,7 @@
@Override
public void createInstance(final ServerContext context, final CreateRequest request,
final ResultHandler<Resource> handler) {
- // We will support three use-cases:
- //
- // 1) client provided: the RDN is derived from the ID
- // 2) client provided: the RDN is derived from a JSON attribute, the ID maps to a user attribute
- // 3) server provided: the RDN is derived from a JSON attribute
- //
- // Procedure:
- //
- // 1) Generate LDAP attributes and create entry
- // 2) Apply ID mapper: create RDN from entry/ID, store ID in entry
- // 3) Create add request
- // 4) Add post read control if policy rfc
- // 5) Do add request
- // 6) If add failed then return error
- // 7) If policy is rfc then return entry
- // 8) If policy is search then read entry
- //
final Context c = wrap(context);
- final AddRequest addRequest = Requests.newAddRequest(DN.rootDN());
attributeMapper.toLDAP(c, request.getContent(), new ResultHandler<List<Attribute>>() {
@Override
public void handleError(final ResourceException error) {
@@ -213,10 +204,16 @@
@Override
public void handleResult(final List<Attribute> result) {
+ final AddRequest addRequest = Requests.newAddRequest(DN.rootDN());
for (final Attribute attribute : result) {
addRequest.addAttribute(attribute);
}
- nameStrategy.setResourceId(c, baseDN, request.getNewResourceId(), addRequest);
+ nameStrategy.setResourceId(c, getBaseDN(c), request.getNewResourceId(), addRequest);
+ if (config.readOnUpdatePolicy() == USE_READ_ENTRY_CONTROLS) {
+ final String[] attributes = getLDAPAttributes(c, request.getFieldFilters());
+ addRequest.addControl(PostReadRequestControl.newControl(false, attributes));
+ }
+ applyUpdate(c, addRequest, handler);
}
});
}
@@ -374,24 +371,9 @@
@Override
public void handleResult(final SearchResultEntry entry) {
- final String revision = mvccStrategy.getRevisionFromEntry(c, entry);
- final ResultHandler<Map<String, Object>> mapHandler =
- new ResultHandler<Map<String, Object>>() {
- @Override
- public void handleError(final ResourceException e) {
- handler.handleError(e);
- }
-
- @Override
- public void handleResult(final Map<String, Object> result) {
- final Resource resource =
- new Resource(resourceId, revision, new JsonValue(
- result));
- handler.handleResult(resource);
- }
- };
- attributeMapper.toJSON(c, entry, mapHandler);
+ adaptEntry(c, entry, handler);
}
+
};
// The handler which will be invoked
@@ -421,6 +403,27 @@
handler.handleError(new NotSupportedException("Not yet implemented"));
}
+ private void adaptEntry(final Context c, final Entry entry,
+ final ResultHandler<Resource> handler) {
+ final String actualResourceId = nameStrategy.getResourceId(c, entry);
+ final String revision = mvccStrategy.getRevisionFromEntry(c, entry);
+ final ResultHandler<Map<String, Object>> mapHandler =
+ new ResultHandler<Map<String, Object>>() {
+ @Override
+ public void handleError(final ResourceException e) {
+ handler.handleError(e);
+ }
+
+ @Override
+ public void handleResult(final Map<String, Object> result) {
+ final Resource resource =
+ new Resource(actualResourceId, revision, new JsonValue(result));
+ handler.handleResult(resource);
+ }
+ };
+ attributeMapper.toJSON(c, entry, mapHandler);
+ }
+
/**
* Adapts an LDAP result code to a resource exception.
*
@@ -676,4 +679,79 @@
private Context wrap(final ServerContext context) {
return new Context(config, context);
}
+
+ private org.forgerock.opendj.ldap.ResultHandler<Result> postUpdateHandler(final Context c,
+ final ResultHandler<Resource> handler) {
+ // The handler which will be invoked for the LDAP add result.
+ final org.forgerock.opendj.ldap.ResultHandler<Result> resultHandler =
+ new org.forgerock.opendj.ldap.ResultHandler<Result>() {
+ @Override
+ public void handleErrorResult(final ErrorResultException error) {
+ handler.handleError(adaptErrorResult(error));
+ }
+
+ @Override
+ public void handleResult(final Result result) {
+ // FIXME: handle USE_SEARCH policy.
+ Entry entry;
+ try {
+ PostReadResponseControl postReadControl =
+ result.getControl(PostReadResponseControl.DECODER, config
+ .decodeOptions());
+ if (postReadControl != null) {
+ entry = postReadControl.getEntry();
+ } else {
+ PreReadResponseControl preReadControl =
+ result.getControl(PreReadResponseControl.DECODER, config
+ .decodeOptions());
+ if (preReadControl != null) {
+ entry = preReadControl.getEntry();
+ } else {
+ entry = null;
+ }
+ }
+ } catch (DecodeException e) {
+ // FIXME: log something?
+ entry = null;
+ }
+ if (entry != null) {
+ adaptEntry(c, entry, handler);
+ } else {
+ final Resource resource =
+ new Resource(null, null, new JsonValue(Collections.emptyMap()));
+ handler.handleResult(resource);
+ }
+ }
+
+ };
+ return resultHandler;
+ }
+
+ private void applyUpdate(final Context c, final Request request,
+ final ResultHandler<Resource> handler) {
+ final org.forgerock.opendj.ldap.ResultHandler<Result> resultHandler =
+ postUpdateHandler(c, handler);
+ final ConnectionCompletionHandler<Result> outerHandler =
+ new ConnectionCompletionHandler<Result>(resultHandler) {
+
+ @Override
+ public void handleResult(final Connection connection) {
+ final RequestCompletionHandler<Result> innerHandler =
+ new RequestCompletionHandler<Result>(connection, resultHandler);
+ // FIXME: simplify this once we have Connection#applyChange()
+ if (request instanceof AddRequest) {
+ connection.addAsync((AddRequest) request, null, innerHandler);
+ } else if (request instanceof org.forgerock.opendj.ldap.requests.DeleteRequest) {
+ connection.deleteAsync(
+ (org.forgerock.opendj.ldap.requests.DeleteRequest) request,
+ null, innerHandler);
+ } else if (request instanceof ModifyRequest) {
+ connection.modifyAsync((ModifyRequest) request, null, innerHandler);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+ };
+ factory.getConnectionAsync(outerHandler);
+ }
}
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPConstantAttributeMapper.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPConstantAttributeMapper.java
new file mode 100644
index 0000000..87272b6
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPConstantAttributeMapper.java
@@ -0,0 +1,67 @@
+/*
+ * 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 static org.forgerock.opendj.ldap.Attributes.singletonAttribute;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.forgerock.json.fluent.JsonPointer;
+import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.json.resource.ResultHandler;
+import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.AttributeDescription;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
+
+/**
+ * An attribute mapper which maps a single LDAP attribute to a fixed value.
+ */
+final class LDAPConstantAttributeMapper extends AttributeMapper {
+ private final List<Attribute> attributes;
+
+ LDAPConstantAttributeMapper(final AttributeDescription attributeName,
+ final Object attributeValue) {
+ attributes = Collections.singletonList(singletonAttribute(attributeName, attributeValue));
+ }
+
+ @Override
+ void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
+ // Nothing to do.
+ }
+
+ @Override
+ void getLDAPFilter(final Context c, final FilterType type, final JsonPointer jsonAttribute,
+ final String operator, final Object valueAssertion, final ResultHandler<Filter> h) {
+ // This attribute mapper cannot handle the provided filter component.
+ h.handleResult(null);
+ }
+
+ @Override
+ void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
+ h.handleResult(Collections.<String, Object> emptyMap());
+ }
+
+ @Override
+ void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
+ h.handleResult(attributes);
+ }
+
+}
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReadOnUpdatePolicy.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReadOnUpdatePolicy.java
new file mode 100644
index 0000000..1d68304
--- /dev/null
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReadOnUpdatePolicy.java
@@ -0,0 +1,48 @@
+/*
+ * 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 2013 ForgeRock AS.
+ */
+
+package org.forgerock.opendj.rest2ldap;
+
+/**
+ * The policy which should be used in order to read an entry before it is
+ * deleted, or after it is added or modified.
+ */
+public 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;
+}
diff --git a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
index 46ad0f4..8b7583a 100644
--- a/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
+++ b/opendj-sdk/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Rest2LDAP.java
@@ -18,6 +18,7 @@
import static org.forgerock.opendj.ldap.requests.Requests.newSearchRequest;
import static org.forgerock.opendj.ldap.schema.CoreSchema.getEntryUUIDAttributeType;
+import static org.forgerock.opendj.rest2ldap.ReadOnUpdatePolicy.USE_READ_ENTRY_CONTROLS;
import static org.forgerock.opendj.rest2ldap.Utils.ensureNotNull;
import java.util.Arrays;
@@ -52,16 +53,73 @@
*/
public static final class Builder {
private DN baseDN; // TODO: support template variables.
- private Config config = Config.defaultConfig();
private ConnectionFactory factory;
private final List<AttributeMapper> mappers = new LinkedList<AttributeMapper>();
private MVCCStrategy mvccStrategy = mvccUsingEtag();
private NameStrategy nameStrategy = nameByEntryUUID("uid");
+ private Filter falseFilter = Filter.present("1.1");
+ private Schema schema = Schema.getDefaultSchema();
+ private ReadOnUpdatePolicy readOnUpdatePolicy = USE_READ_ENTRY_CONTROLS;
+ private Filter trueFilter = Filter.objectClassPresent();
Builder() {
// No implementation required.
}
+ /**
+ * Sets the schema which should be used when attribute types and
+ * controls.
+ *
+ * @param schema
+ * The schema which should be used when attribute types and
+ * controls.
+ * @return A reference to this builder.
+ */
+ public Builder schema(final Schema schema) {
+ this.schema = ensureNotNull(schema);
+ return this;
+ }
+
+ /**
+ * 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;
+ }
+
public Builder baseDN(final DN dn) {
ensureNotNull(dn);
this.baseDN = dn;
@@ -80,14 +138,9 @@
if (mappers.isEmpty()) {
throw new IllegalStateException("No mappings provided");
}
- return new LDAPCollectionResourceProvider(baseDN, mapOf(mappers), factory, config,
- nameStrategy, mvccStrategy);
- }
-
- public Builder config(final Config config) {
- ensureNotNull(config);
- this.config = config;
- return this;
+ return new LDAPCollectionResourceProvider(baseDN, mapOf(mappers), factory,
+ nameStrategy, mvccStrategy, new Config(trueFilter, falseFilter,
+ readOnUpdatePolicy, schema));
}
public Builder factory(final ConnectionFactory factory) {
@@ -254,8 +307,19 @@
return new ComplexAttributeMapper(jsonAttribute, mapOf(mappers));
}
- public static AttributeMapper mapConstant(final String attribute, final Object attributeValue) {
- return new ConstantAttributeMapper(attribute, attributeValue);
+ public static AttributeMapper mapJSONConstant(final String attribute,
+ final Object attributeValue) {
+ return new JSONConstantAttributeMapper(attribute, attributeValue);
+ }
+
+ public static AttributeMapper mapLDAPConstant(final String attribute,
+ final Object attributeValue) {
+ return mapLDAPConstant(AttributeDescription.valueOf(attribute), attributeValue);
+ }
+
+ public static AttributeMapper mapLDAPConstant(final AttributeDescription attribute,
+ final Object attributeValue) {
+ return new LDAPConstantAttributeMapper(attribute, attributeValue);
}
public static MVCCStrategy mvccUsingAttribute(final AttributeDescription attribute) {
--
Gitblit v1.10.0