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

Jean-Noel Rouvignac
25.58.2013 66c134a26e114d536df67dd566e57d3ec5e8aae4
OPENDJ-808 Implement a simple commons REST based HTTP connection handler

Added support for using the Rest2LDAP servlet from OpenDJ HTTPConnectionHandler.



HTTPConnectionHandler.java:
Added support for using the Rest2LDAP servlet + removed the FakeServlet.
Added the JSON_MAPPER constant to parse the JSON config file for Rest2LDAP.
Added getClientConnectionsMap() getter.
Added getConnectionFactory() method, heavily inspired from org.forgerock.opendj.rest2ldap.servlet.Rest2LDAPConnectionFactoryProvider.getConnectionFactory().
In startHttpServer(), silenced Grizzly logging + set the IOThreadStrategy to be single threaded + changed the access path to the servlet.

CollectClientConnectionsFilter.java:
Added support for using the Rest2LDAP servlet.
Removed clientConnections instance member, now get this value from the HTTPConnectionHandler.

config.ldif, 02-config.ldif, HTTPConnectionHandlerConfiguration.xml, HTTPConnectionHandlerCfgDefn.properties:
Added ds-cfg-config-file property to ds-cfg-http-connection-handler objectClass.

http-config.json: ADDED (copied from opendj-rest2ldap-servlet project)


ivy.xml:
Added dependencies to Rest2LDAP.

ivysettings.xml:
Added support for updating SNAPSHOTS.
1 files added
8 files modified
484 ■■■■ changed files
opends/ivy.xml 28 ●●●● patch | view | raw | blame | history
opends/ivysettings.xml 23 ●●●● patch | view | raw | blame | history
opends/resource/config/config.ldif 1 ●●●● patch | view | raw | blame | history
opends/resource/config/http-config.json 229 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif 9 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/HTTPConnectionHandlerConfiguration.xml 26 ●●●●● patch | view | raw | blame | history
opends/src/admin/messages/HTTPConnectionHandlerCfgDefn.properties 2 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/http/CollectClientConnectionsFilter.java 30 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/http/HTTPConnectionHandler.java 136 ●●●● patch | view | raw | blame | history
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" />
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>
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
opends/resource/config/http-config.json
New file
@@ -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" } }
                    } }
                }
            }
        }
    }
}
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'
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>
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.
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);
    }
  }
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...");