From 5d8774080700c97c4f290818e4b5aa42a209740b Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Wed, 23 May 2012 16:39:10 +0000
Subject: [PATCH] Add support for complex and simple JSON attribute mappings.

---
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java   |  115 ++++++++++++++++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java   |  111 +++++++++++++++
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SimpleAttributeMapper.java    |  111 +++++++--------
 opendj3/opendj-rest2ldap/pom.xml                                                                    |    2 
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/CompositeAttributeMapper.java |   22 +-
 opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java                  |   13 +
 6 files changed, 301 insertions(+), 73 deletions(-)

diff --git a/opendj3/opendj-rest2ldap/pom.xml b/opendj3/opendj-rest2ldap/pom.xml
index fa1dd8a..67ae69a 100644
--- a/opendj3/opendj-rest2ldap/pom.xml
+++ b/opendj3/opendj-rest2ldap/pom.xml
@@ -61,7 +61,7 @@
         <dependency>
             <groupId>org.forgerock.commons</groupId>
             <artifactId>json-fluent</artifactId>
-            <version>1.1.0</version>
+            <version>1.2.0-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
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
new file mode 100644
index 0000000..fcd9f1b
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/ComplexAttributeMapper.java
@@ -0,0 +1,115 @@
+/*
+ * 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.opendj.rest2ldap.Utils.toLowerCase;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.forgerock.json.fluent.JsonPointer;
+import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.resource.exception.ResourceException;
+import org.forgerock.resource.provider.Context;
+
+/**
+ *
+ */
+public class ComplexAttributeMapper implements AttributeMapper {
+
+    private final String normalizedJsonAttributeName;
+    private final String jsonAttributeName;
+    private final AttributeMapper mapper;
+
+    /**
+     * Creates a new complex attribute mapper which will wrap the results of the
+     * provided mapper as a complex JSON object.
+     *
+     * @param jsonAttributeName
+     *            The name of the complex attribute.
+     * @param mapper
+     *            The mapper which should be used to provide the contents of the
+     *            complex attribute.
+     */
+    public ComplexAttributeMapper(String jsonAttributeName, AttributeMapper mapper) {
+        this.jsonAttributeName = jsonAttributeName;
+        this.normalizedJsonAttributeName = toLowerCase(jsonAttributeName);
+        this.mapper = mapper;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void getLDAPAttributes(Set<String> ldapAttributes) {
+        mapper.getLDAPAttributes(ldapAttributes);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void getLDAPAttributes(Set<String> ldapAttributes, JsonPointer resourceAttribute) {
+        if (resourceAttribute.size() > 0) {
+            String rootName = resourceAttribute.get(0);
+            if (toLowerCase(rootName).equals(normalizedJsonAttributeName)) {
+                JsonPointer relativePointer = resourceAttribute.relativePointer();
+                if (relativePointer == null) {
+                    // User requested the entire contents of this complex
+                    // attribute.
+                    mapper.getLDAPAttributes(ldapAttributes);
+                } else {
+                    // User requested partial contents of this complex
+                    // attribute.
+                    mapper.getLDAPAttributes(ldapAttributes, relativePointer);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@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 onSuccess(Map<String, Object> result) {
+                        Map<String, Object> complexResult =
+                                Collections.singletonMap(jsonAttributeName, (Object) result);
+                        h.onSuccess(complexResult);
+                    }
+
+                    public void onFailure(ResourceException e) {
+                        h.onFailure(e);
+                    }
+                };
+        mapper.toJson(c, e, wrapper);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
+        // TODO Auto-generated method stub
+
+    }
+
+}
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 3bb818d..f62c6f1 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
@@ -20,6 +20,7 @@
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -36,22 +37,21 @@
  *
  */
 public final class CompositeAttributeMapper implements AttributeMapper {
-    private final Set<String> allLDAPAttributes;
-    private final List<AttributeMapper> attributeMappers;
+    private final Set<String> allLDAPAttributes = new LinkedHashSet<String>();
+    private final List<AttributeMapper> attributeMappers = new LinkedList<AttributeMapper>();
 
     /**
      * Creates a new composite attribute mapper.
      *
-     * @param attributeMappers
-     *            The list of attribute mappers.
      */
-    public CompositeAttributeMapper(final List<AttributeMapper> attributeMappers) {
-        this.attributeMappers = new ArrayList<AttributeMapper>(attributeMappers);
-        Set<String> tmp = new LinkedHashSet<String>(attributeMappers.size());
-        for (final AttributeMapper mapper : attributeMappers) {
-            mapper.getLDAPAttributes(tmp);
-        }
-        allLDAPAttributes = Collections.unmodifiableSet(tmp);
+    public CompositeAttributeMapper() {
+        // No implementation required.
+    }
+
+    public CompositeAttributeMapper addMapper(AttributeMapper mapper) {
+        attributeMappers.add(mapper);
+        mapper.getLDAPAttributes(allLDAPAttributes);
+        return this;
     }
 
     /**
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
new file mode 100644
index 0000000..6b363b7
--- /dev/null
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/DefaultAttributeMapper.java
@@ -0,0 +1,111 @@
+/*
+ * 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.opendj.rest2ldap.Utils.attributeToJson;
+import static org.forgerock.opendj.rest2ldap.Utils.getAttributeName;
+import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
+
+import java.util.LinkedHashMap;
+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.opendj.ldap.Attribute;
+import org.forgerock.opendj.ldap.Entry;
+import org.forgerock.resource.provider.Context;
+
+/**
+ *
+ */
+public final class DefaultAttributeMapper implements AttributeMapper {
+
+    // 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) {
+            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(Set<String> ldapAttributes, JsonPointer resourceAttribute) {
+        String name = resourceAttribute.leaf();
+        if (name != null) {
+            if (isIncludedAttribute(name)) {
+                ldapAttributes.add(name);
+            } else {
+                // FIXME: log something or return a ResourceException?
+            }
+        }
+    }
+
+    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);
+            if (isIncludedAttribute(name)) {
+                result.put(name, attributeToJson(a));
+            }
+        }
+        h.onSuccess(result);
+    }
+
+    private boolean isIncludedAttribute(String name) {
+        String lowerName = toLowerCase(name);
+
+        // Ignore the requested attribute if it has been excluded.
+        if (excludedAttributes.containsKey(lowerName)) {
+            return false;
+        }
+
+        // Include all attributes by default.
+        if (includedAttributes.isEmpty() || includedAttributes.containsKey(lowerName)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
+        // TODO:
+    }
+}
diff --git a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java
index 2994682..651c38d 100644
--- a/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java
+++ b/opendj3/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Example.java
@@ -61,8 +61,15 @@
 
         // Create user resource.
         AttributeMapper userMapper =
-                new SimpleAttributeMapper().includeAttribute("uid", "cn", "sn", "mail",
-                        "isMemberOf", "modifyTimestamp");
+                new CompositeAttributeMapper().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")).addMapper(
+                                        new SimpleAttributeMapper("emailAddress", "mail"))));
         LDAPResource userResource = new LDAPResource(userContainer, userMapper);
         ResourceInvoker userResourceInvoker = new ResourceInvoker();
         userResourceInvoker.resource = userResource; // FIXME: Yuk!
@@ -70,7 +77,7 @@
 
         // Create group resource.
         AttributeMapper groupMapper =
-                new SimpleAttributeMapper().includeAttribute("cn", "ou", "description",
+                new DefaultAttributeMapper().includeAttribute("cn", "ou", "description",
                         "uniquemember");
         LDAPResource groupResource = new LDAPResource(groupContainer, groupMapper);
         ResourceInvoker groupResourceInvoker = new ResourceInvoker();
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 43bdf36..3d3b5bf 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
@@ -16,10 +16,10 @@
 
 package org.forgerock.opendj.rest2ldap;
 
-import static org.forgerock.opendj.rest2ldap.Utils.getAttributeName;
+import static org.forgerock.opendj.rest2ldap.Utils.attributeToJson;
 import static org.forgerock.opendj.rest2ldap.Utils.toLowerCase;
 
-import java.util.LinkedHashMap;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -33,78 +33,73 @@
 /**
  *
  */
-public final class SimpleAttributeMapper implements AttributeMapper {
+public class SimpleAttributeMapper implements AttributeMapper {
 
-    // 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>();
+    private final String ldapAttributeName;
+    private final String jsonAttributeName;
+    private final String normalizedJsonAttributeName;
 
-    public SimpleAttributeMapper() {
-        // No implementation required.
+    /**
+     * Creates a new simple attribute mapper which maps a single LDAP attribute
+     * to an entry.
+     *
+     * @param attributeName
+     *            The name of the simple JSON and LDAP attribute.
+     */
+    public SimpleAttributeMapper(String attributeName) {
+        this(attributeName, attributeName);
     }
 
-    public SimpleAttributeMapper includeAttribute(String... attributes) {
-        for (String attribute : attributes) {
-            includedAttributes.put(toLowerCase(attribute), attribute);
-        }
-        return this;
+    /**
+     * Creates a new simple attribute mapper which maps a single LDAP attribute
+     * to an entry.
+     *
+     * @param jsonAttributeName
+     *            The name of the simple JSON attribute.
+     * @param ldapAttributeName
+     *            The name of the LDAP attribute.
+     */
+    public SimpleAttributeMapper(String jsonAttributeName, String ldapAttributeName) {
+        this.jsonAttributeName = jsonAttributeName;
+        this.normalizedJsonAttributeName = toLowerCase(jsonAttributeName);
+        this.ldapAttributeName = ldapAttributeName;
     }
 
-    public SimpleAttributeMapper excludeAttribute(String... attributes) {
-        for (String attribute : attributes) {
-            excludedAttributes.put(toLowerCase(attribute), attribute);
-        }
-        return this;
-    }
-
+    /**
+     * {@inheritDoc}
+     */
     public void getLDAPAttributes(Set<String> ldapAttributes) {
-        if (!includedAttributes.isEmpty()) {
-            ldapAttributes.addAll(includedAttributes.values());
-        } else {
-            // All user attributes.
-            ldapAttributes.add("*");
-        }
+        ldapAttributes.add(ldapAttributeName);
     }
 
+    /**
+     * {@inheritDoc}
+     */
     public void getLDAPAttributes(Set<String> ldapAttributes, JsonPointer resourceAttribute) {
-        String name = resourceAttribute.leaf();
-        if (name != null) {
-            if (isIncludedAttribute(name)) {
-                ldapAttributes.add(name);
-            } else {
-                // FIXME: log something or return a ResourceException?
-            }
+        if (toLowerCase(resourceAttribute.leaf()).equals(normalizedJsonAttributeName)) {
+            ldapAttributes.add(ldapAttributeName);
         }
     }
 
-    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);
-            if (isIncludedAttribute(name)) {
-                result.put(name, Utils.attributeToJson(a));
-            }
+    /**
+     * {@inheritDoc}
+     */
+    public void toJson(Context c, Entry e,
+            final AttributeMapperCompletionHandler<Map<String, Object>> h) {
+        Attribute a = e.getAttribute(ldapAttributeName);
+        if (a != null) {
+            Map<String, Object> result =
+                    Collections.singletonMap(jsonAttributeName, attributeToJson(a));
+            h.onSuccess(result);
         }
-        h.onSuccess(result);
     }
 
-    private boolean isIncludedAttribute(String name) {
-        String lowerName = toLowerCase(name);
-
-        // Ignore the requested attribute if it has been excluded.
-        if (excludedAttributes.containsKey(lowerName)) {
-            return false;
-        }
-
-        // Include all attributes by default.
-        if (includedAttributes.isEmpty() || includedAttributes.containsKey(lowerName)) {
-            return true;
-        }
-
-        return false;
-    }
-
+    /**
+     * {@inheritDoc}
+     */
     public void toLDAP(Context c, JsonValue v, AttributeMapperCompletionHandler<List<Attribute>> h) {
-        // TODO:
+        // TODO Auto-generated method stub
+
     }
+
 }

--
Gitblit v1.10.0