From 1f468b81913675dac6e0a2d984e63f66659e440e Mon Sep 17 00:00:00 2001
From: Jean-Noel Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Mon, 25 Mar 2013 11:58:10 +0000
Subject: [PATCH] OPENDJ-808 Implement a simple commons REST based HTTP connection handler

---
 opendj-sdk/opends/resource/config/http-config.json                                                  |  229 +++++++++++++++++++++++++
 opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml |   26 ++
 opendj-sdk/opends/resource/config/config.ldif                                                       |    1 
 opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties                        |    2 
 opendj-sdk/opends/ivy.xml                                                                           |   28 ++
 opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java   |   30 ++-
 opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java            |  136 ++++++++------
 opendj-sdk/opends/ivysettings.xml                                                                   |   23 ++
 opendj-sdk/opends/resource/schema/02-config.ldif                                                    |    9 
 9 files changed, 402 insertions(+), 82 deletions(-)

diff --git a/opendj-sdk/opends/ivy.xml b/opendj-sdk/opends/ivy.xml
index 5326a2e..5eaecf2 100644
--- a/opendj-sdk/opends/ivy.xml
+++ b/opendj-sdk/opends/ivy.xml
@@ -25,8 +25,11 @@
  !
  !      Copyright 2013 ForgeRock AS
  ! -->
+<!-- Using entities prevent constantly declaring the same versions -->
 <!DOCTYPE ivy-module [
-    <!ENTITY grizzly.version "2.3-rc6">
+    <!ENTITY grizzly.version    "2.3-rc6">
+    <!ENTITY opendj.sdk.version "3.0.0-SNAPSHOT">
+    <!ENTITY crest.version      "2.0.0-SNAPSHOT">
 ]>
 <ivy-module version="2.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -37,15 +40,30 @@
     <!-- subsequent versions are not compatible with java 6, so force to use this version only -->
     <dependency org="javax.servlet"         name="javax.servlet-api"        rev="[3.1-b02]" />
     <dependency org="com.sleepycat"         name="je"                       rev="5.0.48" />
-    <!--dependency org="org.forgerock.opendj"  name="opendj-rest2ldap-servlet" rev="3.0.0-SNAPSHOT" /-->
+    <dependency org="org.forgerock.opendj"  name="opendj-rest2ldap-servlet" rev="&opendj.sdk.version;" />
+    <dependency org="org.forgerock.opendj"  name="opendj-server2x-adapter"  rev="&opendj.sdk.version;">
+      <!-- This is the OpenDJ server module :) -->
+      <exclude  module="opendj-server"/>
+    </dependency>
     <dependency org="org.glassfish.grizzly" name="grizzly-http-servlet"     rev="&grizzly.version;">
       <exclude module="javax.servlet-api" />
     </dependency>
 
-    <!-- Force download of the sources jar -->
+    <!-- Force download of the source jars -->
     <!--
-    <dependency org="org.glassfish.grizzly" name="grizzly-http-server"      rev="&grizzly.version;" conf="default->master,sources"/>
-    <dependency org="org.glassfish.grizzly" name="grizzly-framework"        rev="&grizzly.version;" conf="default->master,sources"/>
+    <dependency org="org.codehaus.jackson"  name="jackson-core-asl"         rev="1.9.2"                conf="default->master,sources"/>
+    <dependency org="org.codehaus.jackson"  name="jackson-mapper-asl"       rev="1.9.2"                conf="default->master,sources"/>
+    <dependency org="org.glassfish.grizzly" name="grizzly-framework"        rev="&grizzly.version;"    conf="default->master,sources"/>
+    <dependency org="org.glassfish.grizzly" name="grizzly-http"             rev="&grizzly.version;"    conf="default->master,sources"/>
+    <dependency org="org.glassfish.grizzly" name="grizzly-http-server"      rev="&grizzly.version;"    conf="default->master,sources"/>
+    <dependency org="org.glassfish.grizzly" name="grizzly-rcm"              rev="&grizzly.version;"    conf="default->master,sources"/>
+    <dependency org="org.forgerock.commons" name="i18n-core"                rev="1.4.0"                conf="default->master,sources"/>
+    <dependency org="org.forgerock.commons" name="json-fluent"              rev="&crest.version;"      conf="default->master,sources"/>
+    <dependency org="org.forgerock.commons" name="json-resource"            rev="&crest.version;"      conf="default->master,sources"/>
+    <dependency org="org.forgerock.commons" name="json-resource-servlet"    rev="&crest.version;"      conf="default->master,sources"/>
+    <dependency org="org.forgerock.commons" name="org.forgerock.util"       rev="1.0.0"                conf="default->master,sources"/>
+    <dependency org="org.forgerock.opendj"  name="opendj-ldap-sdk"          rev="&opendj.sdk.version;" conf="default->master,sources"/>
+    <dependency org="org.forgerock.opendj"  name="opendj-rest2ldap"         rev="&opendj.sdk.version;" conf="default->master,sources"/>
     -->
 
     <exclude org="javax.activation"         artifact="activation" />
diff --git a/opendj-sdk/opends/ivysettings.xml b/opendj-sdk/opends/ivysettings.xml
index 9c88cba..82fb62e 100644
--- a/opendj-sdk/opends/ivysettings.xml
+++ b/opendj-sdk/opends/ivysettings.xml
@@ -28,13 +28,28 @@
 <ivy-settings>
   <settings defaultResolver="main" />
   <resolvers>
-    <chain name="main">
+    <!-- FIXME Not sure whether checkmodified is useful. Maybe not. -->
+    <!-- FIXME This change does not work when offline.
+         Check this link to see how to solve offline mode:
+         http://svn.apache.org/viewvc?view=revision&revision=1203477 
+    -->
+    <chain name="main" checkmodified="true" changingPattern=".*-SNAPSHOT">
 	  <!-- for forgerock dependencies -->
-      <ibiblio name="forgerock" m2compatible="true" root="http://maven.forgerock.org/repo/repo" />
+      <ibiblio name="forgerock"
+               m2compatible="true"
+               root="http://maven.forgerock.org/repo/repo"
+               checkmodified="true"
+               changingPattern=".*-SNAPSHOT"
+               />
 	  <!-- for nearly everything else -->
-      <ibiblio name="central"   m2compatible="true"/>
+      <ibiblio name="central"
+               m2compatible="true"
+               />
 	  <!-- for JE -->
-      <ibiblio name="oracle"    m2compatible="true" root="http://download.oracle.com/maven" />
+      <ibiblio name="oracle"
+               m2compatible="true"
+               root="http://download.oracle.com/maven"
+               />
     </chain>
   </resolvers>
 </ivy-settings>
diff --git a/opendj-sdk/opends/resource/config/config.ldif b/opendj-sdk/opends/resource/config/config.ldif
index c3f45b8..3fc9255 100644
--- a/opendj-sdk/opends/resource/config/config.ldif
+++ b/opendj-sdk/opends/resource/config/config.ldif
@@ -497,6 +497,7 @@
 ds-cfg-use-ssl: false
 ds-cfg-ssl-client-auth-policy: optional
 ds-cfg-ssl-cert-nickname: server-cert
+ds-cfg-config-file: config/http-config.json
 
 dn: cn=LDIF Connection Handler,cn=Connection Handlers,cn=config
 objectClass: top
diff --git a/opendj-sdk/opends/resource/config/http-config.json b/opendj-sdk/opends/resource/config/http-config.json
new file mode 100644
index 0000000..cef1345
--- /dev/null
+++ b/opendj-sdk/opends/resource/config/http-config.json
@@ -0,0 +1,229 @@
+{
+    // The array of connection factories which will be used by the Rest2LDAP
+    // Servlet and authentication filter.
+    "ldapConnectionFactories" : {
+        // Unauthenticated connections used for performing bind requests.
+        "default" : {
+            "connectionPoolSize"       : 10,
+            "heartBeatIntervalSeconds" : 30,
+            
+            // The preferred load-balancing pool.
+            "primaryLDAPServers"       : [
+                {
+                    "hostname" : "localhost",
+                    "port"     : 1389
+                }
+            ],
+            // The fail-over load-balancing pool (optional).
+            "secondaryLDAPServers"     : [
+                // Empty.
+            ]
+        },
+        
+        // Authenticated connections which will be used for searches during
+        // authentication and proxied operations (if enabled). This factory
+        // will re-use the server "default" configuration.
+        "root" : {
+            "inheritFrom"    : "default",
+            
+            // Defines how authentication should be performed. Only "simple"
+            // authentication is supported at the moment.
+            "authentication" : {
+                "simple" : {
+                    "bindDN"       : "cn=directory manager",
+                    "bindPassword" : "password"
+                }
+            }
+        }
+    },
+    
+    // The Rest2LDAP authentication filter configuration. The filter will be
+    // disabled if the configuration is not present. Upon successful
+    // authentication the filter will create a security context containing the
+    // following principals:
+    //
+    // "dn" - the DN of the user if known (may not be the case for sasl-plain)
+    // "id" - the username used for authentication.
+    "authenticationFilter" : {
+        // Indicates whether the filter should allow HTTP BASIC authentication.
+        "supportHTTPBasicAuthentication" : true,
+        
+        // Indicates whether the filter should allow alternative authentication
+        // and, if so, which HTTP headers it should obtain the username and
+        // password from.
+        "supportAltAuthentication"        : true, 
+        "altAuthenticationUsernameHeader" : "X-OpenIDM-Username",
+        "altAuthenticationPasswordHeader" : "X-OpenIDM-Password",
+        
+        // Indicates whether the authenticated LDAP connection should be cached
+        // for use within the Rest2LDAP Servlet for subsequent LDAP operations.
+        // If this is set to true then the Servlet will not need its own LDAP
+        // connection factory and will also not need to use proxied
+        // authorization.
+        "reuseAuthenticatedConnection" : true,
+        
+        // Specifies how LDAP authentications should be performed. The method
+        // must be one of:
+        //
+        // "simple"        - the username is an LDAP DN
+        // "sasl-plain"    - the username is an authzid which will be
+        //                   substituted into the "saslAuthzIdTemplate" using
+        //                   %s substitution
+        // "search-simple" - the user's DN will be resolved by performing an
+        //                   LDAP search using a filter constructed by
+        //                   substituting the username into the
+        //                   "searchFilterTemplate" using %s substitution.
+        "method" : "simple",
+        
+        // The connection factory which will be exclusively used for
+        // authenticating users using LDAP bind operations.
+        "bindLDAPConnectionFactory" : "default",
+        
+        // The SASL AuthzID template which will be used for "sasl-plain"
+        // authentication.
+        "saslAuthzIdTemplate" : "dn:uid=%s,ou=people,dc=example,dc=com",
+        
+        // The connection factory which will be used for performing LDAP
+        // searches to locate users when "search-simple" authentication is
+        // enabled.
+        "searchLDAPConnectionFactory" : "root",
+        
+        // The search parameters to use for "search-simple" authentication.
+        "searchBaseDN"         : "ou=people,dc=example,dc=com",
+        "searchScope"          : "sub", // Or "one".
+        "searchFilterTemplate" : "(&(objectClass=inetOrgPerson)(uid=%s))"
+        
+        // TODO: support for HTTP sessions?
+    },
+
+    // The Rest2LDAP Servlet configuration.
+    "servlet" : {
+        // The connection factory which will be used for performing LDAP
+        // operations. Pre-authenticated connections passed through from the
+        // authentication filter (see "reuseAuthenticatedConnection") will be
+        // used in preference to this factory. Specifically, a connection
+        // factory does not need to be configured if a connection will always
+        // be passed on from the filter, which may not always be the case
+        // if the filter is configured to use HTTP sessions.
+        "ldapConnectionFactory" : "root",
+        
+        // Specifies how LDAP authorization should be performed. The method
+        // must be one of:
+        //
+        // "none"        - use connections acquired from the LDAP connection
+        //                 factory. Don't use proxied authorization, and don't
+        //                 use cached pre-authenticated connections,
+        // "reuse"       - use the connection obtained during LDAP
+        //                 authentication. If no connection was passed through
+        //                 the authorization will fail,
+        // "proxy"       - use proxied authorization with an authorization ID
+        //                 derived from the "proxyAuthzIdTemplate". Proxied
+        //                 authorization will only be used if there is no
+        //                 pre-authenticated connection available.
+        "authorizationPolicy" : "proxy",
+        
+        // The AuthzID template which will be used for proxied authorization.
+        // The template should contain fields which are expected to be found in
+        // the security context create during authentication, e.g. "dn" and "id".
+        "proxyAuthzIdTemplate" : "dn:{dn}",
+        
+        // The REST APIs and their LDAP attribute mappings.
+        "mappings" : {
+            "/users" : {
+                "baseDN" : "ou=people,dc=example,dc=com",
+                "readOnUpdatePolicy" : "controls",
+                "additionalLDAPAttributes" : [
+                    {
+                        "type" : "objectClass",
+                        "values" : [
+                            "top",
+                            "person",
+                            "organizationalPerson",
+                            "inetOrgPerson"
+                        ]
+                    }
+                ],
+                "namingStrategy" : {
+                    "strategy" : "clientDNNaming",
+                    "dnAttribute" : "uid"
+                },
+                "etagAttribute" : "etag",
+                "attributes" : {
+                    "schemas"     : { "constant" : [ "urn:scim:schemas:core:1.0" ] },
+                    "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } },
+                    "_rev"        : { "simple"   : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } },
+                    "userName"    : { "simple"   : { "ldapAttribute" : "mail", "isSingleValued" : true, "writability" : "readOnly" } },
+                    "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true } },
+                    "name"        : { "object"   : {
+                        "givenName"  : { "simple" : { "ldapAttribute" : "givenName", "isSingleValued" : true } },
+                        "familyName" : { "simple" : { "ldapAttribute" : "sn", "isSingleValued" : true, "isRequired" : true } }
+                    } },
+                    "manager"     : { "reference" : {
+                        "ldapAttribute" : "manager",
+                        "baseDN"        : "ou=people,dc=example,dc=com",
+                        "primaryKey"    : "uid",
+                        "mapper"         : { "object" : {
+                            "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } },
+                            "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } }
+                        } }
+                    } },
+                    "groups"     : { "reference" : {
+                        "ldapAttribute" : "isMemberOf",
+                        "baseDN"        : "ou=groups,dc=example,dc=com",
+                        "writability"   : "readOnly",
+                        "primaryKey"    : "cn",
+                        "mapper"        : { "object" : {
+                            "_id"         : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true } }
+                        } }
+                    } },
+                    "contactInformation" : { "object" : {
+                        "telephoneNumber" : { "simple" : { "ldapAttribute" : "telephoneNumber", "isSingleValued" : true } },
+                        "emailAddress"    : { "simple" : { "ldapAttribute" : "mail", "isSingleValued" : true } }
+                    } },
+                    "meta"        : { "object" : {
+                        "created"      : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } },
+                        "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }
+                    } }
+                }
+            },
+            "/groups" : {
+                "baseDN" : "ou=groups,dc=example,dc=com",
+                "readOnUpdatePolicy" : "controls",
+                "additionalLDAPAttributes" : [
+                    {
+                        "type" : "objectClass",
+                        "values" : [
+                            "top",
+                            "groupOfUniqueNames"
+                        ]
+                    }
+                ],
+                "namingStrategy" : {
+                    "strategy" : "clientDNNaming",
+                    "dnAttribute" : "cn"
+                },
+                "etagAttribute" : "etag",
+                "attributes" : {
+                    "schemas"     : { "constant" : [ "urn:scim:schemas:core:1.0" ] },
+                    "_id"         : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "createOnly" } },
+                    "_rev"        : { "simple"   : { "ldapAttribute" : "etag", "isSingleValued" : true, "writability" : "readOnly" } },
+                    "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "isRequired" : true, "writability" : "readOnly" } },
+                    "members"    : { "reference" : {
+                        "ldapAttribute" : "uniqueMember",
+                        "baseDN"        : "dc=example,dc=com",
+                        "primaryKey"    : "uid",
+                        "mapper"        : { "object" : {
+                            "_id"         : { "simple"   : { "ldapAttribute" : "uid", "isSingleValued" : true, "isRequired" : true } },
+                            "displayName" : { "simple"   : { "ldapAttribute" : "cn", "isSingleValued" : true, "writability" : "readOnlyDiscardWrites" } }
+                        } }
+                    } },
+                    "meta"        : { "object" : {
+                        "created"      : { "simple" : { "ldapAttribute" : "createTimestamp", "isSingleValued" : true, "writability" : "readOnly" } },
+                        "lastModified" : { "simple" : { "ldapAttribute" : "modifyTimestamp", "isSingleValued" : true, "writability" : "readOnly" } }
+                    } }
+                }
+            }
+        }
+    }
+}
+
diff --git a/opendj-sdk/opends/resource/schema/02-config.ldif b/opendj-sdk/opends/resource/schema/02-config.ldif
index 1e7e4ce..4a1a62b 100644
--- a/opendj-sdk/opends/resource/schema/02-config.ldif
+++ b/opendj-sdk/opends/resource/schema/02-config.ldif
@@ -3665,6 +3665,12 @@
   SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
   SINGLE-VALUE
   X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.118
+  NAME 'ds-cfg-config-file'
+  EQUALITY caseIgnoreMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDJ Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
   NAME 'ds-cfg-access-control-handler'
   SUP top
@@ -3841,7 +3847,8 @@
         ds-cfg-ssl-protocol $
         ds-cfg-ssl-cipher-suite $
         ds-cfg-max-blocked-write-time-limit $
-        ds-cfg-buffer-size )
+        ds-cfg-buffer-size $
+        ds-cfg-config-file )
   X-ORIGIN 'OpenDJ Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.14
   NAME 'ds-cfg-entry-cache'
diff --git a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml
index dc84048..ec91d57 100644
--- a/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml
+++ b/opendj-sdk/opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml
@@ -427,4 +427,30 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
+  <adm:property name="config-file" mandatory="true" advanced="true">
+    <adm:synopsis>
+      Specifies the name of the configuration file for the HTTP Connection Handler.
+    </adm:synopsis>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>config/http-config.json</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:string>
+        <adm:pattern>
+         <adm:regex>.*</adm:regex>
+         <adm:usage>FILE</adm:usage>
+           <adm:synopsis>
+             A path to an existing file that is readable by the server.
+           </adm:synopsis>
+         </adm:pattern>
+      </adm:string>
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-config-file</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
 </adm:managed-object>
diff --git a/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties b/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties
index aad1fbe..d606222 100644
--- a/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties
+++ b/opendj-sdk/opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties
@@ -14,6 +14,8 @@
 property.allow-tcp-reuse-address.description=If enabled, the SO_REUSEADDR socket option is used on the server listen socket to potentially allow the reuse of socket descriptors for clients in a TIME_WAIT state. This may help the server avoid temporarily running out of socket descriptors in cases in which a very large number of short-lived connections have been established from the same client system.
 property.buffer-size.synopsis=Specifies the size in bytes of the HTTP response message write buffer.
 property.buffer-size.description=This property specifies write buffer size allocated by the server for each client connection and used to buffer HTTP response messages data when writing.
+property.config-file.synopsis=Specifies the name of the configuration file for the HTTP Connection Handler.
+property.config-file.syntax.string.pattern.synopsis=A path to an existing file that is readable by the server.
 property.denied-client.synopsis=Specifies a set of host names or address masks that determine the clients that are not allowed to establish connections to this HTTP Connection Handler.
 property.denied-client.description=Valid values include a host name, a fully qualified domain name, a domain name, an IP address, or a subnetwork with subnetwork mask. If both allowed and denied client masks are defined and a client connection matches one or more masks in both lists, then the connection is denied. If only a denied list is specified, then any client not matching a mask in that list is allowed.
 property.denied-client.default-behavior.alias.synopsis=If an allow list is specified, then only clients with addresses on the allow list are allowed. Otherwise, all clients are allowed.
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
index 8b3dd12..77fa2db 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
@@ -42,6 +42,8 @@
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
+import org.forgerock.opendj.adapter.server2x.Adapters;
+import org.forgerock.opendj.rest2ldap.servlet.Rest2LDAPContextFactory;
 import org.opends.messages.Message;
 import org.opends.server.admin.std.server.ConnectionHandlerCfg;
 import org.opends.server.api.ClientConnection;
@@ -60,23 +62,17 @@
   private static final DebugTracer TRACER = getTracer();
 
   /** The connection handler that created this servlet filter. */
-  private HTTPConnectionHandler connectionHandler;
-  private final Map<ClientConnection, ClientConnection> clientConnections;
+  private final HTTPConnectionHandler connectionHandler;
 
   /**
    * Constructs a new instance of this class.
    *
    * @param connectionHandler
    *          the connection handler that accepted this connection
-   * @param clientConnections
-   *          Map where to add new connections
    */
-  public CollectClientConnectionsFilter(
-      HTTPConnectionHandler connectionHandler,
-      Map<ClientConnection, ClientConnection> clientConnections)
+  public CollectClientConnectionsFilter(HTTPConnectionHandler connectionHandler)
   {
     this.connectionHandler = connectionHandler;
-    this.clientConnections = clientConnections;
   }
 
   /** {@inheritDoc} */
@@ -91,9 +87,11 @@
   public void doFilter(ServletRequest request, ServletResponse response,
       FilterChain chain)
   {
+    final Map<ClientConnection, ClientConnection> clientConnections =
+        this.connectionHandler.getClientConnectionsMap();
     final ClientConnection clientConnection =
         new HTTPClientConnection(this.connectionHandler, request);
-    this.clientConnections.put(clientConnection, clientConnection);
+    clientConnections.put(clientConnection, clientConnection);
     try
     {
       String ipAddress = request.getRemoteAddr();
@@ -133,6 +131,18 @@
         return;
       }
 
+      // TODO JNR handle authentication + send the HTTPClientConnection
+      // to Rest2LDAP
+      Object result = Adapters.newRootConnection();
+
+      // WARNING: This action triggers 3-4 others:
+      // Set the connection for use with this request on the HttpServletRequest.
+      // It will make Rest2LDAPContextFactory create an
+      // AuthenticatedConnectionContext which will in turn ensure Rest2LDAP uses
+      // the supplied Connection object
+      request.setAttribute(Rest2LDAPContextFactory.ATTRIBUTE_AUTHN_CONNECTION,
+          result);
+
       // send the request further down the filter chain or pass to servlet
       chain.doFilter(request, response);
     }
@@ -154,7 +164,7 @@
     }
     finally
     {
-      this.clientConnections.remove(clientConnection);
+      clientConnections.remove(clientConnection);
     }
   }
 
diff --git a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
index 8c32c84..4dfaff1 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -31,6 +31,7 @@
 import static org.opends.server.loggers.debug.DebugLogger.*;
 import static org.opends.server.util.StaticUtils.*;
 
+import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.util.ArrayList;
@@ -44,6 +45,8 @@
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLContext;
@@ -51,10 +54,18 @@
 import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 
+import org.codehaus.jackson.JsonParser;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.forgerock.json.fluent.JsonValue;
+import org.forgerock.json.resource.CollectionResourceProvider;
+import org.forgerock.json.resource.ConnectionFactory;
+import org.forgerock.json.resource.Resources;
+import org.forgerock.json.resource.Router;
+import org.forgerock.json.resource.servlet.HttpServlet;
+import org.forgerock.opendj.rest2ldap.AuthorizationPolicy;
+import org.forgerock.opendj.rest2ldap.Rest2LDAP;
+import org.forgerock.opendj.rest2ldap.servlet.Rest2LDAPContextFactory;
 import org.glassfish.grizzly.http.server.HttpServer;
 import org.glassfish.grizzly.http.server.NetworkListener;
 import org.glassfish.grizzly.http.server.ServerConfiguration;
@@ -63,6 +74,7 @@
 import org.glassfish.grizzly.servlet.ServletRegistration;
 import org.glassfish.grizzly.servlet.WebappContext;
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
+import org.glassfish.grizzly.strategies.SameThreadIOStrategy;
 import org.opends.messages.Message;
 import org.opends.server.admin.server.ConfigurationChangeListener;
 import org.opends.server.admin.std.server.ConnectionHandlerCfg;
@@ -98,23 +110,6 @@
     ServerShutdownListener, AlertGenerator
 {
 
-  /**
-   * Fake Servlet.
-   * <p>
-   * TODO JNR remove when using REST2LDAP servlet
-   */
-  private static final class FakeServlet extends HttpServlet
-  {
-    @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
-        throws ServletException, IOException
-    {
-      // TODO Auto-generated method stub
-      super.doGet(req, resp);
-    }
-
-  }
-
   /** The tracer object for the debug logger. */
   private static final DebugTracer TRACER = getTracer();
 
@@ -124,6 +119,9 @@
   /** SSL instance name used in context creation. */
   private static final String SSL_CONTEXT_INSTANCE_NAME = "TLS";
 
+  private static final ObjectMapper JSON_MAPPER = new ObjectMapper().configure(
+      JsonParser.Feature.ALLOW_COMMENTS, true);
+
   /** The initialization configuration. */
   private HTTPConnectionHandlerCfg initConfig;
 
@@ -333,6 +331,16 @@
     return clientConnections.keySet();
   }
 
+  /**
+   * Gives access to the clientConnections to classes in this package.
+   *
+   * @return the Map containing the current client connections
+   */
+  Map<ClientConnection, ClientConnection> getClientConnectionsMap()
+  {
+    return clientConnections;
+  }
+
   /** {@inheritDoc} */
   @Override
   public DN getComponentEntryDN()
@@ -660,35 +668,9 @@
 
   private void startHttpServer()
   {
-    // TODO JNR stop Grizzly own logging.
-    // [testng] Mar 14, 2013 11:22:13 AM org.glassfish.grizzly.http.server.
-    // NetworkListener stop
-    // [testng] INFO: Stopped listener bound to [0.0.0.0:8080]
-    // [testng] Mar 14, 2013 11:22:19 AM org.glassfish.grizzly.servlet.
-    // WebappContext deploy
-    // [testng] INFO: Starting application [example] ...
-    // [testng] Mar 14, 2013 11:22:19 AM org.glassfish.grizzly.servlet.
-    // WebappContext initServlets
-    // [testng] INFO: [example] Servlet
-    // [org.opends.server.protocols.http.HTTPConnec
-    // tionHandler$FakeServlet] registered for url pattern(s) [[/managed/*]].
-    // [testng] Mar 14, 2013 11:22:19 AM
-    // org.glassfish.grizzly.servlet.WebappContext
-    // initFilters
-    // [testng] INFO: [example] Filter [org.opends.server.protocols.http.
-    // AllowAdressesFilter] registered for
-    // url pattern(s) [[/managed/*]] and servlet name(s) [[]]
-    // [testng] Mar 14, 2013 11:22:19 AM
-    // org.glassfish.grizzly.servlet.WebappContext
-    // deploy
-    // [testng] INFO: Application [example] is ready to service requests. Root:
-    // [/example].
-    // [testng] Mar 14, 2013 11:22:19 AM org.glassfish.grizzly.http.server.
-    // NetworkListener start
-    // [testng] INFO: Started listener bound to [0.0.0.0:8080]
-    // [testng] Mar 14, 2013 11:22:19 AM org.glassfish.grizzly.http.server.
-    // HttpServer start
-    // [testng] INFO: [HttpServer-1] Started.
+    // silence Grizzly's own logging
+    Logger.getLogger("org.glassfish.grizzly").setLevel(Level.OFF);
+
     this.httpServer =
         HttpServer.createSimpleServer("./", initConfig.getListenPort());
 
@@ -713,8 +695,11 @@
         transport.setReadBufferSize(bufferSize);
         transport.setWriteBufferSize(bufferSize);
         // TODO JNR
-        // transport.setIOStrategy(SameThreadIOStrategy.getInstance());
-        // transport.setWorkerThreadPool(threadPool);
+        transport.setIOStrategy(SameThreadIOStrategy.getInstance());
+        // ThreadPoolConfig workerPoolConfig =
+        // ThreadPoolConfig.defaultConfig().copy();
+        // workerPoolConfig.setCorePoolSize(currentConfig
+        // .getNumRequestHandlers());
         // transport.setWorkerThreadPoolConfig(workerPoolConfig);
         transport.setServerConnectionBackLog(currentConfig.getAcceptBacklog());
 
@@ -725,17 +710,11 @@
         }
       }
 
-      // TODO JNR what to use here?
-      final String displayName = "example";
-      final String contextPath = "/example";
-      final String servletName = "managed";
-      final String urlPattern = "/managed/*";
+      final String servletName = "OpenDJ Rest2LDAP servlet";
+      final String urlPattern = "/*";
+      final WebappContext ctx = new WebappContext(servletName);
 
-      // TODO JNR what to use here?
-      final WebappContext ctx = new WebappContext(displayName, contextPath);
-
-      Filter filter =
-          new CollectClientConnectionsFilter(this, clientConnections);
+      Filter filter = new CollectClientConnectionsFilter(this);
       FilterRegistration filterReg =
           ctx.addFilter("collectClientConnections", filter);
       // TODO JNR this is not working
@@ -744,8 +723,14 @@
       filterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),
           urlPattern);
 
+      ConnectionFactory connFactory =
+          getConnectionFactory(getFileForPath(this.currentConfig
+              .getConfigFile()));
+
       final ServletRegistration reg =
-          ctx.addServlet(servletName, new FakeServlet());
+          ctx.addServlet(servletName, new HttpServlet(connFactory,
+          // Used for hooking our HTTPClientConnection in Rest2LDAP
+              Rest2LDAPContextFactory.getHttpServletContextFactory()));
       reg.addMapping(urlPattern);
 
       ctx.deploy(this.httpServer);
@@ -772,6 +757,33 @@
     }
   }
 
+  private ConnectionFactory getConnectionFactory(File configFile)
+      throws Exception
+  {
+    // Parse the config file.
+    final Object content = JSON_MAPPER.readValue(configFile, Object.class);
+    if (!(content instanceof Map))
+    {
+      throw new ServletException("Servlet configuration file '" + configFile
+          + "' does not contain a valid JSON configuration");
+    }
+    final JsonValue configuration = new JsonValue(content);
+
+    // Create the router.
+    final Router router = new Router();
+    final JsonValue mappings =
+        configuration.get("servlet").get("mappings").required();
+    for (final String mappingUrl : mappings.keys())
+    {
+      final JsonValue mapping = mappings.get(mappingUrl);
+      final CollectionResourceProvider provider =
+          Rest2LDAP.builder().authorizationPolicy(AuthorizationPolicy.REUSE)
+              .configureMapping(mapping).build();
+      router.addRoute(mappingUrl, provider);
+    }
+    return Resources.newInternalConnectionFactory(router);
+  }
+
   private void stopHttpServer()
   {
     TRACER.debugInfo("Stopping HTTP server...");

--
Gitblit v1.10.0