From b0a81eda9a0b2717e90dee49af53adc71f7cc3dc Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 31 Jan 2013 18:01:23 +0000
Subject: [PATCH] Fix OPENDJ-690: Rest2LDAP - Implement basic search support
---
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java | 63 ++-
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java | 49 ++
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java | 41 +
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java | 150 +++---
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java | 52 +-
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java | 9
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/FilterType.java | 87 ++++
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java | 45 ++
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java | 97 ++++
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubContainerAttributeMapper.java | 21
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java | 41 +
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java | 149 ++++++-
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java | 50 ++
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java | 319 +++++++++++++--
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java | 22
15 files changed, 945 insertions(+), 250 deletions(-)
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
index b9dd1c5..9240bac 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
@@ -11,7 +11,7 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
@@ -22,9 +22,9 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
/**
* An attribute mapper is responsible for converting JSON values to and from
@@ -40,13 +40,48 @@
* Implementations should only add the names of attributes found in the LDAP
* entry directly associated with the resource.
*
+ * @param c
+ * The context.
* @param jsonAttribute
* The name of the resource attribute requested by the client.
* @param ldapAttributes
* The set into which the required LDAP attribute names should be
* put.
*/
- void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes);
+ void getLDAPAttributes(Context c, JsonPointer jsonAttribute, Set<String> ldapAttributes);
+
+ /**
+ * Transforms the provided REST comparison filter parameters to an LDAP
+ * filter representation, invoking a completion handler once the
+ * transformation has completed.
+ * <p>
+ * If this attribute mapper is not responsible for mapping the provided JSON
+ * attribute then the result handler's {@link ResultHandler#handleResult
+ * handleResult} method must be invoked with the value {@code null}. If this
+ * attribute mapper is responsible for mapping the JSON attribute, but an
+ * error occurred while constructing the LDAP filter, then the result
+ * handler's {@link ResultHandler#handleError handleError} method must be
+ * invoked with an appropriate exception indicating the problem which
+ * occurred.
+ *
+ * @param c
+ * The context.
+ * @param type
+ * The type of REST comparison filter.
+ * @param jsonAttribute
+ * The name of the resource attribute to be filtered.
+ * @param operator
+ * The name of the extended operator to use for the comparison,
+ * or {@code null} if {@code type} is not
+ * {@link FilterType#EXTENDED}.
+ * @param valueAssertion
+ * The value assertion, or {@code null} if {@code type} is
+ * {@link FilterType#PRESENT}.
+ * @param h
+ * The result handler.
+ */
+ void getLDAPFilter(Context c, FilterType type, JsonPointer jsonAttribute, String operator,
+ Object valueAssertion, ResultHandler<Filter> h);
/**
* Transforms attributes contained in the provided LDAP entry to JSON
@@ -58,13 +93,13 @@
* requests.
*
* @param c
- * The server context.
+ * The context.
* @param e
* The LDAP entry to be converted to JSON.
* @param h
* The result handler.
*/
- void toJSON(ServerContext c, Entry e, ResultHandler<Map<String, Object>> h);
+ void toJSON(Context c, Entry e, ResultHandler<Map<String, Object>> h);
/**
* Transforms JSON content in the provided JSON value to LDAP attributes,
@@ -75,16 +110,15 @@
* requests.
*
* @param c
- * The server context.
+ * The context.
* @param v
* The JSON value to be converted to LDAP attributes.
* @param h
* The result handler.
*/
- void toLDAP(ServerContext c, JsonValue v, ResultHandler<List<Attribute>> h);
+ void toLDAP(Context c, JsonValue v, ResultHandler<List<Attribute>> h);
// TODO: methods for obtaining schema information (e.g. name, description,
// type information).
- // TODO: methods for creating filters createLDAPEqualityFilter().
// TODO: methods for creating sort controls.
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
index 84f0fa6..a5e5705 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
@@ -11,7 +11,7 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
@@ -26,9 +26,9 @@
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ResourceException;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
/**
* An attribute mapper which maps a single JSON attribute to the result of
@@ -60,10 +60,11 @@
* {@inheritDoc}
*/
@Override
- public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
- if (attributeMatchesPointer(jsonAttribute)) {
+ public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
+ if (jsonAttribute.isEmpty() || matches(jsonAttribute)) {
final JsonPointer relativePointer = jsonAttribute.relativePointer();
- mapper.getLDAPAttributes(relativePointer, ldapAttributes);
+ mapper.getLDAPAttributes(c, relativePointer, ldapAttributes);
}
}
@@ -71,22 +72,39 @@
* {@inheritDoc}
*/
@Override
- public void toJSON(final ServerContext c, final Entry e,
+ public void getLDAPFilter(final Context c, final FilterType type,
+ final JsonPointer jsonAttribute, final String operator, final Object valueAssertion,
+ final ResultHandler<Filter> h) {
+ if (matches(jsonAttribute)) {
+ final JsonPointer relativePointer = jsonAttribute.relativePointer();
+ mapper.getLDAPFilter(c, type, relativePointer, operator, valueAssertion, h);
+ } else {
+ // This attribute mapper cannot handle the provided filter component.
+ h.handleResult(null);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void toJSON(final Context c, final Entry e,
final ResultHandler<Map<String, Object>> h) {
- final ResultHandler<Map<String, Object>> wrapper = new ResultHandler<Map<String, Object>>() {
+ final ResultHandler<Map<String, Object>> wrapper =
+ new ResultHandler<Map<String, Object>>() {
- @Override
- public void handleError(final ResourceException e) {
- h.handleError(e);
- }
+ @Override
+ public void handleError(final ResourceException e) {
+ h.handleError(e);
+ }
- @Override
- public void handleResult(final Map<String, Object> result) {
- final Map<String, Object> complexResult = Collections.singletonMap(
- jsonAttributeName, (Object) result);
- h.handleResult(complexResult);
- }
- };
+ @Override
+ public void handleResult(final Map<String, Object> result) {
+ final Map<String, Object> complexResult =
+ Collections.singletonMap(jsonAttributeName, (Object) result);
+ h.handleResult(complexResult);
+ }
+ };
mapper.toJSON(c, e, wrapper);
}
@@ -94,15 +112,14 @@
* {@inheritDoc}
*/
@Override
- public void toLDAP(final ServerContext c, final JsonValue v,
+ public void toLDAP(final Context c, final JsonValue v,
final ResultHandler<List<Attribute>> h) {
// TODO Auto-generated method stub
-
}
- private boolean attributeMatchesPointer(final JsonPointer resourceAttribute) {
- return resourceAttribute.isEmpty()
- || toLowerCase(resourceAttribute.get(0)).equals(normalizedJsonAttributeName);
+ private boolean matches(final JsonPointer jsonAttribute) {
+ return !jsonAttribute.isEmpty()
+ && toLowerCase(jsonAttribute.get(0)).equals(normalizedJsonAttributeName);
}
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
index b69fcf8..9d7f367 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
@@ -11,26 +11,29 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
+import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
+import static org.forgerock.opendj.rest2ldap.Utils.transform;
+
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
-import org.forgerock.json.resource.ResourceException;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
+import org.forgerock.opendj.ldap.Function;
/**
* An attribute mapper which combines the results of a set of subordinate
@@ -62,9 +65,10 @@
* {@inheritDoc}
*/
@Override
- public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
+ public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
for (final AttributeMapper attribute : attributeMappers) {
- attribute.getLDAPAttributes(jsonAttribute, ldapAttributes);
+ attribute.getLDAPAttributes(c, jsonAttribute, ldapAttributes);
}
}
@@ -72,49 +76,68 @@
* {@inheritDoc}
*/
@Override
- public void toJSON(final ServerContext c, final Entry e,
- final ResultHandler<Map<String, Object>> h) {
- final ResultHandler<Map<String, Object>> resultAccumulater = new ResultHandler<Map<String, Object>>() {
- private final AtomicInteger latch = new AtomicInteger(attributeMappers.size());
- private final List<Map<String, Object>> results = new ArrayList<Map<String, Object>>(
- latch.get());
+ public void getLDAPFilter(final Context c, final FilterType type,
+ final JsonPointer jsonAttribute, final String operator, final Object valueAssertion,
+ final ResultHandler<Filter> h) {
+ final ResultHandler<Filter> handler =
+ accumulate(attributeMappers.size(), transform(
+ new Function<List<Filter>, Filter, Void>() {
+ @Override
+ public Filter apply(final List<Filter> value, final Void p) {
+ // Remove unmapped filters and combine using logical-OR.
+ final Iterator<Filter> i = value.iterator();
+ while (i.hasNext()) {
+ final Filter f = i.next();
+ 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();
+ }
+ }
+ switch (value.size()) {
+ case 0:
+ // No mappings found.
+ return null;
+ case 1:
+ return value.get(0);
+ default:
+ return Filter.or(value);
+ }
+ }
+ }, h));
+ for (final AttributeMapper subMapper : attributeMappers) {
+ subMapper.getLDAPFilter(c, type, jsonAttribute, operator, valueAssertion, handler);
+ }
+ }
- @Override
- public void handleError(final ResourceException e) {
- // Ensure that handler is only invoked once.
- if (latch.getAndSet(0) > 0) {
- h.handleError(e);
- }
- }
-
- @Override
- public void handleResult(final Map<String, Object> result) {
- if (result != null && !result.isEmpty()) {
- synchronized (this) {
- results.add(result);
- }
- }
- if (latch.decrementAndGet() == 0) {
- final Map<String, Object> mergeResult;
- switch (results.size()) {
- case 0:
- mergeResult = Collections.<String, Object> emptyMap();
- break;
- case 1:
- mergeResult = results.get(0);
- break;
- default:
- mergeResult = new LinkedHashMap<String, Object>();
- mergeJsonValues(results, mergeResult);
- break;
- }
- h.handleResult(mergeResult);
- }
- }
- };
-
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
+ final ResultHandler<Map<String, Object>> handler =
+ accumulate(attributeMappers.size(), transform(
+ new Function<List<Map<String, Object>>, Map<String, Object>, Void>() {
+ @Override
+ public Map<String, Object> apply(final List<Map<String, Object>> value,
+ final Void p) {
+ switch (value.size()) {
+ case 0:
+ return Collections.<String, Object> emptyMap();
+ case 1:
+ return value.get(0) != null ? value.get(0) : Collections
+ .<String, Object> emptyMap();
+ default:
+ return mergeJsonValues(value,
+ new LinkedHashMap<String, Object>());
+ }
+ }
+ }, h));
for (final AttributeMapper mapper : attributeMappers) {
- mapper.toJSON(c, e, resultAccumulater);
+ mapper.toJSON(c, e, handler);
}
}
@@ -122,21 +145,10 @@
* {@inheritDoc}
*/
@Override
- public void toLDAP(final ServerContext c, final JsonValue v,
- final ResultHandler<List<Attribute>> h) {
+ public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
// TODO Auto-generated method stub
-
}
- /**
- * Merge one JSON value into another.
- *
- * @param srcValue
- * The source value.
- * @param dstValue
- * The destination value, into which which the value should be
- * merged.
- */
@SuppressWarnings("unchecked")
private void mergeJsonValue(final Map<String, Object> srcValue,
final Map<String, Object> dstValue) {
@@ -150,8 +162,8 @@
} else if ((existingValue instanceof Map) && (newValue instanceof Map)) {
// Merge two maps - create a new Map, in case the existing one
// is unmodifiable.
- existingValue = new LinkedHashMap<String, Object>(
- (Map<String, Object>) existingValue);
+ existingValue =
+ new LinkedHashMap<String, Object>((Map<String, Object>) existingValue);
mergeJsonValue((Map<String, Object>) newValue, (Map<String, Object>) existingValue);
} else if ((existingValue instanceof List) && (newValue instanceof List)) {
// Merge two lists- create a new List, in case the existing one
@@ -166,19 +178,13 @@
}
}
- /**
- * Merge the provided list of JSON values into a single value.
- *
- * @param srcValues
- * The source values.
- * @param dstValue
- * The destination value, into which which the values should be
- * merged.
- */
- private void mergeJsonValues(final List<Map<String, Object>> srcValues,
+ private Map<String, Object> mergeJsonValues(final List<Map<String, Object>> srcValues,
final Map<String, Object> dstValue) {
for (final Map<String, Object> value : srcValues) {
- mergeJsonValue(value, dstValue);
+ if (value != null) {
+ mergeJsonValue(value, dstValue);
+ }
}
+ return dstValue;
}
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
new file mode 100644
index 0000000..b36b195
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Config.java
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import org.forgerock.opendj.ldap.Filter;
+
+/**
+ * Common configuration options.
+ */
+public final class Config {
+
+ private final Filter trueFilter = Filter.objectClassPresent();
+ private final Filter falseFilter = Filter.present("1.1");
+
+ /**
+ * Returns the absolute true filter.
+ *
+ * @return The absolute true filter.
+ */
+ public Filter getTrueFilter() {
+ return trueFilter;
+ }
+
+ /**
+ * Returns the absolute false filter.
+ *
+ * @return The absolute false filter.
+ */
+ public Filter getFalseFilter() {
+ return falseFilter;
+ }
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
index 1468b9c..5fc4644 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
@@ -11,10 +11,12 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
+import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
+
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -23,15 +25,14 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
/**
* An attribute mapper which maps a single JSON attribute to a fixed value.
*/
public class ConstantAttributeMapper implements AttributeMapper {
-
private final String jsonAttributeName;
private final Object jsonAttributeValue;
@@ -53,7 +54,8 @@
* {@inheritDoc}
*/
@Override
- public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
+ public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
// Nothing to do.
}
@@ -61,8 +63,55 @@
* {@inheritDoc}
*/
@Override
- public void toJSON(final ServerContext c, final Entry e,
- final ResultHandler<Map<String, Object>> h) {
+ public void getLDAPFilter(final Context c, final FilterType type,
+ final JsonPointer jsonAttribute, final String operator, final Object valueAssertion,
+ final ResultHandler<Filter> h) {
+ if (jsonAttribute.size() == 1 && jsonAttribute.get(0).equalsIgnoreCase(jsonAttributeName)) {
+ final Filter filter;
+ if (type == FilterType.PRESENT) {
+ filter = c.getConfig().getTrueFilter();
+ } 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();
+ break;
+ case STARTS_WITH:
+ filter =
+ v1.startsWith(v2) ? c.getConfig().getTrueFilter() : c.getConfig()
+ .getFalseFilter();
+ break;
+ default:
+ filter = compare(c, type, v1, v2);
+ break;
+ }
+ } else if (jsonAttributeValue instanceof Number && valueAssertion instanceof Number) {
+ final Double v1 = ((Number) jsonAttributeValue).doubleValue();
+ final Double v2 = ((Number) valueAssertion).doubleValue();
+ filter = compare(c, type, v1, v2);
+ } else if (jsonAttributeValue instanceof Boolean && valueAssertion instanceof Boolean) {
+ final Boolean v1 = (Boolean) jsonAttributeValue;
+ final Boolean v2 = (Boolean) valueAssertion;
+ filter = compare(c, type, v1, v2);
+ } else {
+ // This attribute mapper is a candidate but it does not match.
+ filter = c.getConfig().getFalseFilter();
+ }
+ h.handleResult(filter);
+ } else {
+ // This attribute mapper cannot handle the provided filter component.
+ h.handleResult(null);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
// FIXME: how do we know if the user requested it???
h.handleResult(Collections.singletonMap(jsonAttributeName, jsonAttributeValue));
@@ -72,10 +121,42 @@
* {@inheritDoc}
*/
@Override
- public void toLDAP(final ServerContext c, final JsonValue v,
- final ResultHandler<List<Attribute>> h) {
+ public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
// TODO Auto-generated method stub
+ }
+ private <T extends Comparable<T>> Filter compare(Context c, final FilterType type, final T v1,
+ final T v2) {
+ final Filter filter;
+ switch (type) {
+ case EQUAL_TO:
+ filter = v1.equals(v2) ? c.getConfig().getTrueFilter() : c.getConfig().getFalseFilter();
+ break;
+ case GREATER_THAN:
+ filter =
+ v1.compareTo(v2) > 0 ? c.getConfig().getTrueFilter() : c.getConfig()
+ .getFalseFilter();
+ break;
+ case GREATER_THAN_OR_EQUAL_TO:
+ filter =
+ v1.compareTo(v2) >= 0 ? c.getConfig().getTrueFilter() : c.getConfig()
+ .getFalseFilter();
+ break;
+ case LESS_THAN:
+ filter =
+ v1.compareTo(v2) < 0 ? c.getConfig().getTrueFilter() : c.getConfig()
+ .getFalseFilter();
+ break;
+ case LESS_THAN_OR_EQUAL_TO:
+ filter =
+ v1.compareTo(v2) <= 0 ? c.getConfig().getTrueFilter() : c.getConfig()
+ .getFalseFilter();
+ break;
+ default:
+ filter = c.getConfig().getFalseFilter(); // Not supported.
+ break;
+ }
+ return filter;
}
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
new file mode 100644
index 0000000..5faf14f
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Context.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import org.forgerock.json.resource.ServerContext;
+
+/**
+ * Common context information passed to containers and mappers.
+ */
+public final class Context {
+ private final Config config;
+ private final ServerContext context;
+
+ Context(Config config, ServerContext context) {
+ this.config = config;
+ this.context = context;
+ }
+
+ /**
+ * Returns the common configuration options.
+ *
+ * @return The common configuration options.
+ */
+ public Config getConfig() {
+ return config;
+ }
+
+ /**
+ * Returns the commons REST server context passed in with the REST request.
+ *
+ * @return The commons REST server context passed in with the REST request.
+ */
+ public ServerContext getServerContext() {
+ return context;
+ }
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
index a0d8abb..8314c1b 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
@@ -11,12 +11,13 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
import static org.forgerock.opendj.rest2ldap.Utils.attributeToJson;
import static org.forgerock.opendj.rest2ldap.Utils.getAttributeName;
+import static org.forgerock.opendj.rest2ldap.Utils.toFilter;
import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
import java.util.LinkedHashMap;
@@ -27,18 +28,17 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
/**
* An attribute mapper that directly maps a configurable selection of attributes
* to and from LDAP without any transformation.
*/
public final class DefaultAttributeMapper implements AttributeMapper {
-
- private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>();
// All user attributes by default.
+ private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>();
private final Map<String, String> includedAttributes = new LinkedHashMap<String, String>();
/**
@@ -63,8 +63,12 @@
return this;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
+ public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
switch (jsonAttribute.size()) {
case 0:
// Requested everything.
@@ -85,6 +89,21 @@
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void getLDAPFilter(final Context c, final FilterType type,
+ final JsonPointer jsonAttribute, final String operator, final Object valueAssertion,
+ final ResultHandler<Filter> h) {
+ if (jsonAttribute.size() == 1 && isIncludedAttribute(jsonAttribute.get(0))) {
+ h.handleResult(toFilter(c, type, jsonAttribute.get(0), valueAssertion));
+ } else {
+ // This attribute mapper cannot handle the provided filter component.
+ h.handleResult(null);
+ }
+ }
+
+ /**
* Includes one or more LDAP attributes in this mapping.
*
* @param attributes
@@ -98,9 +117,11 @@
return this;
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void toJSON(final ServerContext c, final Entry e,
- final ResultHandler<Map<String, Object>> h) {
+ public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
final Map<String, Object> result = new LinkedHashMap<String, Object>(e.getAttributeCount());
for (final Attribute a : e.getAllAttributes()) {
final String name = getAttributeName(a);
@@ -111,9 +132,11 @@
h.handleResult(result);
}
+ /**
+ * {@inheritDoc}
+ */
@Override
- public void toLDAP(final ServerContext c, final JsonValue v,
- final ResultHandler<List<Attribute>> h) {
+ public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
// TODO:
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
index e318c3a..f6916de 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
@@ -11,13 +11,12 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
import java.util.Collection;
-import org.forgerock.json.resource.Context;
import org.forgerock.opendj.ldap.Connection;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.DN;
@@ -119,6 +118,7 @@
// 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;
@@ -164,27 +164,30 @@
*
* @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 Collection<String> attributes,
- final SearchResultHandler 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) {
+ 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.objectClassPresent(), tmp);
- connection.searchAsync(request, null, innerHandler);
- }
+ @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);
}
@@ -208,16 +211,17 @@
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);
- }
+ @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);
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/FilterType.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/FilterType.java
new file mode 100644
index 0000000..83fe8a2
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/FilterType.java
@@ -0,0 +1,87 @@
+/*
+ * 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;
+
+import org.forgerock.json.resource.QueryFilter;
+
+/**
+ * An enumeration of the commons REST query comparison filter types.
+ */
+public enum FilterType {
+
+ /**
+ * Substring filter.
+ *
+ * @see QueryFilter#contains
+ */
+ CONTAINS,
+
+ /**
+ * Equality filter.
+ *
+ * @see QueryFilter#equalTo
+ */
+ EQUAL_TO,
+
+ /**
+ * Extended match filter.
+ *
+ * @see QueryFilter#comparisonFilter
+ */
+ EXTENDED,
+
+ /**
+ * Greater than ordering filter.
+ *
+ * @see QueryFilter#greaterThan
+ */
+ GREATER_THAN,
+
+ /**
+ * Greater than or equal to ordering filter.
+ *
+ * @see QueryFilter#greaterThanOrEqualTo
+ */
+ GREATER_THAN_OR_EQUAL_TO,
+
+ /**
+ * Less than ordering filter.
+ *
+ * @see QueryFilter#lessThan
+ */
+ LESS_THAN,
+
+ /**
+ * Less than or equal to ordering filter.
+ *
+ * @see QueryFilter#lessThanOrEqualTo
+ */
+ LESS_THAN_OR_EQUAL_TO,
+
+ /**
+ * Presence filter.
+ *
+ * @see QueryFilter#present
+ */
+ PRESENT,
+
+ /**
+ * Initial sub-string filter.
+ *
+ * @see QueryFilter#startsWith
+ */
+ STARTS_WITH;
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
index 0ff5003..5ef4c76 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
@@ -11,12 +11,17 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
+import static org.forgerock.opendj.rest2ldap.Utils.accumulate;
+import static org.forgerock.opendj.rest2ldap.Utils.transform;
+
import java.util.Collection;
+import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -31,6 +36,8 @@
import org.forgerock.json.resource.DeleteRequest;
import org.forgerock.json.resource.NotSupportedException;
import org.forgerock.json.resource.PatchRequest;
+import org.forgerock.json.resource.QueryFilter;
+import org.forgerock.json.resource.QueryFilterVisitor;
import org.forgerock.json.resource.QueryRequest;
import org.forgerock.json.resource.QueryResult;
import org.forgerock.json.resource.QueryResultHandler;
@@ -47,6 +54,8 @@
import org.forgerock.opendj.ldap.ConnectionException;
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.TimeoutResultException;
@@ -62,8 +71,8 @@
// Dummy exception used for signalling search success.
private static final ResourceException SUCCESS = new UncategorizedException(0, null, null);
private final AttributeMapper attributeMapper;
-
private final EntryContainer entryContainer;
+ private final Config config = new Config();
/**
* Creates a new LDAP resource.
@@ -131,13 +140,12 @@
@Override
public void queryCollection(final ServerContext context, final QueryRequest request,
final QueryResultHandler handler) {
- final Set<JsonPointer> requestedAttributes = new LinkedHashSet<JsonPointer>();
- final Collection<String> requestedLDAPAttributes = getRequestedLDAPAttributes(requestedAttributes);
-
// List the entries.
+ final Context c = wrap(context);
final SearchResultHandler searchHandler = new SearchResultHandler() {
private final AtomicInteger pendingResourceCount = new AtomicInteger();
- private final AtomicReference<ResourceException> pendingResult = new AtomicReference<ResourceException>();
+ private final AtomicReference<ResourceException> pendingResult =
+ new AtomicReference<ResourceException>();
private final AtomicBoolean resultSent = new AtomicBoolean();
@Override
@@ -155,25 +163,27 @@
// mapping?
final String id = entryContainer.getIDFromEntry(entry);
final String revision = entryContainer.getEtagFromEntry(entry);
- final ResultHandler<Map<String, Object>> mapHandler = new ResultHandler<Map<String, Object>>() {
- @Override
- public void handleError(final ResourceException e) {
- pendingResult.compareAndSet(null, e);
- pendingResourceCount.decrementAndGet();
- completeIfNecessary();
- }
+ final ResultHandler<Map<String, Object>> mapHandler =
+ new ResultHandler<Map<String, Object>>() {
+ @Override
+ public void handleError(final ResourceException e) {
+ pendingResult.compareAndSet(null, e);
+ pendingResourceCount.decrementAndGet();
+ completeIfNecessary();
+ }
- @Override
- public void handleResult(final Map<String, Object> result) {
- final Resource resource = new Resource(id, revision, new JsonValue(result));
- handler.handleResource(resource);
- pendingResourceCount.decrementAndGet();
- completeIfNecessary();
- }
- };
+ @Override
+ public void handleResult(final Map<String, Object> result) {
+ final Resource resource =
+ new Resource(id, revision, new JsonValue(result));
+ handler.handleResource(resource);
+ pendingResourceCount.decrementAndGet();
+ completeIfNecessary();
+ }
+ };
pendingResourceCount.incrementAndGet();
- attributeMapper.toJSON(context, entry, mapHandler);
+ attributeMapper.toJSON(c, entry, mapHandler);
return true;
}
@@ -213,7 +223,24 @@
}
}
};
- entryContainer.listEntries(context, requestedLDAPAttributes, searchHandler);
+
+ final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
+ getLDAPFilter(c, request.getQueryFilter(), new ResultHandler<Filter>() {
+ @Override
+ public void handleError(final ResourceException error) {
+ handler.handleError(error);
+ }
+
+ @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()) {
+ handler.handleResult(new QueryResult());
+ } else {
+ entryContainer.listEntries(c, ldapFilter, ldapAttributes, searchHandler);
+ }
+ }
+ });
}
/**
@@ -222,39 +249,39 @@
@Override
public void readInstance(final ServerContext context, final String resourceId,
final ReadRequest request, final ResultHandler<Resource> handler) {
- // TODO: Determine the set of LDAP attributes that need to be read.
- final Set<JsonPointer> requestedAttributes = new LinkedHashSet<JsonPointer>();
- final Collection<String> requestedLDAPAttributes = getRequestedLDAPAttributes(requestedAttributes);
-
+ final Context c = wrap(context);
// @Checkstyle:off
final org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> searchHandler =
new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
- @Override
- public void handleErrorResult(final ErrorResultException error) {
- handler.handleError(adaptErrorResult(error));
- }
-
- @Override
- public void handleResult(final SearchResultEntry entry) {
- final String revision = entryContainer.getEtagFromEntry(entry);
- final ResultHandler<Map<String, Object>> mapHandler = new ResultHandler<Map<String, Object>>() {
@Override
- public void handleError(final ResourceException e) {
- handler.handleError(e);
+ public void handleErrorResult(final ErrorResultException error) {
+ handler.handleError(adaptErrorResult(error));
}
@Override
- public void handleResult(final Map<String, Object> result) {
- final Resource resource = new Resource(resourceId, revision, new JsonValue(
- result));
- handler.handleResult(resource);
+ public void handleResult(final SearchResultEntry entry) {
+ final String revision = entryContainer.getEtagFromEntry(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);
}
};
- attributeMapper.toJSON(context, entry, mapHandler);
- }
- };
// @Checkstyle:on
- entryContainer.readEntry(context, resourceId, requestedLDAPAttributes, searchHandler);
+ final Collection<String> ldapAttributes = getLDAPAttributes(c, request.getFieldFilters());
+ entryContainer.readEntry(c, resourceId, ldapAttributes, searchHandler);
}
/**
@@ -299,27 +326,217 @@
/**
* Determines the set of LDAP attributes to request in an LDAP read (search,
- * post-read), based on the provided set of JSON pointers.
+ * post-read), based on the provided list of JSON pointers.
*
* @param requestedAttributes
- * The set of resource attributes to be read.
+ * The list of resource attributes to be read.
* @return The set of LDAP attributes associated with the resource
* attributes.
*/
- private Collection<String> getRequestedLDAPAttributes(final Set<JsonPointer> requestedAttributes) {
+ private Collection<String> getLDAPAttributes(final Context c,
+ final Collection<JsonPointer> requestedAttributes) {
final Set<String> requestedLDAPAttributes;
if (requestedAttributes.isEmpty()) {
// Full read.
requestedLDAPAttributes = new LinkedHashSet<String>();
- attributeMapper.getLDAPAttributes(new JsonPointer(), requestedLDAPAttributes);
+ attributeMapper.getLDAPAttributes(c, new JsonPointer(), requestedLDAPAttributes);
} else {
// Partial read.
requestedLDAPAttributes = new LinkedHashSet<String>(requestedAttributes.size());
for (final JsonPointer requestedAttribute : requestedAttributes) {
- attributeMapper.getLDAPAttributes(requestedAttribute, requestedLDAPAttributes);
+ attributeMapper.getLDAPAttributes(c, requestedAttribute, requestedLDAPAttributes);
}
}
return requestedLDAPAttributes;
}
+ private void getLDAPFilter(final Context c, final QueryFilter queryFilter,
+ final ResultHandler<Filter> h) {
+ final QueryFilterVisitor<Void, ResultHandler<Filter>> visitor =
+ new QueryFilterVisitor<Void, ResultHandler<Filter>>() {
+ @Override
+ public Void visitAndFilter(final ResultHandler<Filter> p,
+ final List<QueryFilter> subFilters) {
+ final ResultHandler<Filter> handler =
+ accumulate(subFilters.size(), transform(
+ new Function<List<Filter>, Filter, Void>() {
+ @Override
+ public Filter apply(final List<Filter> value,
+ final Void p) {
+ // Check for unmapped filter components and optimize.
+ final Iterator<Filter> i = value.iterator();
+ while (i.hasNext()) {
+ 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()) {
+ i.remove();
+ }
+ }
+ switch (value.size()) {
+ case 0:
+ return c.getConfig().getTrueFilter();
+ case 1:
+ return value.get(0);
+ default:
+ return Filter.and(value);
+ }
+ }
+ }, p));
+ for (final QueryFilter subFilter : subFilters) {
+ subFilter.accept(this, handler);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitBooleanLiteralFilter(final ResultHandler<Filter> p,
+ final boolean value) {
+ p.handleResult(value ? c.getConfig().getTrueFilter() : c.getConfig()
+ .getFalseFilter());
+ return null;
+ }
+
+ @Override
+ public Void visitContainsFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.CONTAINS, field, null,
+ valueAssertion, p);
+ return null;
+ }
+
+ @Override
+ public Void visitEqualsFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.EQUAL_TO, field, null,
+ valueAssertion, p);
+ return null;
+ }
+
+ @Override
+ public Void visitExtendedMatchFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final String operator,
+ final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.EXTENDED, field, operator,
+ valueAssertion, p);
+ return null;
+ }
+
+ @Override
+ public Void visitGreaterThanFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.GREATER_THAN, field, null,
+ valueAssertion, p);
+ return null;
+ }
+
+ @Override
+ public Void visitGreaterThanOrEqualToFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.GREATER_THAN_OR_EQUAL_TO,
+ field, null, valueAssertion, p);
+ return null;
+ }
+
+ @Override
+ public Void visitLessThanFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.LESS_THAN, field, null,
+ valueAssertion, p);
+ return null;
+ }
+
+ @Override
+ public Void visitLessThanOrEqualToFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.LESS_THAN_OR_EQUAL_TO, field,
+ null, valueAssertion, p);
+ return null;
+ }
+
+ @Override
+ public Void visitNotFilter(final ResultHandler<Filter> p,
+ final QueryFilter subFilter) {
+ subFilter.accept(this, transform(new Function<Filter, Filter, Void>() {
+ @Override
+ 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();
+ } else {
+ return Filter.not(value);
+ }
+ }
+ }, p));
+ return null;
+ }
+
+ @Override
+ public Void visitOrFilter(final ResultHandler<Filter> p,
+ final List<QueryFilter> subFilters) {
+ final ResultHandler<Filter> handler =
+ accumulate(subFilters.size(), transform(
+ new Function<List<Filter>, Filter, Void>() {
+ @Override
+ public Filter apply(final List<Filter> value,
+ final Void p) {
+ // Check for unmapped filter components and optimize.
+ final Iterator<Filter> i = value.iterator();
+ while (i.hasNext()) {
+ final Filter f = i.next();
+ if (f == null) {
+ // Filter component did not match any attribute mappers.
+ i.remove();
+ } else if (f == c.getConfig().getFalseFilter()) {
+ i.remove();
+ } else if (f == c.getConfig().getTrueFilter()) {
+ return c.getConfig().getTrueFilter();
+ }
+ }
+ switch (value.size()) {
+ case 0:
+ return c.getConfig().getFalseFilter();
+ case 1:
+ return value.get(0);
+ default:
+ return Filter.or(value);
+ }
+ }
+ }, p));
+ for (final QueryFilter subFilter : subFilters) {
+ subFilter.accept(this, handler);
+ }
+ return null;
+ }
+
+ @Override
+ public Void visitPresentFilter(final ResultHandler<Filter> p,
+ final JsonPointer field) {
+ attributeMapper.getLDAPFilter(c, FilterType.PRESENT, field, null, null, p);
+ return null;
+ }
+
+ @Override
+ public Void visitStartsWithFilter(final ResultHandler<Filter> p,
+ final JsonPointer field, final Object valueAssertion) {
+ attributeMapper.getLDAPFilter(c, FilterType.STARTS_WITH, field, null,
+ valueAssertion, p);
+ return null;
+ }
+
+ };
+ // Note that the returned LDAP filter may be null if it could not be mapped by any attribute mappers.
+ queryFilter.accept(visitor, h);
+ }
+
+ private Context wrap(ServerContext context) {
+ return new Context(config, context);
+ }
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
index 8ed53fc..645c14c 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
@@ -11,7 +11,7 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
@@ -22,9 +22,9 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
/**
* An attribute mapper which maps DN valued LDAP attributes to resource IDs.
@@ -37,7 +37,16 @@
* {@inheritDoc}
*/
@Override
- public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
+ public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void getLDAPFilter(final Context c, final FilterType type,
+ final JsonPointer jsonAttribute, final String operator, final Object valueAssertion,
+ final ResultHandler<Filter> h) {
// TODO Auto-generated method stub
}
@@ -46,8 +55,7 @@
* {@inheritDoc}
*/
@Override
- public void toJSON(final ServerContext c, final Entry e,
- final ResultHandler<Map<String, Object>> h) {
+ public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
// TODO Auto-generated method stub
}
@@ -56,10 +64,8 @@
* {@inheritDoc}
*/
@Override
- public void toLDAP(final ServerContext c, final JsonValue v,
- final ResultHandler<List<Attribute>> h) {
+ public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
// TODO Auto-generated method stub
-
}
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
index 74ba121..8d6a1c5 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
@@ -11,11 +11,12 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
import static org.forgerock.opendj.rest2ldap.Utils.byteStringToJson;
+import static org.forgerock.opendj.rest2ldap.Utils.toFilter;
import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
import java.util.Collections;
@@ -26,10 +27,10 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.Function;
import org.forgerock.opendj.ldap.Functions;
@@ -104,13 +105,29 @@
* {@inheritDoc}
*/
@Override
- public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
- if (attributeMatchesPointer(jsonAttribute)) {
+ public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
+ if (jsonAttribute.isEmpty() || matches(jsonAttribute)) {
ldapAttributes.add(ldapAttributeName);
}
}
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public void getLDAPFilter(final Context c, final FilterType type,
+ final JsonPointer jsonAttribute, final String operator, final Object valueAssertion,
+ final ResultHandler<Filter> h) {
+ if (matches(jsonAttribute)) {
+ h.handleResult(toFilter(c, type, ldapAttributeName, valueAssertion));
+ } else {
+ // This attribute mapper cannot handle the provided filter component.
+ h.handleResult(null);
+ }
+ }
+
+ /**
* Prevents the LDAP attribute from being updated.
*
* @param readOnly
@@ -141,12 +158,11 @@
* {@inheritDoc}
*/
@Override
- public void toJSON(final ServerContext c, final Entry e,
- final ResultHandler<Map<String, Object>> h) {
+ public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
final Attribute a = e.getAttribute(ldapAttributeName);
if (a != null) {
- final Function<ByteString, ?, Void> f = decoder == null ? Functions.fixedFunction(
- byteStringToJson(), a) : decoder;
+ final Function<ByteString, ?, Void> f =
+ decoder == null ? Functions.fixedFunction(byteStringToJson(), a) : decoder;
final Object value;
if (forceSingleValued || a.getAttributeDescription().getAttributeType().isSingleValue()) {
value = a.parse().as(f, defaultValue);
@@ -163,15 +179,14 @@
* {@inheritDoc}
*/
@Override
- public void toLDAP(final ServerContext c, final JsonValue v,
- final ResultHandler<List<Attribute>> h) {
+ public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
// TODO Auto-generated method stub
}
- private boolean attributeMatchesPointer(final JsonPointer resourceAttribute) {
- return resourceAttribute.isEmpty()
- || toLowerCase(resourceAttribute.get(0)).equals(normalizedJsonAttributeName);
+ private boolean matches(final JsonPointer jsonAttribute) {
+ return !jsonAttribute.isEmpty()
+ && toLowerCase(jsonAttribute.get(0)).equals(normalizedJsonAttributeName);
}
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubContainerAttributeMapper.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubContainerAttributeMapper.java
index d5591aa..5b5de74 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubContainerAttributeMapper.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubContainerAttributeMapper.java
@@ -11,7 +11,7 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
@@ -22,9 +22,9 @@
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.ResultHandler;
-import org.forgerock.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.opendj.ldap.Filter;
/**
* An attribute mapper which inlines LDAP attributes from subordinate LDAP
@@ -38,7 +38,16 @@
* {@inheritDoc}
*/
@Override
- public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
+ public void getLDAPAttributes(final Context c, final JsonPointer jsonAttribute,
+ final Set<String> ldapAttributes) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void getLDAPFilter(final Context c, final FilterType type,
+ final JsonPointer jsonAttribute, final String operator, final Object valueAssertion,
+ final ResultHandler<Filter> h) {
// TODO Auto-generated method stub
}
@@ -47,8 +56,7 @@
* {@inheritDoc}
*/
@Override
- public void toJSON(final ServerContext c, final Entry e,
- final ResultHandler<Map<String, Object>> h) {
+ public void toJSON(final Context c, final Entry e, final ResultHandler<Map<String, Object>> h) {
// TODO Auto-generated method stub
}
@@ -57,8 +65,7 @@
* {@inheritDoc}
*/
@Override
- public void toLDAP(final ServerContext c, final JsonValue v,
- final ResultHandler<List<Attribute>> h) {
+ public void toLDAP(final Context c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
// TODO Auto-generated method stub
}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
index 9d83322..9a66c9c 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -11,7 +11,7 @@
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright 2012 ForgeRock AS.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
@@ -21,13 +21,18 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.xml.bind.DatatypeConverter;
+import org.forgerock.json.resource.ResourceException;
+import org.forgerock.json.resource.ResultHandler;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.Filter;
import org.forgerock.opendj.ldap.Function;
import org.forgerock.opendj.ldap.Functions;
import org.forgerock.opendj.ldap.schema.Syntax;
@@ -36,37 +41,85 @@
* Internal utility methods.
*/
final class Utils {
+ /**
+ * Implementation class for {@link #accumulate}.
+ *
+ * @param <V>
+ * The type of result.
+ */
+ private static final class AccumulatingResultHandler<V> implements ResultHandler<V> {
+ private final ResultHandler<List<V>> handler;
+ private final AtomicInteger latch;
+ private final List<V> results;
+
+ private AccumulatingResultHandler(final int size, final ResultHandler<List<V>> handler) {
+ this.latch = new AtomicInteger(size);
+ this.results = new ArrayList<V>(size);
+ this.handler = handler;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleError(final ResourceException e) {
+ // Ensure that handler is only invoked once.
+ if (latch.getAndSet(0) > 0) {
+ handler.handleError(e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleResult(final V result) {
+ synchronized (results) {
+ results.add(result);
+ }
+ if (latch.decrementAndGet() == 0) {
+ handler.handleResult(results);
+ }
+ }
+
+ }
// @Checkstyle:off
private static final Function<ByteString, Object, Attribute> BYTESTRING_TO_JSON =
new Function<ByteString, Object, Attribute>() {
- @Override
- public Object apply(final ByteString value, final Attribute a) {
- final Syntax syntax = a.getAttributeDescription().getAttributeType().getSyntax();
- if (syntax.equals(getBooleanSyntax())) {
- return Functions.byteStringToBoolean().apply(value, null);
- } else if (syntax.equals(getIntegerSyntax())) {
- return Functions.byteStringToLong().apply(value, null);
- } else if (syntax.equals(getGeneralizedTimeSyntax())) {
- return DatatypeConverter.printDateTime(Functions.byteStringToGeneralizedTime()
- .apply(value, null).toCalendar());
- } else if (syntax.isHumanReadable()) {
- return Functions.byteStringToString().apply(value, null);
- } else {
- // Base 64 encoded binary.
- return DatatypeConverter.printBase64Binary(value.toByteArray());
- }
- }
- };
- // @Checkstyle:on
+ @Override
+ public Object apply(final ByteString value, final Attribute a) {
+ final Syntax syntax =
+ a.getAttributeDescription().getAttributeType().getSyntax();
+ if (syntax.equals(getBooleanSyntax())) {
+ return Functions.byteStringToBoolean().apply(value, null);
+ } else if (syntax.equals(getIntegerSyntax())) {
+ return Functions.byteStringToLong().apply(value, null);
+ } else if (syntax.equals(getGeneralizedTimeSyntax())) {
+ return DatatypeConverter.printDateTime(Functions
+ .byteStringToGeneralizedTime().apply(value, null).toCalendar());
+ } else if (syntax.isHumanReadable()) {
+ return Functions.byteStringToString().apply(value, null);
+ } else {
+ // Base 64 encoded binary.
+ return DatatypeConverter.printBase64Binary(value.toByteArray());
+ }
+ }
+ };
+
+ static <V> ResultHandler<V> accumulate(final int size, final ResultHandler<List<V>> handler) {
+ return new AccumulatingResultHandler<V>(size, handler);
+ }
static Object attributeToJson(final Attribute a) {
final Function<ByteString, Object, Void> f = Functions.fixedFunction(BYTESTRING_TO_JSON, a);
- final boolean isSingleValued = a.getAttributeDescription().getAttributeType()
- .isSingleValue();
+ final boolean isSingleValued =
+ a.getAttributeDescription().getAttributeType().isSingleValue();
return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f));
}
+ // @Checkstyle:on
+
static Function<ByteString, Object, Attribute> byteStringToJson() {
return BYTESTRING_TO_JSON;
}
@@ -89,10 +142,62 @@
return a.getAttributeDescription().withoutOption("binary").toString();
}
+ static Filter toFilter(final Context c, final FilterType type, final String ldapAttribute,
+ final Object valueAssertion) {
+ final String v = String.valueOf(valueAssertion);
+ final Filter filter;
+ switch (type) {
+ case CONTAINS:
+ filter = Filter.substrings(ldapAttribute, null, Collections.singleton(v), null);
+ break;
+ case STARTS_WITH:
+ filter = Filter.substrings(ldapAttribute, v, null, null);
+ break;
+ case EQUAL_TO:
+ filter = Filter.equality(ldapAttribute, v);
+ break;
+ case GREATER_THAN:
+ filter = Filter.greaterThan(ldapAttribute, v);
+ break;
+ case GREATER_THAN_OR_EQUAL_TO:
+ filter = Filter.greaterOrEqual(ldapAttribute, v);
+ break;
+ case LESS_THAN:
+ filter = Filter.lessThan(ldapAttribute, v);
+ break;
+ case LESS_THAN_OR_EQUAL_TO:
+ filter = Filter.lessOrEqual(ldapAttribute, v);
+ break;
+ case PRESENT:
+ filter = Filter.present(ldapAttribute);
+ break;
+ case EXTENDED:
+ default:
+ filter = c.getConfig().getFalseFilter(); // Not supported.
+ break;
+ }
+ return filter;
+ }
+
static String toLowerCase(final String s) {
return s != null ? s.toLowerCase(Locale.ENGLISH) : null;
}
+ static <M, N> ResultHandler<M> transform(final Function<M, N, Void> f,
+ final ResultHandler<N> handler) {
+ return new ResultHandler<M>() {
+ @Override
+ public void handleError(final ResourceException error) {
+ handler.handleError(error);
+ }
+
+ @Override
+ public void handleResult(final M result) {
+ handler.handleResult(f.apply(result, null));
+ }
+ };
+ }
+
private static <T> List<T> asList(final Collection<T> c) {
if (c instanceof List) {
return (List<T>) c;
diff --git a/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java b/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
index 385aa48..0ec2721 100644
--- a/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
+++ b/opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
@@ -9,9 +9,9 @@
* 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 Copyrighted [year] [name of copyright owner]".
+ * information: "Portions Copyright [year] [name of copyright owner]".
*
- * Copyright © 2012 ForgeRock AS. All rights reserved.
+ * Copyright 2012-2013 ForgeRock AS.
*/
package org.forgerock.opendj.rest2ldap;
@@ -22,7 +22,6 @@
import java.util.logging.Logger;
import org.forgerock.json.resource.Router;
-import org.forgerock.json.resource.RoutingMode;
import org.forgerock.json.resource.servlet.HttpServlet;
import org.forgerock.opendj.ldap.ConnectionFactory;
import org.forgerock.opendj.ldap.DN;
@@ -86,8 +85,8 @@
// Create the router.
final Router router = new Router();
- router.addRoute(RoutingMode.EQUALS, "/users", userResource);
- router.addRoute(RoutingMode.EQUALS, "/groups", groupResource);
+ router.addRoute("/users", userResource);
+ router.addRoute("/groups", groupResource);
final org.forgerock.json.resource.ConnectionFactory resourceFactory = newInternalConnectionFactory(router);
final HttpServer httpServer = HttpServer.createSimpleServer("./", PORT);
--
Gitblit v1.10.0