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

Matthew Swift
20.10.2012 db786032bf45be89c4a893281911364d158cfb6e
Update to use new json-resource 2.0 APIs.
3 files deleted
3 files added
10 files modified
1394 ■■■■ changed files
opendj3/opendj-rest2ldap/pom.xml 57 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java 17 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapperCompletionHandler.java 29 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java 46 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java 146 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java 24 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java 69 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java 63 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java 104 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java 290 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java 244 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java 9 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java 74 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubContainerAttributeMapper.java 61 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java 56 ●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java 105 ●●●●● patch | view | raw | blame | history
opendj3/opendj-rest2ldap/pom.xml
@@ -61,45 +61,32 @@
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>json-fluent</artifactId>
            <version>1.2.0-SNAPSHOT</version>
            <version>2.0.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>json-resource</artifactId>
            <version>2.0.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>json-resource-servlet</artifactId>
            <version>2.0.0-SNAPSHOT</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish.grizzly</groupId>
            <artifactId>grizzly-http-servlet</artifactId>
            <version>2.3-SNAPSHOT</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>org.forgerock.json.resource</artifactId>
            <version>1.3.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>org.forgerock.json.resource.restlet</artifactId>
            <version>1.3.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>org.forgerock.restlet</artifactId>
            <version>1.1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.forgerock.commons</groupId>
            <artifactId>json-patch</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.restlet.jse</groupId>
            <artifactId>org.restlet</artifactId>
            <version>2.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.restlet.jse</groupId>
            <artifactId>org.restlet.ext.jackson</artifactId>
            <version>2.0.9</version>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapper.java
@@ -22,12 +22,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.resource.provider.Context;
/**
 *
 * An attribute mapper is responsible for converting JSON values to and from
 * LDAP attributes.
 */
public interface AttributeMapper {
@@ -38,6 +40,7 @@
     * <p>
     * Implementations should only add the names of attributes found in the LDAP
     * entry directly associated with the resource.
     *
     * @param jsonAttribute
     *            The name of the resource attribute requested by the client.
     * @param ldapAttributes
@@ -56,10 +59,13 @@
     * requests.
     *
     * @param c
     *            The server context.
     * @param e
     *            The LDAP entry to be converted to JSON.
     * @param h
     *            The result handler.
     */
    void toJson(Context c, Entry e, AttributeMapperCompletionHandler<Map<String, Object>> h);
    void toJson(ServerContext c, Entry e, ResultHandler<Map<String, Object>> h);
    /**
     * Transforms JSON content in the provided JSON value to LDAP attributes,
@@ -70,10 +76,13 @@
     * requests.
     *
     * @param c
     *            The server context.
     * @param v
     *            The JSON value to be converted to LDAP attributes.
     * @param h
     *            The result handler.
     */
    void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h);
    void toLDAP(ServerContext c, JsonValue v, ResultHandler<List<Attribute>> h);
    // TODO: methods for obtaining schema information (e.g. name, description,
    // type information).
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/AttributeMapperCompletionHandler.java
File was deleted
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
@@ -25,19 +25,21 @@
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.json.resource.ResourceException;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.resource.exception.ResourceException;
import org.forgerock.resource.provider.Context;
/**
 *
 * An attribute mapper which will wrap the results of the provided mapper as a
 * complex JSON object.
 */
public class ComplexAttributeMapper implements AttributeMapper {
    private final String normalizedJsonAttributeName;
    private final String jsonAttributeName;
    private final AttributeMapper mapper;
    private final String normalizedJsonAttributeName;
    /**
     * Creates a new complex attribute mapper which will wrap the results of the
@@ -49,7 +51,7 @@
     *            The mapper which should be used to provide the contents of the
     *            complex attribute.
     */
    public ComplexAttributeMapper(String jsonAttributeName, AttributeMapper mapper) {
    public ComplexAttributeMapper(final String jsonAttributeName, final AttributeMapper mapper) {
        this.jsonAttributeName = jsonAttributeName;
        this.mapper = mapper;
        this.normalizedJsonAttributeName = toLowerCase(jsonAttributeName);
@@ -58,9 +60,9 @@
    /**
     * {@inheritDoc}
     */
    public void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes) {
    public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
        if (attributeMatchesPointer(jsonAttribute)) {
            JsonPointer relativePointer = jsonAttribute.relativePointer();
            final JsonPointer relativePointer = jsonAttribute.relativePointer();
            mapper.getLDAPAttributes(relativePointer, ldapAttributes);
        }
    }
@@ -68,33 +70,33 @@
    /**
     * {@inheritDoc}
     */
    public void toJson(Context c, Entry e,
            final AttributeMapperCompletionHandler<Map<String, Object>> h) {
        AttributeMapperCompletionHandler<Map<String, Object>> wrapper =
                new AttributeMapperCompletionHandler<Map<String, Object>>() {
    public void toJson(final ServerContext c, final Entry e,
            final ResultHandler<Map<String, Object>> h) {
        final ResultHandler<Map<String, Object>> wrapper = new ResultHandler<Map<String, Object>>() {
                    public void onSuccess(Map<String, Object> result) {
                        Map<String, Object> complexResult =
                                Collections.singletonMap(jsonAttributeName, (Object) result);
                        h.onSuccess(complexResult);
                    }
            public void handleError(final ResourceException e) {
                h.handleError(e);
            }
                    public void onFailure(ResourceException e) {
                        h.onFailure(e);
                    }
                };
            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);
    }
    /**
     * {@inheritDoc}
     */
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
    public void toLDAP(final ServerContext c, final JsonValue v,
            final ResultHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
    }
    private boolean attributeMatchesPointer(JsonPointer resourceAttribute) {
    private boolean attributeMatchesPointer(final JsonPointer resourceAttribute) {
        return resourceAttribute.isEmpty()
                || toLowerCase(resourceAttribute.get(0)).equals(normalizedJsonAttributeName);
    }
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java
@@ -27,26 +27,34 @@
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.json.resource.ResourceException;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
import org.forgerock.resource.exception.ResourceException;
import org.forgerock.resource.provider.Context;
/**
 *
 * A collection of one or more attribute mappers whose content will be combined
 * into a single JSON array.
 */
public final class CompositeAttributeMapper implements AttributeMapper {
    private final List<AttributeMapper> attributeMappers = new LinkedList<AttributeMapper>();
    /**
     * Creates a new composite attribute mapper.
     *
     */
    public CompositeAttributeMapper() {
        // No implementation required.
    }
    public CompositeAttributeMapper addMapper(AttributeMapper mapper) {
    /**
     * Adds a subordinate attribute mapper to this composite attribute mapper.
     *
     * @param mapper
     *            The subordinate attribute mapper.
     * @return This composite attribute mapper.
     */
    public CompositeAttributeMapper addMapper(final AttributeMapper mapper) {
        attributeMappers.add(mapper);
        return this;
    }
@@ -54,8 +62,8 @@
    /**
     * {@inheritDoc}
     */
    public void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes) {
        for (AttributeMapper attribute : attributeMappers) {
    public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
        for (final AttributeMapper attribute : attributeMappers) {
            attribute.getLDAPAttributes(jsonAttribute, ldapAttributes);
        }
    }
@@ -63,45 +71,44 @@
    /**
     * {@inheritDoc}
     */
    public void toJson(Context c, Entry e,
            final AttributeMapperCompletionHandler<Map<String, Object>> h) {
        AttributeMapperCompletionHandler<Map<String, Object>> resultAccumulater =
                new AttributeMapperCompletionHandler<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 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 onFailure(ResourceException e) {
                        // Ensure that handler is only invoked once.
                        if (latch.getAndSet(0) > 0) {
                            h.onFailure(e);
                        }
            public void handleError(final ResourceException e) {
                // Ensure that handler is only invoked once.
                if (latch.getAndSet(0) > 0) {
                    h.handleError(e);
                }
            }
            public void handleResult(final Map<String, Object> result) {
                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);
                }
            }
        };
                    public void onSuccess(Map<String, Object> result) {
                        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.onSuccess(mergeResult);
                        }
                    }
                };
        for (AttributeMapper mapper : attributeMappers) {
        for (final AttributeMapper mapper : attributeMappers) {
            mapper.toJson(c, e, resultAccumulater);
        }
    }
@@ -109,27 +116,13 @@
    /**
     * {@inheritDoc}
     */
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
    public void toLDAP(final ServerContext c, final JsonValue v,
            final ResultHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
    }
    /**
     * 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(List<Map<String, Object>> srcValues, Map<String, Object> dstValue) {
        for (Map<String, Object> value : srcValues) {
            mergeJsonValue(value, dstValue);
        }
    }
    /**
     * Merge one JSON value into another.
     *
     * @param srcValue
@@ -139,24 +132,25 @@
     *            merged.
     */
    @SuppressWarnings("unchecked")
    private void mergeJsonValue(Map<String, Object> srcValue, Map<String, Object> dstValue) {
        for (Map.Entry<String, Object> record : srcValue.entrySet()) {
            String key = record.getKey();
            Object newValue = record.getValue();
    private void mergeJsonValue(final Map<String, Object> srcValue,
            final Map<String, Object> dstValue) {
        for (final Map.Entry<String, Object> record : srcValue.entrySet()) {
            final String key = record.getKey();
            final Object newValue = record.getValue();
            Object existingValue = dstValue.get(key);
            if (existingValue == null) {
                // Value is new, so just add it.
                dstValue.put(key, newValue);
            } else if (existingValue instanceof Map && newValue instanceof Map) {
            } 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) {
            } else if ((existingValue instanceof List) && (newValue instanceof List)) {
                // Merge two lists- create a new List, in case the existing one
                // is unmodifiable.
                List<Object> tmp = new ArrayList<Object>((List<Object>) existingValue);
                final List<Object> tmp = new ArrayList<Object>((List<Object>) existingValue);
                tmp.addAll((List<Object>) newValue);
                existingValue = tmp;
            }
@@ -165,4 +159,20 @@
            dstValue.put(key, newValue);
        }
    }
    /**
     * 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,
            final Map<String, Object> dstValue) {
        for (final Map<String, Object> value : srcValues) {
            mergeJsonValue(value, dstValue);
        }
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ConstantAttributeMapper.java
@@ -23,12 +23,13 @@
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.resource.provider.Context;
/**
 *
 * An attribute mapper which maps a single JSON attribute to a fixed value.
 */
public class ConstantAttributeMapper implements AttributeMapper {
@@ -36,7 +37,7 @@
    private final Object jsonAttributeValue;
    /**
     * Creates a new constant attribute mapper which maps a single LDAP
     * Creates a new constant attribute mapper which maps a single JSON
     * attribute to a fixed value.
     *
     * @param attributeName
@@ -44,7 +45,7 @@
     * @param attributeValue
     *            The value of the simple JSON attribute.
     */
    public ConstantAttributeMapper(String attributeName, Object attributeValue) {
    public ConstantAttributeMapper(final String attributeName, final Object attributeValue) {
        this.jsonAttributeName = attributeName;
        this.jsonAttributeValue = attributeValue;
    }
@@ -52,26 +53,27 @@
    /**
     * {@inheritDoc}
     */
    public void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes) {
    public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
        // Nothing to do.
    }
    /**
     * {@inheritDoc}
     */
    public void toJson(Context c, Entry e,
            final AttributeMapperCompletionHandler<Map<String, Object>> h) {
    public void toJson(final ServerContext c, final Entry e,
            final ResultHandler<Map<String, Object>> h) {
        // FIXME: how do we know if the user requested it???
        Map<String, Object> result =
                Collections.singletonMap(jsonAttributeName, jsonAttributeValue);
        h.onSuccess(result);
        final Map<String, Object> result = Collections.singletonMap(jsonAttributeName,
                jsonAttributeValue);
        h.handleResult(result);
    }
    /**
     * {@inheritDoc}
     */
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
    public void toLDAP(final ServerContext c, final JsonValue v,
            final ResultHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
    }
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
@@ -27,47 +27,32 @@
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.resource.provider.Context;
/**
 *
 */
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> includedAttributes = new LinkedHashMap<String, String>();
    private final Map<String, String> excludedAttributes = new LinkedHashMap<String, String>();
    public DefaultAttributeMapper() {
        // No implementation required.
    }
    public DefaultAttributeMapper includeAttribute(String... attributes) {
        for (String attribute : attributes) {
            includedAttributes.put(toLowerCase(attribute), attribute);
        }
        return this;
    }
    public DefaultAttributeMapper excludeAttribute(String... attributes) {
        for (String attribute : attributes) {
    public DefaultAttributeMapper excludeAttribute(final String... attributes) {
        for (final String attribute : attributes) {
            excludedAttributes.put(toLowerCase(attribute), attribute);
        }
        return this;
    }
    public void getLDAPAttributes(Set<String> ldapAttributes) {
        if (!includedAttributes.isEmpty()) {
            ldapAttributes.addAll(includedAttributes.values());
        } else {
            // All user attributes.
            ldapAttributes.add("*");
        }
    }
    public void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes) {
    public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
        switch (jsonAttribute.size()) {
        case 0:
            // Requested everything.
@@ -79,7 +64,7 @@
            }
            break;
        default:
            String name = jsonAttribute.get(0);
            final String name = jsonAttribute.get(0);
            if (isIncludedAttribute(name)) {
                ldapAttributes.add(name);
            }
@@ -87,19 +72,41 @@
        }
    }
    public void toJson(Context c, Entry e, AttributeMapperCompletionHandler<Map<String, Object>> h) {
        Map<String, Object> result = new LinkedHashMap<String, Object>(e.getAttributeCount());
        for (Attribute a : e.getAllAttributes()) {
            String name = getAttributeName(a);
    public void getLDAPAttributes(final Set<String> ldapAttributes) {
        if (!includedAttributes.isEmpty()) {
            ldapAttributes.addAll(includedAttributes.values());
        } else {
            // All user attributes.
            ldapAttributes.add("*");
        }
    }
    public DefaultAttributeMapper includeAttribute(final String... attributes) {
        for (final String attribute : attributes) {
            includedAttributes.put(toLowerCase(attribute), attribute);
        }
        return this;
    }
    public void toJson(final ServerContext 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);
            if (isIncludedAttribute(name)) {
                result.put(name, attributeToJson(a));
            }
        }
        h.onSuccess(result);
        h.handleResult(result);
    }
    private boolean isIncludedAttribute(String name) {
        String lowerName = toLowerCase(name);
    public void toLDAP(final ServerContext c, final JsonValue v,
            final ResultHandler<List<Attribute>> h) {
        // TODO:
    }
    private boolean isIncludedAttribute(final String name) {
        final String lowerName = toLowerCase(name);
        // Ignore the requested attribute if it has been excluded.
        if (excludedAttributes.containsKey(lowerName)) {
@@ -113,8 +120,4 @@
        return false;
    }
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
        // TODO:
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/EntryContainer.java
@@ -18,6 +18,7 @@
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;
@@ -32,25 +33,15 @@
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
import org.forgerock.resource.provider.Context;
/**
 *
 */
public final class EntryContainer {
    // FIXME: make this configurable, also allow use of DN.
    private static final String UUID_ATTRIBUTE = "entryUUID";
    // FIXME: make this configurable.
    private static final String ETAG_ATTRIBUTE = "etag";
    private final ConnectionFactory factory;
    private final DN baseDN;
    private abstract class AbstractRequestCompletionHandler<R, H extends ResultHandler<? super R>>
            implements ResultHandler<R> {
        final H resultHandler;
        final Connection connection;
        final H resultHandler;
        AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
            this.connection = connection;
@@ -123,12 +114,32 @@
    }
    public EntryContainer(DN baseDN, ConnectionFactory factory) {
    // FIXME: make this configurable.
    private static final String ETAG_ATTRIBUTE = "etag";
    // FIXME: make this configurable, also allow use of DN.
    private static final String UUID_ATTRIBUTE = "entryUUID";
    private final DN baseDN;
    private final ConnectionFactory factory;
    public EntryContainer(final DN baseDN, final ConnectionFactory factory) {
        this.baseDN = baseDN;
        this.factory = factory;
    }
    public void listEntries(final Context context, final SearchResultHandler handler) {
    public String getEtagFromEntry(final Entry entry) {
        return entry.parseAttribute(ETAG_ATTRIBUTE).asString();
    }
    public String getIDFromEntry(final Entry entry) {
        return entry.parseAttribute(UUID_ATTRIBUTE).asString();
    }
    public void listEntries(final Context context, final Collection<String> attributes,
            final SearchResultHandler handler) {
        final String[] tmp = getSearchAttributes(attributes);
        final ConnectionCompletionHandler<Result> outerHandler =
                new ConnectionCompletionHandler<Result>(handler) {
@@ -136,9 +147,9 @@
                    public void handleResult(final Connection connection) {
                        final SearchRequestCompletionHandler innerHandler =
                                new SearchRequestCompletionHandler(connection, handler);
                        SearchRequest request =
                        final SearchRequest request =
                                Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter
                                        .objectClassPresent(), UUID_ATTRIBUTE, ETAG_ATTRIBUTE);
                                        .objectClassPresent(), tmp);
                        connection.searchAsync(request, null, innerHandler);
                    }
@@ -149,6 +160,7 @@
    public void readEntry(final Context c, final String id, final Collection<String> attributes,
            final ResultHandler<SearchResultEntry> handler) {
        final String[] tmp = getSearchAttributes(attributes);
        final ConnectionCompletionHandler<SearchResultEntry> outerHandler =
                new ConnectionCompletionHandler<SearchResultEntry>(handler) {
@@ -156,13 +168,7 @@
                    public void handleResult(final Connection connection) {
                        final RequestCompletionHandler<SearchResultEntry> innerHandler =
                                new RequestCompletionHandler<SearchResultEntry>(connection, handler);
                        // FIXME: who is responsible for adding the UUID and
                        // etag attributes to this search?
                        String[] tmp = attributes.toArray(new String[attributes.size() + 2]);
                        tmp[tmp.length - 2] = UUID_ATTRIBUTE;
                        tmp[tmp.length - 1] = ETAG_ATTRIBUTE;
                        SearchRequest request =
                        final SearchRequest request =
                                Requests.newSearchRequest(baseDN, SearchScope.SINGLE_LEVEL, Filter
                                        .equality(UUID_ATTRIBUTE, id), tmp);
                        connection.searchSingleEntryAsync(request, innerHandler);
@@ -173,12 +179,13 @@
        factory.getConnectionAsync(outerHandler);
    }
    public String getIDFromEntry(final Entry entry) {
        return entry.parseAttribute(UUID_ATTRIBUTE).asString();
    }
    public String getEtagFromEntry(final Entry entry) {
        return entry.parseAttribute(ETAG_ATTRIBUTE).asString();
    private String[] getSearchAttributes(final Collection<String> attributes) {
        // FIXME: who is responsible for adding the UUID and etag attributes to
        // this search?
        final String[] tmp = attributes.toArray(new String[attributes.size() + 2]);
        tmp[tmp.length - 2] = UUID_ATTRIBUTE;
        tmp[tmp.length - 1] = ETAG_ATTRIBUTE;
        return tmp;
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java
File was deleted
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPCollectionResourceProvider.java
New file
@@ -0,0 +1,290 @@
/*
 * 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 Copyrighted [year] [name of copyright owner]".
 *
 * Copyright 2012 ForgeRock AS. All rights reserved.
 */
package org.forgerock.opendj.rest2ldap;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.forgerock.json.fluent.JsonPointer;
import org.forgerock.json.fluent.JsonValue;
import org.forgerock.json.resource.*;
import org.forgerock.opendj.ldap.AssertionFailureException;
import org.forgerock.opendj.ldap.AuthenticationException;
import org.forgerock.opendj.ldap.AuthorizationException;
import org.forgerock.opendj.ldap.ConnectionException;
import org.forgerock.opendj.ldap.EntryNotFoundException;
import org.forgerock.opendj.ldap.ErrorResultException;
import org.forgerock.opendj.ldap.MultipleEntriesFoundException;
import org.forgerock.opendj.ldap.SearchResultHandler;
import org.forgerock.opendj.ldap.TimeoutResultException;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.forgerock.opendj.ldap.responses.SearchResultReference;
/**
 *
 */
public class LDAPCollectionResourceProvider implements CollectionResourceProvider {
    private final AttributeMapper attributeMapper;
    private final EntryContainer entryContainer;
    // Dummy exception used for signalling search success.
    private static final ResourceException SUCCESS = new UncategorizedException(0, null, null);
    /**
     * Creates a new LDAP resource.
     */
    public LDAPCollectionResourceProvider(final EntryContainer container, final AttributeMapper mapper) {
        this.entryContainer = container;
        this.attributeMapper = mapper;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void actionCollection(ServerContext context, ActionRequest request,
            ResultHandler<JsonValue> handler) {
        handler.handleError(new NotSupportedException("Not yet implemented"));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void actionInstance(ServerContext context, String resourceId, ActionRequest request,
            ResultHandler<JsonValue> handler) {
        handler.handleError(new NotSupportedException("Not yet implemented"));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void createInstance(ServerContext context, CreateRequest request,
            ResultHandler<Resource> handler) {
        handler.handleError(new NotSupportedException("Not yet implemented"));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void deleteInstance(ServerContext context, String resourceId, DeleteRequest request,
            ResultHandler<Resource> handler) {
        handler.handleError(new NotSupportedException("Not yet implemented"));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void patchInstance(ServerContext context, String resourceId, PatchRequest request,
            ResultHandler<Resource> handler) {
        handler.handleError(new NotSupportedException("Not yet implemented"));
    }
    /**
     * {@inheritDoc}
     */
    @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 SearchResultHandler searchHandler = new SearchResultHandler() {
            private final AtomicInteger pendingResourceCount = new AtomicInteger();
            private final AtomicReference<ResourceException> pendingResult = new AtomicReference<ResourceException>();
            private final AtomicBoolean resultSent = new AtomicBoolean();
            /*
             * Close out the query result set if there are no more pending
             * resources and the LDAP result has been received.
             */
            private void completeIfNecessary() {
                if (pendingResourceCount.get() == 0) {
                    final ResourceException result = pendingResult.get();
                    if (result != null && resultSent.compareAndSet(false, true)) {
                        if (result == SUCCESS) {
                            handler.handleResult(null);
                        } else {
                            handler.handleError(result);
                        }
                    }
                }
            }
            public boolean handleEntry(final SearchResultEntry entry) {
                /*
                 * Search result entries will be returned before the search
                 * result/error so the only reason pendingResult will be
                 * non-null is if a mapping error has occurred.
                 */
                if (pendingResult.get() != null) {
                    return false;
                }
                // TODO: should the resource or the container define the ID
                // mapping?
                final String id = entryContainer.getIDFromEntry(entry);
                final String revision = entryContainer.getEtagFromEntry(entry);
                final ResultHandler<Map<String, Object>> mapHandler = new ResultHandler<Map<String, Object>>() {
                    public void handleError(final ResourceException e) {
                        pendingResult.compareAndSet(null, e);
                        pendingResourceCount.decrementAndGet();
                        completeIfNecessary();
                    }
                    public void handleResult(final Map<String, Object> result) {
                        Resource resource = new Resource(id, revision, new JsonValue(result));
                        handler.handleResource(resource);
                        pendingResourceCount.decrementAndGet();
                        completeIfNecessary();
                    }
                };
                pendingResourceCount.incrementAndGet();
                attributeMapper.toJson(context, entry, mapHandler);
                return true;
            }
            public void handleErrorResult(final ErrorResultException error) {
                pendingResult.compareAndSet(null, adaptErrorResult(error));
                completeIfNecessary();
            }
            public boolean handleReference(final SearchResultReference reference) {
                // TODO: should this be classed as an error since rest2ldap
                // assumes entries are all colocated?
                return true;
            }
            public void handleResult(final Result result) {
                pendingResult.compareAndSet(null, SUCCESS);
                completeIfNecessary();
            }
        };
        entryContainer.listEntries(context, requestedLDAPAttributes, searchHandler);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void readInstance(final ServerContext context, final String resourceId,
            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 org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry> searchHandler = new org.forgerock.opendj.ldap.ResultHandler<SearchResultEntry>() {
            public void handleErrorResult(final ErrorResultException error) {
                handler.handleError(adaptErrorResult(error));
            }
            public void handleResult(final SearchResultEntry entry) {
                final String revision = entryContainer.getEtagFromEntry(entry);
                final ResultHandler<Map<String, Object>> mapHandler = new ResultHandler<Map<String, Object>>() {
                    public void handleError(final ResourceException e) {
                        handler.handleError(e);
                    }
                    public void handleResult(final Map<String, Object> result) {
                        Resource resource = new Resource(resourceId, revision,
                                new JsonValue(result));
                        handler.handleResult(resource);
                    }
                };
                attributeMapper.toJson(context, entry, mapHandler);
            }
        };
        entryContainer.readEntry(context, resourceId, requestedLDAPAttributes, searchHandler);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void updateInstance(ServerContext context, String resourceId, UpdateRequest request,
            ResultHandler<Resource> handler) {
        handler.handleError(new NotSupportedException("Not yet implemented"));
    }
    /**
     * Adapts an LDAP result code to a resource exception.
     *
     * @param error
     *            The LDAP error that should be adapted.
     * @return The equivalent resource exception.
     */
    private ResourceException adaptErrorResult(final ErrorResultException error) {
        int resourceResultCode;
        try {
            throw error;
        } catch (final AssertionFailureException e) {
            resourceResultCode = ResourceException.VERSION_MISMATCH;
        } catch (final AuthenticationException e) {
            resourceResultCode = 401;
        } catch (final AuthorizationException e) {
            resourceResultCode = ResourceException.FORBIDDEN;
        } catch (final ConnectionException e) {
            resourceResultCode = ResourceException.UNAVAILABLE;
        } catch (final EntryNotFoundException e) {
            resourceResultCode = ResourceException.NOT_FOUND;
        } catch (final MultipleEntriesFoundException e) {
            resourceResultCode = ResourceException.INTERNAL_ERROR;
        } catch (final TimeoutResultException e) {
            resourceResultCode = 408;
        } catch (final ErrorResultException e) {
            resourceResultCode = ResourceException.INTERNAL_ERROR;
        }
        return ResourceException.getException(resourceResultCode, null, error.getMessage(), error);
    }
    /**
     * Determines the set of LDAP attributes to request in an LDAP read (search,
     * post-read), based on the provided set of JSON pointers.
     *
     * @param requestedAttributes
     *            The set 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) {
        final Set<String> requestedLDAPAttributes;
        if (requestedAttributes.isEmpty()) {
            // Full read.
            requestedLDAPAttributes = new LinkedHashSet<String>();
            attributeMapper.getLDAPAttributes(new JsonPointer(), requestedLDAPAttributes);
        } else {
            // Partial read.
            requestedLDAPAttributes = new LinkedHashSet<String>(requestedAttributes.size());
            for (final JsonPointer requestedAttribute : requestedAttributes) {
                attributeMapper.getLDAPAttributes(requestedAttribute, requestedLDAPAttributes);
            }
        }
        return requestedLDAPAttributes;
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/LDAPResource.java
File was deleted
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ReferenceAttributeMapper.java
@@ -22,9 +22,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.Entry;
import org.forgerock.resource.provider.Context;
/**
 *
@@ -36,7 +37,7 @@
    /**
     * {@inheritDoc}
     */
    public void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes) {
    public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
        // TODO Auto-generated method stub
    }
@@ -44,7 +45,7 @@
    /**
     * {@inheritDoc}
     */
    public void toJson(Context c, Entry e, AttributeMapperCompletionHandler<Map<String, Object>> h) {
    public void toJson(final ServerContext c, final Entry e, final ResultHandler<Map<String, Object>> h) {
        // TODO Auto-generated method stub
    }
@@ -52,7 +53,7 @@
    /**
     * {@inheritDoc}
     */
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
    public void toLDAP(final ServerContext c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
    }
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java
@@ -26,26 +26,27 @@
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.Function;
import org.forgerock.opendj.ldap.Functions;
import org.forgerock.resource.provider.Context;
/**
 *
 */
public class SimpleAttributeMapper implements AttributeMapper {
    private final String ldapAttributeName;
    private final String jsonAttributeName;
    private final String normalizedJsonAttributeName;
    private boolean forceSingleValued = false;
    private Object defaultValue = null;
    private boolean isReadOnly = false;
    private Function<ByteString, ?, Void> decoder = null;
    private Object defaultValue = null;
    private boolean forceSingleValued = false;
    private boolean isReadOnly = false;
    private final String jsonAttributeName;
    private final String ldapAttributeName;
    private final String normalizedJsonAttributeName;
    /**
     * Creates a new simple attribute mapper which maps a single LDAP attribute
@@ -54,7 +55,7 @@
     * @param attributeName
     *            The name of the simple JSON and LDAP attribute.
     */
    public SimpleAttributeMapper(String attributeName) {
    public SimpleAttributeMapper(final String attributeName) {
        this(attributeName, attributeName);
    }
@@ -67,54 +68,38 @@
     * @param ldapAttributeName
     *            The name of the LDAP attribute.
     */
    public SimpleAttributeMapper(String jsonAttributeName, String ldapAttributeName) {
    public SimpleAttributeMapper(final String jsonAttributeName, final String ldapAttributeName) {
        this.jsonAttributeName = jsonAttributeName;
        this.ldapAttributeName = ldapAttributeName;
        this.normalizedJsonAttributeName = toLowerCase(jsonAttributeName);
    }
    public SimpleAttributeMapper withDefaultValue(Object defaultValue) {
        this.defaultValue = defaultValue;
        return this;
    }
    public SimpleAttributeMapper isReadOnly(boolean readOnly) {
        this.isReadOnly = readOnly;
        return this;
    }
    public SimpleAttributeMapper forceSingleValued(boolean singleValued) {
    public SimpleAttributeMapper forceSingleValued(final boolean singleValued) {
        this.forceSingleValued = singleValued;
        return this;
    }
    public SimpleAttributeMapper withDecoder(Function<ByteString, ?, Void> f) {
        this.decoder = f;
        return this;
    }
    /**
     * {@inheritDoc}
     */
    public void getLDAPAttributes(JsonPointer jsonAttribute, Set<String> ldapAttributes) {
    public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
        if (attributeMatchesPointer(jsonAttribute)) {
            ldapAttributes.add(ldapAttributeName);
        }
    }
    private boolean attributeMatchesPointer(JsonPointer resourceAttribute) {
        return resourceAttribute.isEmpty()
                || toLowerCase(resourceAttribute.get(0)).equals(normalizedJsonAttributeName);
    public SimpleAttributeMapper isReadOnly(final boolean readOnly) {
        this.isReadOnly = readOnly;
        return this;
    }
    /**
     * {@inheritDoc}
     */
    public void toJson(Context c, Entry e,
            final AttributeMapperCompletionHandler<Map<String, Object>> h) {
        Attribute a = e.getAttribute(ldapAttributeName);
    public void toJson(final ServerContext c, final Entry e, final ResultHandler<Map<String, Object>> h) {
        final Attribute a = e.getAttribute(ldapAttributeName);
        if (a != null) {
            Function<ByteString, ?, Void> f =
            final Function<ByteString, ?, Void> f =
                    decoder == null ? Functions.fixedFunction(byteStringToJson(), a) : decoder;
            final Object value;
            if (forceSingleValued || a.getAttributeDescription().getAttributeType().isSingleValue()) {
@@ -122,17 +107,32 @@
            } else {
                value = a.parse().asSetOf(f, defaultValue);
            }
            Map<String, Object> result = Collections.singletonMap(jsonAttributeName, value);
            h.onSuccess(result);
            final Map<String, Object> result = Collections.singletonMap(jsonAttributeName, value);
            h.handleResult(result);
        }
    }
    /**
     * {@inheritDoc}
     */
    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
    public void toLDAP(final ServerContext c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
    }
    public SimpleAttributeMapper withDecoder(final Function<ByteString, ?, Void> f) {
        this.decoder = f;
        return this;
    }
    public SimpleAttributeMapper withDefaultValue(final Object defaultValue) {
        this.defaultValue = defaultValue;
        return this;
    }
    private boolean attributeMatchesPointer(final JsonPointer resourceAttribute) {
        return resourceAttribute.isEmpty()
                || toLowerCase(resourceAttribute.get(0)).equals(normalizedJsonAttributeName);
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubContainerAttributeMapper.java
New file
@@ -0,0 +1,61 @@
/*
 * 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 Copyrighted [year] [name of copyright owner]".
 *
 * Copyright 2012 ForgeRock AS. All rights reserved.
 */
package org.forgerock.opendj.rest2ldap;
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.json.resource.ServerContext;
import org.forgerock.opendj.ldap.Attribute;
import org.forgerock.opendj.ldap.Entry;
/**
 *
 */
public class SubContainerAttributeMapper implements AttributeMapper {
    // private final EntryContainer referencedContainer;
    /**
     * {@inheritDoc}
     */
    public void getLDAPAttributes(final JsonPointer jsonAttribute, final Set<String> ldapAttributes) {
        // TODO Auto-generated method stub
    }
    /**
     * {@inheritDoc}
     */
    public void toJson(final ServerContext c, final Entry e, final ResultHandler<Map<String, Object>> h) {
        // TODO Auto-generated method stub
    }
    /**
     * {@inheritDoc}
     */
    public void toLDAP(final ServerContext c, final JsonValue v, final ResultHandler<List<Attribute>> h) {
        // TODO Auto-generated method stub
    }
}
opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Utils.java
@@ -41,7 +41,8 @@
    private static final Function<ByteString, Object, Attribute> BYTESTRING_TO_JSON =
            new Function<ByteString, Object, Attribute>() {
                public Object apply(final ByteString value, final Attribute a) {
                    Syntax syntax = a.getAttributeDescription().getAttributeType().getSyntax();
                    final Syntax syntax =
                            a.getAttributeDescription().getAttributeType().getSyntax();
                    if (syntax.equals(getBooleanSyntax())) {
                        return Functions.byteStringToBoolean().apply(value, null);
                    } else if (syntax.equals(getIntegerSyntax())) {
@@ -58,39 +59,17 @@
                }
            };
    // Prevent instantiation.
    private Utils() {
        // No implementation required.
    }
    private static <T> List<T> asList(Collection<T> c) {
        if (c instanceof List) {
            return (List<T>) c;
        } else {
            List<T> result = new ArrayList<T>(c.size());
            result.addAll(c);
            return result;
        }
    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();
        return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f));
    }
    static Function<ByteString, Object, Attribute> byteStringToJson() {
        return BYTESTRING_TO_JSON;
    }
    static Object attributeToJson(Attribute a) {
        Function<ByteString, Object, Void> f = Functions.fixedFunction(BYTESTRING_TO_JSON, a);
        boolean isSingleValued = a.getAttributeDescription().getAttributeType().isSingleValue();
        return isSingleValued ? a.parse().as(f) : asList(a.parse().asSetOf(f));
    }
    static String getAttributeName(Attribute a) {
        return a.getAttributeDescription().withoutOption("binary").toString();
    }
    static String toLowerCase(String s) {
        return s != null ? s.toLowerCase(Locale.ENGLISH) : null;
    }
    static <T> T ensureNotNull(final T object) {
        if (object == null) {
            throw new NullPointerException();
@@ -105,4 +84,25 @@
        return object;
    }
    static String getAttributeName(final Attribute a) {
        return a.getAttributeDescription().withoutOption("binary").toString();
    }
    static String toLowerCase(final String s) {
        return s != null ? s.toLowerCase(Locale.ENGLISH) : null;
    }
    private static <T> List<T> asList(final Collection<T> c) {
        if (c instanceof List) {
            return (List<T>) c;
        } else {
            return new ArrayList<T>(c);
        }
    }
    // Prevent instantiation.
    private Utils() {
        // No implementation required.
    }
}
opendj3/opendj-rest2ldap/src/test/java/org/forgerock/opendj/rest2ldap/Example.java
New file
@@ -0,0 +1,105 @@
/*
 * 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 Copyrighted [year] [name of copyright owner]".
 *
 * Copyright © 2012 ForgeRock AS. All rights reserved.
 */
package org.forgerock.opendj.rest2ldap;
import static org.forgerock.json.resource.Resources.newInternalConnectionFactory;
import static org.forgerock.opendj.ldap.Connections.newAuthenticatedConnectionFactory;
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;
import org.forgerock.opendj.ldap.Functions;
import org.forgerock.opendj.ldap.LDAPConnectionFactory;
import org.forgerock.opendj.ldap.requests.Requests;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.servlet.ServletRegistration;
import org.glassfish.grizzly.servlet.WebappContext;
/**
 * Example
 */
public class Example {
    private static final Logger LOGGER = Logger.getLogger(Example.class.getName());
    private static final int PORT = 18890;
    public static void main(final String[] args) throws Exception {
        // All LDAP resources will use this connection factory.
        final ConnectionFactory ldapFactory = newAuthenticatedConnectionFactory(
                new LDAPConnectionFactory("localhost", 1389), Requests.newSimpleBindRequest(
                        "cn=directory manager", "password".toCharArray()));
        // Create two entry containers whose members reference each other.
        final EntryContainer userContainer = new EntryContainer(DN
                .valueOf("ou=people,dc=example,dc=com"), ldapFactory);
        final EntryContainer groupContainer = new EntryContainer(DN
                .valueOf("ou=groups,dc=example,dc=com"), ldapFactory);
        // Create user resource.
        final AttributeMapper userMapper = new CompositeAttributeMapper().addMapper(
                new SimpleAttributeMapper("id", "entryUUID").forceSingleValued(true)).addMapper(
                new DefaultAttributeMapper().includeAttribute("uid", "isMemberOf",
                        "modifyTimestamp")).addMapper(
                new ComplexAttributeMapper("name", new DefaultAttributeMapper().includeAttribute(
                        "cn", "sn", "givenName"))).addMapper(
                new ComplexAttributeMapper("contactInformation", new CompositeAttributeMapper()
                        .addMapper(
                                new SimpleAttributeMapper("telephoneNumber").withDecoder(
                                        Functions.byteStringToString()).forceSingleValued(true))
                        .addMapper(
                                new SimpleAttributeMapper("emailAddress", "mail")
                                        .forceSingleValued(true))));
        final LDAPCollectionResourceProvider userResource = new LDAPCollectionResourceProvider(
                userContainer, userMapper);
        // Create group resource.
        final AttributeMapper groupMapper = new DefaultAttributeMapper().includeAttribute("cn",
                "ou", "description", "uniquemember");
        final LDAPCollectionResourceProvider groupResource = new LDAPCollectionResourceProvider(
                groupContainer, groupMapper);
        // Create the router.
        Router router = new Router();
        router.addRoute(RoutingMode.EQUALS, "/users", userResource);
        router.addRoute(RoutingMode.EQUALS, "/groups", groupResource);
        final org.forgerock.json.resource.ConnectionFactory resourceFactory = newInternalConnectionFactory(router);
        final HttpServer httpServer = HttpServer.createSimpleServer("./", PORT);
        try {
            final WebappContext ctx = new WebappContext("example", "/example");
            final ServletRegistration reg = ctx.addServlet("managed", new HttpServlet(
                    resourceFactory));
            reg.addMapping("/managed/*");
            ctx.deploy(httpServer);
            LOGGER.info("Starting server...");
            httpServer.start();
            LOGGER.info("Server started");
            LOGGER.info("Press any key to stop the server...");
            System.in.read();
        } finally {
            LOGGER.info("Stopping server...");
            httpServer.stop();
            LOGGER.info("Server stopped");
        }
    }
}