From a2e3b43459451b4cc75549bec8a9471f0c9bc76c Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <yannick.lecaillez@forgerock.com>
Date: Tue, 12 Apr 2016 14:13:06 +0000
Subject: [PATCH] OPENDJ-2755: Decouple Rest2LDAP endpoint from the HTTPConnectionHandler.

---
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContext.java                                        |   42 +
 opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPEndpointConfiguration.xml          |   80 +++
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AllowDenyFilter.java                                    |   89 +++
 opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java                                              |   12 
 opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPConnectionHandlerConfiguration.xml |   60 --
 opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/Rest2ldapEndpointConfiguration.xml     |   89 +++
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEndpoint.java                        |   65 ++
 opendj-server-legacy/src/test/java/org/opends/server/protocols/http/AuthenticationFilterTest.java                           |   10 
 opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java                                                |    8 
 opendj-server-legacy/resource/schema/02-config.ldif                                                                         |   29 +
 opendj-server-legacy/resource/config/config.ldif                                                                            |   15 
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java                              |   88 ++-
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEmbeddedHttpApplication.java         |   91 +--
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContextInjectionFilter.java                         |   85 +++
 opendj-server-legacy/pom.xml                                                                                                |   27 +
 opendj-server-legacy/src/main/java/org/opends/server/api/HttpEndpoint.java                                                  |   99 ++++
 opendj-server-legacy/src/messages/org/opends/messages/config.properties                                                     |   11 
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AuthenticationFilter.java                               |  172 ++-----
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/package-info.java                             |   23 +
 opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/RootConfiguration.xml                  |   15 
 opendj-server-legacy/src/main/java/org/opends/server/core/HttpEndpointConfigManager.java                                    |  227 +++++++++
 opendj-server-legacy/src/test/java/org/opends/server/tools/dsconfig/DsconfigOptionsTestCase.java                            |    3 
 opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPAuthenticationConfig.java                           |    4 
 23 files changed, 1,068 insertions(+), 276 deletions(-)

diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPConnectionHandlerConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPConnectionHandlerConfiguration.xml
index 2029960..62acaf2 100644
--- a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPConnectionHandlerConfiguration.xml
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPConnectionHandlerConfiguration.xml
@@ -20,12 +20,10 @@
   xmlns:adm="http://opendj.forgerock.org/admin"
   xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
   <adm:synopsis>
-    The
-    <adm:user-friendly-name />
-    is used to interact with clients using HTTP.
+    <adm:user-friendly-plural-name /> provide HTTP services built on top of the underlying LDAP directory.
   </adm:synopsis>
   <adm:description>
-    It provides full support for Rest2LDAP.
+    It routes HTTP requests to HTTP endpoints registered in the configuration.
   </adm:description>
   <adm:constraint>
     <adm:synopsis>
@@ -478,60 +476,6 @@
       </ldap:attribute>
     </adm:profile>
   </adm:property>
-  <adm:property name="config-file" mandatory="true">
-    <adm:synopsis>
-      Specifies the name of the configuration file for the <adm:user-friendly-name />.
-    </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:property name="authentication-required" mandatory="true">
-    <adm:synopsis>
-      Specifies whether only authenticated requests can be processed by the
-      <adm:user-friendly-name />.
-    </adm:synopsis>
-    <adm:description>
-      If true, only authenticated requests will be processed by the
-      <adm:user-friendly-name />. If false, both authenticated requests and 
-      unauthenticated requests will be processed. All requests are subject
-      to ACI limitations and unauthenticated requests are subject to server
-      limits like maximum number of entries returned. Note that setting
-      ds-cfg-reject-unauthenticated-requests to true will override the current
-      setting.
-    </adm:description>
-    <adm:default-behavior>
-      <adm:defined>
-        <adm:value>true</adm:value>
-      </adm:defined>
-    </adm:default-behavior>
-    <adm:syntax>
-      <adm:boolean />
-    </adm:syntax>
-    <adm:profile name="ldap">
-      <ldap:attribute>
-        <ldap:name>ds-cfg-authentication-required</ldap:name>
-      </ldap:attribute>
-    </adm:profile>
-  </adm:property>
   <adm:property name="max-concurrent-ops-per-connection">
     <adm:synopsis>
       Specifies the maximum number of internal operations that each
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPEndpointConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPEndpointConfiguration.xml
new file mode 100644
index 0000000..1335396
--- /dev/null
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/HTTPEndpointConfiguration.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  The contents of this file are subject to the terms of the Common Development and
+  Distribution License (the License). You may not use this file except in compliance with the
+  License.
+
+  You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+  specific language governing permission and limitations under the License.
+
+  When distributing Covered Software, include this CDDL Header Notice in each file and include
+  the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+  Header, with the fields enclosed by brackets [] replaced by your own identifying
+  information: "Portions Copyright [year] [name of copyright owner]".
+
+  Copyright 2016 ForgeRock AS.
+  ! -->
+<adm:managed-object name="http-endpoint" plural-name="http-endpoints"
+	package="org.forgerock.opendj.server.config" xmlns:adm="http://opendj.forgerock.org/admin"
+	xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
+	<adm:synopsis>
+		The
+		<adm:user-friendly-name />
+		is used to define HTTP endpoint.
+	</adm:synopsis>
+	<adm:profile name="ldap">
+		<ldap:object-class>
+			<ldap:name>ds-cfg-http-endpoint</ldap:name>
+			<ldap:superior>top</ldap:superior>
+		</ldap:object-class>
+	</adm:profile>
+	<adm:property name="enabled" mandatory="true">
+		<adm:synopsis>
+			Indicates whether the
+			<adm:user-friendly-name />
+			is enabled.
+		</adm:synopsis>
+		<adm:syntax>
+			<adm:boolean />
+		</adm:syntax>
+		<adm:profile name="ldap">
+			<ldap:attribute>
+				<ldap:name>ds-cfg-enabled</ldap:name>
+			</ldap:attribute>
+		</adm:profile>
+	</adm:property>
+	<adm:property name="java-class" mandatory="true">
+		<adm:synopsis>
+			Specifies the fully-qualified name of the Java class that provides
+			the
+			<adm:user-friendly-name />
+			implementation.
+		</adm:synopsis>
+		<adm:syntax>
+			<adm:java-class>
+				<adm:instance-of>
+					org.opends.server.api.HttpEndpoint
+				</adm:instance-of>
+			</adm:java-class>
+		</adm:syntax>
+		<adm:profile name="ldap">
+			<ldap:attribute>
+				<ldap:name>ds-cfg-java-class</ldap:name>
+			</ldap:attribute>
+		</adm:profile>
+	</adm:property>
+	<adm:property name="base-path" mandatory="true" read-only="true">
+		<adm:synopsis>
+			All HTTP requests matching the base path or subordinate to it will be routed to the HTTP endpoint
+			unless a more specific HTTP endpoint is found.
+		</adm:synopsis>
+		<adm:syntax>
+			<adm:string />
+		</adm:syntax>
+		<adm:profile name="ldap">
+			<ldap:attribute>
+				<ldap:name>ds-cfg-base-path</ldap:name>
+			</ldap:attribute>
+		</adm:profile>
+	</adm:property>
+</adm:managed-object>
\ No newline at end of file
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/Rest2ldapEndpointConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/Rest2ldapEndpointConfiguration.xml
new file mode 100644
index 0000000..8471525
--- /dev/null
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/Rest2ldapEndpointConfiguration.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  The contents of this file are subject to the terms of the Common Development and
+  Distribution License (the License). You may not use this file except in compliance with the
+  License.
+
+  You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+  specific language governing permission and limitations under the License.
+
+  When distributing Covered Software, include this CDDL Header Notice in each file and include
+  the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+  Header, with the fields enclosed by brackets [] replaced by your own identifying
+  information: "Portions Copyright [year] [name of copyright owner]".
+
+  Copyright 2016 ForgeRock AS.
+  ! -->
+<adm:managed-object name="rest2ldap-endpoint"
+	plural-name="rest2ldap-endpoints" extends="http-endpoint"
+	package="org.forgerock.opendj.server.config" xmlns:adm="http://opendj.forgerock.org/admin"
+	xmlns:ldap="http://opendj.forgerock.org/admin-ldap">
+
+	<adm:synopsis>
+		The <adm:user-friendly-name /> provides RESTful access to LDAP application data using a set of 
+		customizable data transformations.
+	</adm:synopsis>
+	<adm:profile name="ldap">
+		<ldap:object-class>
+			<ldap:name>ds-cfg-rest2ldap-endpoint</ldap:name>
+			<ldap:superior>ds-cfg-http-endpoint</ldap:superior>
+		</ldap:object-class>
+	</adm:profile>
+	<adm:property-override name="java-class"
+		advanced="true">
+		<adm:default-behavior>
+			<adm:defined>
+				<adm:value>
+					org.opends.server.protocols.http.rest2ldap.Rest2LdapEndpoint
+				</adm:value>
+			</adm:defined>
+		</adm:default-behavior>
+	</adm:property-override>
+	<adm:property name="config-url" mandatory="true">
+		<adm:synopsis>
+			URL of the REST2LDAP configuration file.
+		</adm:synopsis>
+		<adm:syntax>
+          <adm:pattern>
+            <adm:regex>.*</adm:regex>
+            <adm:usage>URL</adm:usage>
+            <adm:synopsis>
+              An URL to an existing file that is readable by the server.
+            </adm:synopsis>
+          </adm:pattern>
+		</adm:syntax>
+		<adm:profile name="ldap">
+			<ldap:attribute>
+				<ldap:name>ds-cfg-config-url</ldap:name>
+			</ldap:attribute>
+		</adm:profile>
+	</adm:property>
+	<adm:property name="authentication-required" mandatory="true">
+    <adm:synopsis>
+      Specifies whether only authenticated requests can be processed by the
+      <adm:user-friendly-name />.
+    </adm:synopsis>
+    <adm:description>
+      If true, only authenticated requests will be processed by the
+      <adm:user-friendly-name />. If false, both authenticated requests and 
+      unauthenticated requests will be processed. All requests are subject
+      to ACI limitations and unauthenticated requests are subject to server
+      limits like maximum number of entries returned. Note that setting
+      ds-cfg-reject-unauthenticated-requests to true will override the current
+      setting.
+    </adm:description>
+    <adm:default-behavior>
+      <adm:defined>
+        <adm:value>true</adm:value>
+      </adm:defined>
+    </adm:default-behavior>
+    <adm:syntax>
+      <adm:boolean />
+    </adm:syntax>
+    <adm:profile name="ldap">
+      <ldap:attribute>
+        <ldap:name>ds-cfg-authentication-required</ldap:name>
+      </ldap:attribute>
+    </adm:profile>
+  </adm:property>
+</adm:managed-object>
diff --git a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/RootConfiguration.xml b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/RootConfiguration.xml
index 6ef1c00..4b68051 100644
--- a/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/RootConfiguration.xml
+++ b/opendj-maven-plugin/src/main/resources/config/xml/org/forgerock/opendj/server/config/RootConfiguration.xml
@@ -13,7 +13,7 @@
   information: "Portions Copyright [year] [name of copyright owner]".
 
   Copyright 2007-2010 Sun Microsystems, Inc.
-  Portions Copyright 2011-2014 ForgeRock AS.
+  Portions Copyright 2011-2016 ForgeRock AS.
   ! -->
 <adm:root-managed-object xmlns:adm="http://opendj.forgerock.org/admin"
   xmlns:ldap="http://opendj.forgerock.org/admin-ldap"
@@ -423,6 +423,19 @@
       <ldap:rdn-sequence>cn=Administration Connector,cn=config</ldap:rdn-sequence>
     </adm:profile>
   </adm:relation>
+  <adm:relation name="http-endpoint">
+    <adm:one-to-many naming-property="base-path" />
+    <adm:profile name="ldap">
+      <ldap:rdn-sequence>
+        cn=HTTP Endpoints,cn=config
+      </ldap:rdn-sequence>
+    </adm:profile>
+    <adm:profile name="cli">
+      <cli:relation>
+        <cli:default-property name="enabled" />
+      </cli:relation>
+    </adm:profile>
+  </adm:relation>
   <adm:product-name>OpenDJ</adm:product-name>
   <adm:tag-definition name="logging">
     <adm:synopsis>Logging</adm:synopsis>
diff --git a/opendj-server-legacy/pom.xml b/opendj-server-legacy/pom.xml
index 9343172..3b6677c 100644
--- a/opendj-server-legacy/pom.xml
+++ b/opendj-server-legacy/pom.xml
@@ -315,6 +315,33 @@
                     </execute>
                   </action>
                 </pluginExecution>
+                <pluginExecution>
+                	<pluginExecutionFilter>
+                		<groupId>org.forgerock.opendj</groupId>
+                		<artifactId>opendj-maven-plugin</artifactId>
+                		<versionRange>[4.0.0-SNAPSHOT,)</versionRange>
+                		<goals>
+                			<goal>generate-manifest</goal>
+                			<goal>concat</goal>
+                		</goals>
+                	</pluginExecutionFilter>
+                	<action>
+                		<ignore></ignore>
+                	</action>
+                </pluginExecution>
+                <pluginExecution>
+                	<pluginExecutionFilter>
+                		<groupId>org.forgerock.opendj</groupId>
+                		<artifactId>opendj-doc-maven-plugin</artifactId>
+                		<versionRange>[4.0.0-SNAPSHOT,)</versionRange>
+                		<goals>
+                			<goal>generate-config-ref</goal>
+                		</goals>
+                	</pluginExecutionFilter>
+                	<action>
+                		<ignore></ignore>
+                	</action>
+                </pluginExecution>
               </pluginExecutions>
             </lifecycleMappingMetadata>
           </configuration>
diff --git a/opendj-server-legacy/resource/config/config.ldif b/opendj-server-legacy/resource/config/config.ldif
index 423bfaa..c08f66d 100644
--- a/opendj-server-legacy/resource/config/config.ldif
+++ b/opendj-server-legacy/resource/config/config.ldif
@@ -373,8 +373,21 @@
 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=HTTP Endpoints,cn=config
+objectClass: top
+objectClass: ds-cfg-branch
+cn: HTTP Endpoints
+
+dn: ds-cfg-base-path=/api,cn=HTTP Endpoints,cn=config
+objectClass: top
+objectClass: ds-cfg-http-endpoint
+objectClass: ds-cfg-rest2ldap-endpoint
+ds-cfg-enabled: true
+ds-cfg-java-class: org.opends.server.protocols.http.rest2ldap.Rest2LdapEndpoint
+ds-cfg-base-path: /api
 ds-cfg-authentication-required: true
+ds-cfg-config-url: config/http-config.json
 
 dn: cn=LDIF Connection Handler,cn=Connection Handlers,cn=config
 objectClass: top
diff --git a/opendj-server-legacy/resource/schema/02-config.ldif b/opendj-server-legacy/resource/schema/02-config.ldif
index 9284c0c..865b1ec 100644
--- a/opendj-server-legacy/resource/schema/02-config.ldif
+++ b/opendj-server-legacy/resource/schema/02-config.ldif
@@ -3836,6 +3836,18 @@
   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.160
+  NAME 'ds-cfg-base-path'
+  EQUALITY caseIgnoreMatch
+  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
+  SINGLE-VALUE
+  X-ORIGIN 'OpenDJ Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.36733.2.1.1.161
+  NAME 'ds-cfg-config-url'
+  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
@@ -3965,8 +3977,6 @@
         ds-cfg-ssl-cipher-suite $
         ds-cfg-max-blocked-write-time-limit $
         ds-cfg-buffer-size $
-        ds-cfg-config-file $
-        ds-cfg-authentication-required $
         ds-cfg-max-concurrent-ops-per-connection )
   X-ORIGIN 'OpenDJ Directory Server' )
 objectClasses: ( 1.3.6.1.4.1.26027.1.2.14
@@ -5869,3 +5879,18 @@
   STRUCTURAL
   MAY ds-cfg-bcrypt-cost
   X-ORIGIN 'OpenDJ Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.33
+  NAME 'ds-cfg-http-endpoint'
+  SUP top
+  STRUCTURAL
+  MUST ( ds-cfg-base-path $
+         ds-cfg-enabled $
+         ds-cfg-java-class )
+  X-ORIGIN 'OpenDJ Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.36733.2.1.2.34
+  NAME 'ds-cfg-rest2ldap-endpoint'
+  SUP ds-cfg-http-endpoint
+  STRUCTURAL
+  MUST ( ds-cfg-authentication-required $
+         ds-cfg-config-url )
+  X-ORIGIN 'OpenDJ Directory Server' )
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/api/HttpEndpoint.java b/opendj-server-legacy/src/main/java/org/opends/server/api/HttpEndpoint.java
new file mode 100644
index 0000000..8fea4d9
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/api/HttpEndpoint.java
@@ -0,0 +1,99 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.api;
+
+import static org.opends.messages.ConfigMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
+import java.util.List;
+
+import org.forgerock.http.HttpApplication;
+import org.forgerock.http.HttpApplicationException;
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.opendj.server.config.server.HTTPEndpointCfg;
+import org.opends.server.types.InitializationException;
+
+/**
+ * Endpoint attach an {@link HttpApplication} to an URI.
+ *
+ * @param <C>
+ *          Type of the configuration used by this {@link HttpEndpoint}
+ */
+public abstract class HttpEndpoint<C extends HTTPEndpointCfg>
+{
+  /** Configuration of this endpoint. */
+  protected final C configuration;
+
+  /**
+   * Create a new {@link HttpEndpoint} with the given configuration.
+   *
+   * @param configuration
+   *          Configuration of this {@link HttpEndpoint}.
+   */
+  public HttpEndpoint(C configuration)
+  {
+    this.configuration = configuration;
+  }
+
+  /**
+   * Check that the configuration of this {@link HttpEndpoint} is valid. This
+   * default implementation try to instantiate and start the underlying
+   * {@link HttpApplication}.
+   *
+   * @param unacceptableReasons
+   *          A list that can be used to hold messages about why the
+   *          configuration is not acceptable.
+   * @return true if the configuration is valid.
+   */
+  public boolean isConfigurationValid(List<LocalizableMessage> unacceptableReasons)
+  {
+    HttpApplication dummyApplication = null;
+    try
+    {
+      dummyApplication = newHttpApplication();
+      dummyApplication.start();
+      return true;
+    }
+    catch (HttpApplicationException e)
+    {
+      unacceptableReasons.add(ERR_CONFIG_HTTPENDPOINT_INVALID_CONFIGURATION
+          .get(configuration.dn(), stackTraceToSingleLineString(e)));
+      return false;
+    }
+    catch (InitializationException ie)
+    {
+      unacceptableReasons.add(ie.getMessageObject());
+      return false;
+    }
+    finally
+    {
+      if (dummyApplication != null)
+      {
+        dummyApplication.stop();
+      }
+    }
+  }
+
+  /**
+   * Create a new HttpApplication.
+   *
+   * @return an {@link HttpApplication} configured and ready to be started.
+   * @throws InitializationException
+   *           If the application cannot be created.
+   */
+  public abstract HttpApplication newHttpApplication() throws InitializationException;
+
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java b/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
index dfbc121..9ab6d56 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -60,6 +60,7 @@
 import javax.management.MBeanServer;
 import javax.management.MBeanServerFactory;
 
+import org.forgerock.http.routing.Router;
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
@@ -672,6 +673,8 @@
   /** Entry point to common audit service, where all audit events must be published. */
   private CommonAudit commonAudit;
 
+  private Router httpRouter;
+
   /** Class that prints the version of OpenDJ server to System.out. */
   public static final class DirectoryServerVersionHandler implements VersionHandler
   {
@@ -1022,6 +1025,11 @@
     }
 
     @Override
+    public Router getHTTPRouter() {
+     return directoryServer.httpRouter;
+    }
+
+    @Override
     public CommonAudit getCommonAudit()
     {
       return directoryServer.commonAudit;
@@ -1463,6 +1471,7 @@
       configurationHandler.reinitializeWithFullSchema(schema.getSchemaNG());
 
       commonAudit = new CommonAudit(serverContext);
+      httpRouter = new Router();
 
       // Allow internal plugins to be registered.
       pluginConfigManager.initializePluginConfigManager();
@@ -1513,6 +1522,9 @@
       identityMapperConfigManager = new IdentityMapperConfigManager(serverContext);
       identityMapperConfigManager.initializeIdentityMappers();
 
+      new HttpEndpointConfigManager(httpRouter)
+        .registerTo(serverContext.getServerManagementContext().getRootConfiguration());
+
       initializeRootDNConfigManager();
 
       initializeAuthenticatedUsers();
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/HttpEndpointConfigManager.java b/opendj-server-legacy/src/main/java/org/opends/server/core/HttpEndpointConfigManager.java
new file mode 100644
index 0000000..200b782
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/HttpEndpointConfigManager.java
@@ -0,0 +1,227 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.core;
+
+import static org.forgerock.http.routing.RouteMatchers.*;
+import static org.opends.messages.ConfigMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.forgerock.http.HttpApplication;
+import org.forgerock.http.HttpApplicationException;
+import org.forgerock.http.routing.Router;
+import org.forgerock.http.routing.RoutingMode;
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.config.server.ConfigChangeResult;
+import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.config.server.ConfigurationAddListener;
+import org.forgerock.opendj.config.server.ConfigurationChangeListener;
+import org.forgerock.opendj.config.server.ConfigurationDeleteListener;
+import org.forgerock.opendj.ldap.DN;
+import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.server.config.meta.HTTPEndpointCfgDefn;
+import org.forgerock.opendj.server.config.server.HTTPEndpointCfg;
+import org.forgerock.opendj.server.config.server.RootCfg;
+import org.opends.server.api.HttpEndpoint;
+import org.opends.server.types.InitializationException;
+
+/**
+ * This class defines a utility that will be used to manage the set of HTTP
+ * endpoints defined in the Directory Server. It will initialize the HTTP
+ * endpoints when the server starts, and then will manage any additions,
+ * removals, or modifications to any HTTP endpoints while the server is running.
+ */
+public class HttpEndpointConfigManager implements ConfigurationChangeListener<HTTPEndpointCfg>,
+    ConfigurationAddListener<HTTPEndpointCfg>, ConfigurationDeleteListener<HTTPEndpointCfg>
+{
+  private static final LocalizedLogger LOGGER = LocalizedLogger.getLoggerForThisClass();
+
+  private final Router router;
+  private final Map<DN, HttpApplication> applications;
+
+  /**
+   * Creates a new instance of this HTTP endpoint config manager.
+   *
+   * @param router
+   *          The {@link Router} where to register configured {@link HttpEndpoint}
+   */
+  public HttpEndpointConfigManager(Router router)
+  {
+    this.router = router;
+    this.applications = new HashMap<>();
+  }
+
+  /**
+   * Initializes all HTTP endpoints currently defined in the Directory Server
+   * configuration. This should only be called at Directory Server startup.
+   *
+   * @param rootConfiguration
+   *          The root configuration containing the {@link HttpEndpoint}
+   *          configurations.
+   * @throws ConfigException
+   *           If a configuration problem causes the {@link HttpEndpoint}
+   *           initialization process to fail.
+   */
+  public void registerTo(RootCfg rootConfiguration) throws ConfigException
+  {
+    rootConfiguration.addHTTPEndpointAddListener(this);
+    rootConfiguration.addHTTPEndpointDeleteListener(this);
+
+    for (String endpointName : rootConfiguration.listHTTPEndpoints())
+    {
+      final HTTPEndpointCfg configuration = rootConfiguration.getHTTPEndpoint(endpointName);
+      configuration.addChangeListener(this);
+
+      if (configuration.isEnabled())
+      {
+        final ConfigChangeResult result = applyConfigurationAdd(configuration);
+        if (!result.getResultCode().equals(ResultCode.SUCCESS))
+        {
+          LOGGER.error(result.getMessages().get(0));
+        }
+      }
+    }
+  }
+
+  @Override
+  public boolean isConfigurationAddAcceptable(HTTPEndpointCfg configuration,
+      List<LocalizableMessage> unacceptableReasons)
+  {
+    try
+    {
+      return loadEndpoint(configuration).isConfigurationValid(unacceptableReasons);
+    }
+    catch (InitializationException ie)
+    {
+      unacceptableReasons.add(ie.getMessageObject());
+      return false;
+    }
+  }
+
+  @Override
+  public ConfigChangeResult applyConfigurationAdd(HTTPEndpointCfg configuration)
+  {
+    final ConfigChangeResult ccr = new ConfigChangeResult();
+    configuration.addChangeListener(this);
+    if (!configuration.isEnabled())
+    {
+      return ccr;
+    }
+
+    try
+    {
+      final HttpApplication application = loadEndpoint(configuration).newHttpApplication();
+      router.addRoute(
+          requestUriMatcher(RoutingMode.STARTS_WITH, removeLeadingAndTrailingSlashes(configuration.getBasePath())),
+          application.start());
+      applications.put(configuration.dn(), application);
+    }
+    catch (HttpApplicationException e)
+    {
+      ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode());
+      ccr.addMessage(ERR_CONFIG_HTTPENDPOINT_UNABLE_TO_START.get(configuration.dn(), stackTraceToSingleLineString(e)));
+    }
+    catch (InitializationException ie)
+    {
+      ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode());
+      ccr.addMessage(ie.getMessageObject());
+    }
+
+    return ccr;
+  }
+
+  @Override
+  public boolean isConfigurationDeleteAcceptable(HTTPEndpointCfg configuration,
+      List<LocalizableMessage> unacceptableReasons)
+  {
+    return true;
+  }
+
+  @Override
+  public ConfigChangeResult applyConfigurationDelete(HTTPEndpointCfg configuration)
+  {
+    router.removeRoute(
+        requestUriMatcher(RoutingMode.STARTS_WITH, removeLeadingAndTrailingSlashes(configuration.getBasePath())));
+    final HttpApplication application = applications.remove(configuration.dn());
+    if (application != null)
+    {
+      application.stop();
+    }
+    return new ConfigChangeResult();
+  }
+
+  @Override
+  public boolean isConfigurationChangeAcceptable(HTTPEndpointCfg configuration,
+      List<LocalizableMessage> unacceptableReasons)
+  {
+    try
+    {
+      return loadEndpoint(configuration).isConfigurationValid(unacceptableReasons);
+    }
+    catch (InitializationException e)
+    {
+      return false;
+    }
+  }
+
+  @Override
+  public ConfigChangeResult applyConfigurationChange(HTTPEndpointCfg configuration)
+  {
+    applyConfigurationDelete(configuration);
+    applyConfigurationAdd(configuration);
+    return new ConfigChangeResult();
+  }
+
+  @SuppressWarnings("unchecked")
+  private HttpEndpoint<?> loadEndpoint(HTTPEndpointCfg configuration) throws InitializationException
+  {
+    try
+    {
+      final Class<? extends HttpEndpoint<?>> endpointClass =
+          (Class<? extends HttpEndpoint<?>>) HTTPEndpointCfgDefn.getInstance().getJavaClassPropertyDefinition()
+              .loadClass(configuration.getJavaClass(), HttpEndpoint.class);
+      return endpointClass.getDeclaredConstructor(configuration.configurationClass()).newInstance(configuration);
+    }
+    catch (Exception e)
+    {
+      throw new InitializationException(ERR_CONFIG_HTTPENDPOINT_INITIALIZATION_FAILED.get(configuration.getJavaClass(),
+          configuration.dn(), stackTraceToSingleLineString(e)), e);
+    }
+  }
+
+  private static String removeLeadingAndTrailingSlashes(String path)
+  {
+    // Remove leading /
+    int start = 0;
+    while (path.charAt(start) == '/')
+    {
+      start++;
+    }
+
+    // Remove trailing /
+    int end = path.length();
+    while (path.charAt(end - 1) == '/')
+    {
+      end--;
+    }
+
+    return path.substring(start, end);
+  }
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java b/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
index 6ff7494..1dc13a3 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
@@ -15,6 +15,7 @@
  */
 package org.opends.server.core;
 
+import org.forgerock.http.routing.Router;
 import org.forgerock.opendj.config.server.ServerManagementContext;
 import org.forgerock.opendj.server.config.server.RootCfg;
 import org.opends.server.extensions.DiskSpaceMonitor;
@@ -98,6 +99,13 @@
   DiskSpaceMonitor getDiskSpaceMonitor();
 
   /**
+   * Returns the HTTP request router.
+   *
+   * @return the HTTP Router service
+   */
+  Router getHTTPRouter();
+
+  /**
    * Returns the common audit manager.
    *
    * @return the common audit manager
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AllowDenyFilter.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AllowDenyFilter.java
new file mode 100644
index 0000000..12fd6cc
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AllowDenyFilter.java
@@ -0,0 +1,89 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.protocols.http;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collection;
+
+import org.forgerock.http.Filter;
+import org.forgerock.http.Handler;
+import org.forgerock.http.protocol.Request;
+import org.forgerock.http.protocol.Response;
+import org.forgerock.http.protocol.Status;
+import org.forgerock.opendj.ldap.AddressMask;
+import org.forgerock.services.context.ClientContext;
+import org.forgerock.services.context.Context;
+import org.forgerock.util.promise.NeverThrowsException;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.Promises;
+
+/**
+ * Implements a blacklist/whitelist to prevent denied clients to perform
+ * requests.
+ */
+final class AllowDenyFilter implements Filter
+{
+  private final Collection<AddressMask> deniedClients;
+  private final Collection<AddressMask> allowedClients;
+
+  AllowDenyFilter(Collection<AddressMask> deniedClients, Collection<AddressMask> allowedClients)
+  {
+    this.deniedClients = deniedClients;
+    this.allowedClients = allowedClients;
+  }
+
+  @Override
+  public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next)
+  {
+    final InetAddress clientAddress;
+    try
+    {
+      // The remote address will always be an IP address, so no reverse lookup will be performed.
+      clientAddress = InetAddress.getByName(context.asContext(ClientContext.class).getRemoteAddress());
+    }
+    catch (UnknownHostException e)
+    {
+      return internalError();
+    }
+
+    // Check to see if the client is on the denied list. If so, then reject it immediately.
+    if (!deniedClients.isEmpty() && AddressMask.matchesAny(deniedClients, clientAddress))
+    {
+      return forbidden();
+    }
+
+    // Check to see if there is an allowed list and if there is whether the client is on that list.
+    // If not, then reject the connection.
+    if (!allowedClients.isEmpty() && !AddressMask.matchesAny(allowedClients, clientAddress))
+    {
+      return forbidden();
+    }
+
+    return next.handle(context, request);
+  }
+
+  static final Promise<Response, NeverThrowsException> forbidden()
+  {
+    return Promises.newResultPromise(new Response(Status.FORBIDDEN));
+  }
+
+  static final Promise<Response, NeverThrowsException> internalError()
+  {
+    return Promises.newResultPromise(new Response(Status.INTERNAL_SERVER_ERROR));
+  }
+
+}
\ No newline at end of file
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/CollectClientConnectionsFilter.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AuthenticationFilter.java
similarity index 65%
rename from opendj-server-legacy/src/main/java/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
rename to opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AuthenticationFilter.java
index f8101ed..3293058 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/CollectClientConnectionsFilter.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/AuthenticationFilter.java
@@ -15,13 +15,13 @@
  */
 package org.opends.server.protocols.http;
 
+import static org.opends.messages.ProtocolMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
 import java.io.Closeable;
 import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.nio.charset.Charset;
 import java.text.ParseException;
-import java.util.Collection;
 
 import org.forgerock.http.Handler;
 import org.forgerock.http.protocol.Request;
@@ -31,7 +31,6 @@
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.json.resource.ResourceException;
 import org.forgerock.opendj.adapter.server3x.Adapters;
-import org.forgerock.opendj.ldap.AddressMask;
 import org.forgerock.opendj.ldap.Connection;
 import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.Filter;
@@ -44,24 +43,18 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.forgerock.opendj.rest2ldap.AuthenticatedConnectionContext;
 import org.forgerock.opendj.rest2ldap.Rest2LDAP;
+import org.forgerock.services.context.ClientContext;
 import org.forgerock.services.context.Context;
 import org.forgerock.services.context.SecurityContext;
 import org.forgerock.util.AsyncFunction;
 import org.forgerock.util.promise.NeverThrowsException;
 import org.forgerock.util.promise.Promise;
 import org.forgerock.util.promise.Promises;
-import org.forgerock.opendj.server.config.server.ConnectionHandlerCfg;
-import org.opends.server.core.ServerContext;
 import org.opends.server.schema.SchemaConstants;
-import org.opends.server.types.DisconnectReason;
 import org.opends.server.util.Base64;
 
-import static org.opends.messages.ProtocolMessages.*;
-import static org.opends.server.loggers.AccessLogger.*;
-import static org.opends.server.util.StaticUtils.*;
-
 /** Servlet {@link Filter} that collects information about client connections. */
-final class CollectClientConnectionsFilter implements org.forgerock.http.Filter, Closeable
+public final class AuthenticationFilter implements org.forgerock.http.Filter, Closeable
 {
 
   /** HTTP Header sent by the client with HTTP basic authentication. */
@@ -70,119 +63,61 @@
   /** The tracer object for the debug logger. */
   private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
-  /** The connection handler that created this servlet filter. */
-  private final HTTPConnectionHandler connectionHandler;
   /**
    * Configures how to perform the search for the username prior to
    * authentication.
    */
   private final HTTPAuthenticationConfig authConfig;
 
-  private final ServerContext serverContext;
+  private final boolean authenticationRequired;
 
   /**
    * Constructs a new instance of this class.
-   * @param serverContext
-   *            The server context.
-   * @param connectionHandler
-   *          the connection handler that accepted this connection
+   *
    * @param authenticationConfig
    *          configures how to perform the search for the username prior to
    *          authentication
+   * @param authenticationRequired
+   *          If true, only authenticated requests will be accepted.
    */
-  public CollectClientConnectionsFilter(ServerContext serverContext, HTTPConnectionHandler connectionHandler,
-      HTTPAuthenticationConfig authenticationConfig)
+  public AuthenticationFilter(HTTPAuthenticationConfig authenticationConfig, boolean authenticationRequired)
   {
-    this.serverContext = serverContext;
-    this.connectionHandler = connectionHandler;
     this.authConfig = authenticationConfig;
+    this.authenticationRequired = authenticationRequired;
   }
 
   @Override
   public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next)
   {
-    final HTTPClientConnection clientConnection =
-        new HTTPClientConnection(serverContext, this.connectionHandler, context, request);
-    connectionHandler.addClientConnection(clientConnection);
-
-    if (connectionHandler.keepStats())
-    {
-      connectionHandler.getStatTracker().addRequest(request.getMethod());
-    }
-
     try
     {
-      if (!canProcessRequest(clientConnection))
-      {
-        return resourceExceptionToPromise(ResourceException.getException(ResourceException.INTERNAL_ERROR));
-      }
-      // Logs the connect after all the possible disconnect reasons have been checked.
-      logConnect(clientConnection);
-      final Connection connection = new SdkConnectionAdapter(clientConnection);
-
+      final Connection ldapConnection = context.asContext(LDAPContext.class).getLdapConnectionFactory().getConnection();
       final String[] userCredentials = extractUsernamePassword(request);
       if (userCredentials != null && userCredentials.length == 2)
       {
         final String userName = userCredentials[0];
         final String password = userCredentials[1];
-
         return Adapters.newRootConnection()
-            .searchSingleEntryAsync(buildSearchRequest(userName))
-            .thenAsync(doBindAfterSearch(context, request, next, userName, password, clientConnection, connection),
-                       returnErrorAfterFailedSearch(clientConnection));
+                       .searchSingleEntryAsync(buildSearchRequest(userName))
+                       .thenAsync(doBindAfterSearch(context, request, next, userName, password, ldapConnection),
+                                  returnErrorAfterFailedSearch(context.asContext(ClientContext.class)));
       }
-      else if (this.connectionHandler.acceptUnauthenticatedRequests())
+      else if (authenticationRequired)
       {
-        // Use unauthenticated user
-        return doFilter(context, request, next, connection);
+        return authenticationFailure(context.asContext(ClientContext.class));
       }
       else
       {
-        return authenticationFailure(clientConnection);
+        // Use unauthenticated user
+        return doFilter(context, request, next, ldapConnection);
       }
     }
     catch (Exception e)
     {
-      return asErrorResponse(e, clientConnection);
+      return asErrorResponse(e, context.asContext(ClientContext.class));
     }
   }
 
-  private boolean canProcessRequest(final HTTPClientConnection connection) throws UnknownHostException
-  {
-    final InetAddress clientAddr = connection.getRemoteAddress();
-
-    // Check to see if the core server rejected the connection (e.g. already too many connections established).
-    if (connection.getConnectionID() < 0)
-    {
-      connection.disconnect(
-          DisconnectReason.ADMIN_LIMIT_EXCEEDED, true, ERR_CONNHANDLER_REJECTED_BY_SERVER.get());
-      return false;
-    }
-
-    // Check to see if the client is on the denied list. If so, then reject it immediately.
-    final ConnectionHandlerCfg config = this.connectionHandler.getCurrentConfig();
-    final Collection<AddressMask> deniedClients = config.getDeniedClient();
-    if (!deniedClients.isEmpty()
-        && AddressMask.matchesAny(deniedClients, clientAddr))
-    {
-      connection.disconnect(DisconnectReason.CONNECTION_REJECTED, false,
-          ERR_CONNHANDLER_DENIED_CLIENT.get(connection.getClientHostPort(), connection.getServerHostPort()));
-      return false;
-    }
-
-    // Check to see if there is an allowed list and if there is whether the client is on that list.
-    // If not, then reject the connection.
-    final Collection<AddressMask> allowedClients = config.getAllowedClient();
-    if (!allowedClients.isEmpty()
-        && !AddressMask.matchesAny(allowedClients, clientAddr))
-    {
-      connection.disconnect(DisconnectReason.CONNECTION_REJECTED, false,
-          ERR_CONNHANDLER_DISALLOWED_CLIENT.get(connection.getClientHostPort(), connection.getServerHostPort()));
-      return false;
-    }
-    return true;
-  }
-
   private SearchRequest buildSearchRequest(String userName)
   {
     // Use configured rights to find the user DN
@@ -191,40 +126,40 @@
         authConfig.getSearchBaseDN(), authConfig.getSearchScope(), filter, SchemaConstants.NO_ATTRIBUTES);
   }
 
-  private AsyncFunction<SearchResultEntry, Response, NeverThrowsException> doBindAfterSearch(
-      final Context context, final Request request, final Handler next, final String userName, final String password,
-      final HTTPClientConnection clientConnection, final Connection connection)
+  private AsyncFunction<SearchResultEntry, Response, NeverThrowsException> doBindAfterSearch(final Context context,
+      final Request request, final Handler next, final String userName, final String password,
+      final Connection connection)
   {
+    final ClientContext clientContext = context.asContext(ClientContext.class);
     return new AsyncFunction<SearchResultEntry, Response, NeverThrowsException>()
     {
       @Override
       public Promise<Response, NeverThrowsException> apply(final SearchResultEntry resultEntry)
       {
         final DN bindDN = resultEntry.getName();
+
         if (bindDN == null)
         {
-          return authenticationFailure(clientConnection);
+          return authenticationFailure(clientContext);
         }
 
         final BindRequest bindRequest =
             Requests.newSimpleBindRequest(bindDN.toString(), password.getBytes(Charset.forName("UTF-8")));
         return connection.bindAsync(bindRequest)
-                         .thenAsync(doChain(context, request, next, userName, clientConnection, connection),
-                                    returnErrorAfterFailedBind(clientConnection));
+                         .thenAsync(doChain(context, request, next, userName, connection),
+                                    returnErrorAfterFailedBind(clientContext));
       }
     };
   }
 
-  private AsyncFunction<BindResult, Response, NeverThrowsException> doChain(
-      final Context context, final Request request, final Handler next, final String userName,
-      final HTTPClientConnection clientConnection, final Connection connection)
+  private AsyncFunction<BindResult, Response, NeverThrowsException> doChain(final Context context,
+      final Request request, final Handler next, final String userName, final Connection connection)
   {
     return new AsyncFunction<BindResult, Response, NeverThrowsException>()
     {
       @Override
       public Promise<Response, NeverThrowsException> apply(BindResult value) throws NeverThrowsException
       {
-        clientConnection.setAuthUser(userName);
         try
         {
           SecurityContext securityContext = new SecurityContext(context, userName, null);
@@ -232,7 +167,7 @@
         }
         catch (Exception e)
         {
-          return asErrorResponse(e, clientConnection);
+          return asErrorResponse(e, context.asContext(ClientContext.class));
         }
       }
     };
@@ -247,7 +182,7 @@
   }
 
   private AsyncFunction<? super LdapException, Response, NeverThrowsException> returnErrorAfterFailedSearch(
-      final HTTPClientConnection clientConnection)
+      final ClientContext clientContext)
   {
     return new AsyncFunction<LdapException, Response, NeverThrowsException>()
     {
@@ -260,60 +195,51 @@
         {
           // Avoid information leak:
           // do not hint to the user that it is the username that is invalid
-          return authenticationFailure(clientConnection);
+          return authenticationFailure(clientContext);
         }
         else
         {
-          return asErrorResponse(exception, clientConnection);
+          return asErrorResponse(exception, clientContext);
         }
       }
     };
   }
 
   private AsyncFunction<LdapException, Response, NeverThrowsException> returnErrorAfterFailedBind(
-      final HTTPClientConnection clientConnection)
+      final ClientContext clientContext)
   {
     return new AsyncFunction<LdapException, Response, NeverThrowsException>()
     {
       @Override
       public Promise<Response, NeverThrowsException> apply(final LdapException e)
       {
-        return asErrorResponse(e, clientConnection);
+        return asErrorResponse(e, clientContext);
       }
     };
   }
 
-  private Promise<Response, NeverThrowsException> authenticationFailure(final HTTPClientConnection clientConnection)
+  private Promise<Response, NeverThrowsException> authenticationFailure(final ClientContext clientContext)
   {
-    return asErrorResponse(ResourceException.getException(401, "Invalid Credentials"), clientConnection,
-        DisconnectReason.INVALID_CREDENTIALS, false);
+    return asErrorResponse(ResourceException.getException(401, "Invalid Credentials"), clientContext, false);
   }
 
-  private Promise<Response, NeverThrowsException> asErrorResponse(
-      final Throwable t, final HTTPClientConnection clientConnection)
+  private Promise<Response, NeverThrowsException> asErrorResponse(final Throwable t, final ClientContext clientContext)
   {
-    return asErrorResponse(t, clientConnection, DisconnectReason.SERVER_ERROR, true);
+    return asErrorResponse(t, clientContext, true);
   }
 
-  private Promise<Response, NeverThrowsException> asErrorResponse(final Throwable t,
-      final HTTPClientConnection clientConnection, final DisconnectReason reason, final boolean logError)
+  private Promise<Response, NeverThrowsException> asErrorResponse(final Throwable t, final ClientContext clientContext,
+      final boolean logError)
   {
     final ResourceException ex = Rest2LDAP.asResourceException(t);
-    try
+    LocalizableMessage message = null;
+    if (logError)
     {
-      LocalizableMessage message = null;
-      if (logError)
-      {
-        logger.traceException(ex);
-        message = INFO_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT.get(
-            clientConnection.getClientHostPort(), clientConnection.getServerHostPort(), getExceptionMessage(ex));
-        logger.debug(message);
-      }
-      clientConnection.disconnect(reason, false, message);
-    }
-    finally
-    {
-      clientConnection.log(ex.getCode());
+      logger.traceException(ex);
+      message =
+          INFO_CONNHANDLER_UNABLE_TO_REGISTER_CLIENT.get(clientContext.getRemotePort(), clientContext.getLocalPort(),
+              getExceptionMessage(ex));
+      logger.debug(message);
     }
 
     return resourceExceptionToPromise(ex);
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPAuthenticationConfig.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPAuthenticationConfig.java
index 0750452..3a83dcd 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPAuthenticationConfig.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPAuthenticationConfig.java
@@ -11,7 +11,7 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
- * Copyright 2013 ForgeRock AS.
+ * Copyright 2013-2016 ForgeRock AS.
  */
 package org.opends.server.protocols.http;
 
@@ -22,7 +22,7 @@
  * Class holding the configuration for HTTP authentication. This is extracted
  * from the JSON config file or the config held in LDAP.
  */
-class HTTPAuthenticationConfig
+public final class HTTPAuthenticationConfig
 {
 
   private boolean basicAuthenticationSupported;
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
index 72580b0..0b03a4a 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/HTTPConnectionHandler.java
@@ -15,17 +15,10 @@
  */
 package org.opends.server.protocols.http;
 
-import static org.opends.messages.ConfigMessages.WARN_CONFIG_LOGGER_NO_ACTIVE_HTTP_ACCESS_LOGGERS;
+import static org.opends.messages.ConfigMessages.*;
 import static org.opends.messages.ProtocolMessages.*;
-import static org.opends.server.util.ServerConstants.ALERT_DESCRIPTION_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES;
-import static org.opends.server.util.ServerConstants.ALERT_TYPE_HTTP_CONNECTION_HANDLER_CONSECUTIVE_FAILURES;
-import static org.opends.server.util.StaticUtils.getExceptionMessage;
-import static org.opends.server.util.StaticUtils.isAddressInUse;
-import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
-
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.*;
 
 import java.io.IOException;
 import java.net.InetAddress;
@@ -43,12 +36,26 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.forgerock.http.Handler;
+import org.forgerock.http.HttpApplication;
+import org.forgerock.http.HttpApplicationException;
+import org.forgerock.http.handler.Handlers;
+import org.forgerock.http.io.Buffer;
 import org.forgerock.http.servlet.HttpFrameworkServlet;
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.config.server.ConfigChangeResult;
 import org.forgerock.opendj.config.server.ConfigException;
+import org.forgerock.opendj.config.server.ConfigurationChangeListener;
+import org.forgerock.opendj.ldap.DN;
 import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.server.config.server.ConnectionHandlerCfg;
+import org.forgerock.opendj.server.config.server.HTTPConnectionHandlerCfg;
+import org.forgerock.util.time.TimeService;
 import org.glassfish.grizzly.http.HttpProbe;
 import org.glassfish.grizzly.http.server.HttpServer;
 import org.glassfish.grizzly.http.server.NetworkListener;
@@ -59,9 +66,6 @@
 import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
 import org.glassfish.grizzly.strategies.SameThreadIOStrategy;
 import org.glassfish.grizzly.utils.Charsets;
-import org.forgerock.opendj.config.server.ConfigurationChangeListener;
-import org.forgerock.opendj.server.config.server.ConnectionHandlerCfg;
-import org.forgerock.opendj.server.config.server.HTTPConnectionHandlerCfg;
 import org.opends.server.api.AlertGenerator;
 import org.opends.server.api.ClientConnection;
 import org.opends.server.api.ConnectionHandler;
@@ -74,10 +78,10 @@
 import org.opends.server.extensions.NullTrustManagerProvider;
 import org.opends.server.loggers.HTTPAccessLogger;
 import org.opends.server.monitors.ClientConnectionMonitorProvider;
-import org.forgerock.opendj.ldap.DN;
 import org.opends.server.types.DirectoryException;
 import org.opends.server.types.HostPort;
 import org.opends.server.types.InitializationException;
+import org.opends.server.util.DynamicConstants;
 import org.opends.server.util.SelectableCertificateKeyManager;
 import org.opends.server.util.StaticUtils;
 
@@ -161,19 +165,6 @@
   }
 
   /**
-   * Returns whether unauthenticated HTTP requests are allowed. The server
-   * checks whether unauthenticated requests are allowed server-wide first then
-   * for the HTTP Connection Handler second.
-   *
-   * @return true if unauthenticated requests are allowed, false otherwise.
-   */
-  public boolean acceptUnauthenticatedRequests()
-  {
-    // The global setting overrides the more specific setting here.
-    return !DirectoryServer.rejectUnauthenticatedRequests() && !this.currentConfig.isAuthenticationRequired();
-  }
-
-  /**
    * Registers a client connection to track it.
    *
    * @param clientConnection
@@ -703,7 +694,7 @@
     this.httpServer = createHttpServer();
 
     // Register servlet as default servlet and also able to serve REST requests
-    createAndRegisterServlet("OpenDJ Rest2LDAP servlet", "", "/*");
+    createAndRegisterServlet("OpenDJ HTTP servlet", "", "/*");
 
     logger.trace("Starting HTTP server...");
     this.httpServer.start();
@@ -728,7 +719,7 @@
 
     // Configure the network listener
     final NetworkListener listener = new NetworkListener(
-        "Rest2LDAP", NetworkListener.DEFAULT_NETWORK_HOST, initConfig.getListenPort());
+        "OpenDJ-HTTP", NetworkListener.DEFAULT_NETWORK_HOST, initConfig.getListenPort());
     server.addListener(listener);
 
     // Configure the network transport
@@ -772,8 +763,7 @@
   {
     // Create and deploy the Web app context
     final WebappContext ctx = new WebappContext(servletName);
-    ctx.addServlet(servletName,
-        new HttpFrameworkServlet(new LdapHttpApplication(serverContext, this))).addMapping(urlPatterns);
+    ctx.addServlet(servletName, new HttpFrameworkServlet(new RootHttpApplication())).addMapping(urlPatterns);
     ctx.deploy(this.httpServer);
   }
 
@@ -917,4 +907,40 @@
     sslContext.init(keyManagers, trustManagerProvider.getTrustManagers(), null);
     return sslContext;
   }
+
+  /**
+   * This is the root {@link HttpApplication} handling all the requests from the
+   * {@link HTTPConnectionHandler}. If accepted, requests are audited and then
+   * forwarded to the global {@link ServerContext#getHTTPRouter()}.
+   */
+  private final class RootHttpApplication implements HttpApplication
+  {
+    @Override
+    public Handler start() throws HttpApplicationException
+    {
+      return Handlers.chainOf(
+          serverContext.getHTTPRouter(),
+          new AllowDenyFilter(currentConfig.getDeniedClient(), currentConfig.getAllowedClient()),
+          new CommonAuditTransactionIdFilter(serverContext),
+          new CommonAuditHttpAccessCheckEnabledFilter(serverContext,
+              new CommonAuditHttpAccessAuditFilter(
+                  DynamicConstants.PRODUCT_NAME,
+                  serverContext.getCommonAudit().getAuditServiceForHttpAccessLog(),
+                  TimeService.SYSTEM)),
+          new LDAPContextInjectionFilter(serverContext, HTTPConnectionHandler.this));
+    }
+
+    @Override
+    public void stop()
+    {
+      // Nothing to do
+    }
+
+    @Override
+    public org.forgerock.util.Factory<Buffer> getBufferFactory()
+    {
+      return null;
+    }
+  }
+
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContext.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContext.java
new file mode 100644
index 0000000..ffbec45
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContext.java
@@ -0,0 +1,42 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.protocols.http;
+
+import org.forgerock.opendj.ldap.ConnectionFactory;
+import org.forgerock.services.context.AbstractContext;
+import org.forgerock.services.context.Context;
+
+/** Context provided by this LDAP server to the embedded {@link HttpApplication}s. */
+public final class LDAPContext extends AbstractContext
+{
+  private final ConnectionFactory ldapConnectionFactory;
+
+  LDAPContext(final Context parent, ConnectionFactory ldapConnectionFactory)
+  {
+    super(parent, "LDAP context");
+    this.ldapConnectionFactory = ldapConnectionFactory;
+  }
+
+  /**
+   * Get the {@link LDAPConnectionFactory} attached to this context.
+   *
+   * @return The {@link LDAPConnectionFactory} attached to this context.
+   */
+  public ConnectionFactory getLdapConnectionFactory()
+  {
+    return ldapConnectionFactory;
+  }
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContextInjectionFilter.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContextInjectionFilter.java
new file mode 100644
index 0000000..30d4c99
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LDAPContextInjectionFilter.java
@@ -0,0 +1,85 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.protocols.http;
+
+import static org.opends.messages.ProtocolMessages.*;
+
+import org.forgerock.http.Filter;
+import org.forgerock.http.Handler;
+import org.forgerock.http.protocol.Request;
+import org.forgerock.http.protocol.Response;
+import org.forgerock.http.protocol.Status;
+import org.forgerock.opendj.ldap.Connection;
+import org.forgerock.opendj.ldap.ConnectionFactory;
+import org.forgerock.opendj.ldap.LdapException;
+import org.forgerock.services.context.Context;
+import org.forgerock.util.promise.NeverThrowsException;
+import org.forgerock.util.promise.Promise;
+import org.forgerock.util.promise.Promises;
+import org.opends.server.core.ServerContext;
+import org.opends.server.types.DisconnectReason;
+
+/**
+ * Filter injecting the {@link LDAPContext} giving access to
+ * {@link LDAPConnectionFactory} to the underlying {@link HttpApplication}.
+ */
+final class LDAPContextInjectionFilter implements Filter
+{
+  private final ServerContext serverContext;
+  private final HTTPConnectionHandler httpConnectionHandler;
+
+  LDAPContextInjectionFilter(ServerContext serverContext, HTTPConnectionHandler httpConnectionHandler) {
+    this.serverContext = serverContext;
+    this.httpConnectionHandler= httpConnectionHandler;
+  }
+
+  @Override
+  public Promise<Response, NeverThrowsException> filter(Context context, Request request, Handler next)
+  {
+    final HTTPClientConnection clientConnection =
+        new HTTPClientConnection(serverContext, httpConnectionHandler, context, request);
+    if (clientConnection.getConnectionID() < 0)
+    {
+      clientConnection.disconnect(DisconnectReason.ADMIN_LIMIT_EXCEEDED, true,
+          ERR_CONNHANDLER_REJECTED_BY_SERVER.get());
+      return Promises.newResultPromise(new Response(Status.SERVICE_UNAVAILABLE));
+    }
+
+    final LDAPContext djContext  = new LDAPContext(context, new ConnectionFactory()
+    {
+      private final Connection connection = new SdkConnectionAdapter(clientConnection);
+
+      @Override
+      public Promise<Connection, LdapException> getConnectionAsync()
+      {
+        return Promises.newResultPromise(connection);
+      }
+
+      @Override
+      public Connection getConnection() throws LdapException
+      {
+        return connection;
+      }
+
+      @Override
+      public void close()
+      {
+      }
+    });
+    return next.handle(djContext, request);
+  }
+
+}
\ No newline at end of file
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LdapHttpApplication.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEmbeddedHttpApplication.java
similarity index 61%
rename from opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LdapHttpApplication.java
rename to opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEmbeddedHttpApplication.java
index f618377..79100ae 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/LdapHttpApplication.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEmbeddedHttpApplication.java
@@ -13,13 +13,14 @@
  *
  * Copyright 2015-2016 ForgeRock AS.
  */
-package org.opends.server.protocols.http;
+package org.opends.server.protocols.http.rest2ldap;
 
-import static org.forgerock.util.Utils.closeSilently;
-import static org.opends.server.util.StaticUtils.getFileForPath;
+import static org.forgerock.http.util.Json.*;
+import static org.opends.messages.ProtocolMessages.*;
 
-import java.io.File;
-import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 
 import org.forgerock.http.Handler;
 import org.forgerock.http.HttpApplication;
@@ -28,9 +29,6 @@
 import org.forgerock.http.io.Buffer;
 import org.forgerock.http.protocol.Request;
 import org.forgerock.http.protocol.Response;
-import org.forgerock.http.util.Json;
-import org.forgerock.i18n.LocalizableMessage;
-import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.json.JsonValue;
 import org.forgerock.json.resource.CollectionResourceProvider;
 import org.forgerock.json.resource.ConnectionFactory;
@@ -48,18 +46,18 @@
 import org.forgerock.util.Factory;
 import org.forgerock.util.promise.NeverThrowsException;
 import org.forgerock.util.promise.Promise;
-import org.forgerock.util.time.TimeService;
-import org.opends.messages.ProtocolMessages;
-import org.opends.server.core.ServerContext;
-import org.opends.server.util.DynamicConstants;
+import org.opends.server.protocols.http.AuthenticationFilter;
+import org.opends.server.protocols.http.HTTPAuthenticationConfig;
 
-/** Main class of the HTTP Connection Handler web application */
-class LdapHttpApplication implements HttpApplication
+/** Entry point of the Rest2Ldap application when used in embedded mode. */
+final class Rest2LdapEmbeddedHttpApplication implements HttpApplication
 {
-  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
-
-  /** Http Handler which sets a connection to an OpenDJ server. */
-  private static class LdapHttpHandler implements Handler
+  /**
+   * Http Handler re-using the pre-established internal LDAP connection.
+   *
+   * @see AuthenticationFilter
+   */
+  private static final class Rest2LdapHandler implements Handler
   {
     private final Handler delegate;
 
@@ -70,9 +68,9 @@
      *            The configuration which will be used to set
      *            the connection and the mappings to the OpenDJ server.
      */
-    public LdapHttpHandler(final JsonValue configuration)
+    public Rest2LdapHandler(final JsonValue configuration)
     {
-      ConnectionFactory connectionFactory = Resources.newInternalConnectionFactory(createRouter(configuration));
+      final ConnectionFactory connectionFactory = Resources.newInternalConnectionFactory(createRouter(configuration));
       delegate = CrestHttp.newHttpHandler(connectionFactory, new HttpContextFactory()
       {
         @Override
@@ -106,15 +104,13 @@
     }
   }
 
-  private HTTPConnectionHandler connectionHandler;
-  private LdapHttpHandler handler;
-  private CollectClientConnectionsFilter filter;
-  private final ServerContext serverContext;
+  private final URL configFileUrl;
+  private final boolean authenticationRequired;
 
-  LdapHttpApplication(ServerContext serverContext, HTTPConnectionHandler connectionHandler)
+  Rest2LdapEmbeddedHttpApplication(URL configFileUrl, boolean authenticationRequired)
   {
-    this.serverContext = serverContext;
-    this.connectionHandler = connectionHandler;
+    this.configFileUrl = configFileUrl;
+    this.authenticationRequired = authenticationRequired;
   }
 
   @Override
@@ -122,33 +118,30 @@
   {
     try
     {
-      final File configFile = getFileForPath(connectionHandler.getCurrentConfig().getConfigFile());
-      final Object jsonElems = Json.readJsonLenient(new FileReader(configFile));
+      final Object jsonElems = readJson(configFileUrl);
       final JsonValue configuration = new JsonValue(jsonElems).recordKeyAccesses();
-      handler = new LdapHttpHandler(configuration);
-      filter =
-          new CollectClientConnectionsFilter(serverContext, connectionHandler, getAuthenticationConfig(configuration));
+      final Handler handler = Handlers.chainOf(
+          new Rest2LdapHandler(configuration),
+          new AuthenticationFilter(getAuthenticationConfig(configuration), authenticationRequired));
       configuration.verifyAllKeysAccessed();
-
-      RequestHandler requestHandler = serverContext.getCommonAudit().getAuditServiceForHttpAccessLog();
-      CommonAuditTransactionIdFilter transactionIdFilter = new CommonAuditTransactionIdFilter(serverContext);
-      CommonAuditHttpAccessAuditFilter httpAccessFilter =
-          new CommonAuditHttpAccessAuditFilter(DynamicConstants.PRODUCT_NAME, requestHandler, TimeService.SYSTEM);
-      CommonAuditHttpAccessCheckEnabledFilter checkFilter =
-          new CommonAuditHttpAccessCheckEnabledFilter(serverContext, httpAccessFilter);
-
-      return Handlers.chainOf(handler, transactionIdFilter, checkFilter, filter);
+      return handler;
     }
     catch (final Exception e)
     {
-      final LocalizableMessage errorMsg = ProtocolMessages.ERR_INITIALIZE_HTTP_CONNECTION_HANDLER.get();
-      logger.error(errorMsg, e);
       stop();
-      throw new HttpApplicationException(errorMsg.toString(), e);
+      throw new HttpApplicationException(ERR_INITIALIZE_HTTP_CONNECTION_HANDLER.get().toString(), e);
     }
   }
 
-  private HTTPAuthenticationConfig getAuthenticationConfig(final JsonValue configuration)
+  private static JsonValue readJson(final URL resource) throws IOException
+  {
+    try (final InputStream in = resource.openStream())
+    {
+      return new JsonValue(readJsonLenient(in));
+    }
+  }
+
+  private static HTTPAuthenticationConfig getAuthenticationConfig(final JsonValue configuration)
   {
     final HTTPAuthenticationConfig result = new HTTPAuthenticationConfig();
 
@@ -165,12 +158,12 @@
     return result;
   }
 
-  private String asString(JsonValue value, String key)
+  private static String asString(JsonValue value, String key)
   {
     return value.get(key).required().asString();
   }
 
-  private boolean asBool(JsonValue value, String key)
+  private static boolean asBool(JsonValue value, String key)
   {
     return value.get(key).defaultTo(false).asBoolean();
   }
@@ -184,8 +177,6 @@
   @Override
   public void stop()
   {
-    closeSilently(filter);
-    handler = null;
-    filter = null;
+    // Nothing to do
   }
 }
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEndpoint.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEndpoint.java
new file mode 100644
index 0000000..e4f4da4
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/Rest2LdapEndpoint.java
@@ -0,0 +1,65 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.opends.server.protocols.http.rest2ldap;
+
+import static org.opends.messages.ConfigMessages.*;
+import static org.opends.server.util.StaticUtils.*;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.forgerock.http.HttpApplication;
+import org.forgerock.opendj.server.config.server.Rest2ldapEndpointCfg;
+import org.opends.server.api.HttpEndpoint;
+import org.opends.server.types.InitializationException;
+
+/**
+ * Encapsulates configuration required to start a rest2ldap application in an
+ * OpenDJ context. Acts as a factory for {@link Rest2LdapEmbeddedHttpApplication}.
+ */
+public final class Rest2LdapEndpoint extends HttpEndpoint<Rest2ldapEndpointCfg>
+{
+  /**
+   * Create a new Rest2LdapEnpoint with the supplied configuration.
+   *
+   * @param configuration
+   *          Configuration to use for the {@link HttpApplication}
+   */
+  public Rest2LdapEndpoint(Rest2ldapEndpointCfg configuration)
+  {
+    super(configuration);
+  }
+
+  @Override
+  public HttpApplication newHttpApplication() throws InitializationException
+  {
+    try
+    {
+      final URI configURI = new URI(configuration.getConfigUrl());
+      final URL absoluteConfigUrl =
+          configURI.isAbsolute() ? configURI.toURL() : getFileForPath(configuration.getConfigUrl()).toURI().toURL();
+      return new Rest2LdapEmbeddedHttpApplication(absoluteConfigUrl, configuration.isAuthenticationRequired());
+    }
+    catch (MalformedURLException | URISyntaxException e)
+    {
+      throw new InitializationException(ERR_CONFIG_REST2LDAP_MALFORMED_URL
+          .get(configuration.dn(), stackTraceToSingleLineString(e)));
+    }
+  }
+
+}
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/package-info.java b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/package-info.java
new file mode 100644
index 0000000..656418f
--- /dev/null
+++ b/opendj-server-legacy/src/main/java/org/opends/server/protocols/http/rest2ldap/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+
+
+
+/**
+ * Contains the implementation for the HTTPEndpoint that is responsible for providing
+ * a REST interface to this LDAP server.
+ */
+package org.opends.server.protocols.http.rest2ldap;
diff --git a/opendj-server-legacy/src/messages/org/opends/messages/config.properties b/opendj-server-legacy/src/messages/org/opends/messages/config.properties
index 2df734f..701ff69 100644
--- a/opendj-server-legacy/src/messages/org/opends/messages/config.properties
+++ b/opendj-server-legacy/src/messages/org/opends/messages/config.properties
@@ -799,7 +799,7 @@
 ERR_CONFIG_LOGGING_EMPTY_LOG_FORMAT_733=The log format for configuration \
  entry %s is empty. No information will be logged even if logging is activated
 WARN_CONFIG_LOGGING_UNSUPPORTED_FIELDS_IN_LOG_FORMAT_734=The log format \
- for %s contains the folowing unsupported fields: %s. Their output will be \
+ for %s contains the following unsupported fields: %s. Their output will be \
  replaced with a dash ("-") character
 ERR_CONFIG_LOGGER_CANNOT_UPDATE_LOGGER_735=An error occurred while \
  attempting to update a Directory Server logger from the information in \
@@ -809,3 +809,12 @@
  configuration entry %s: %s
 ERR_CONFIG_CANNOT_CONFIGURE_JUL_LOGGER_737=Cannot configure \
  java.util.logging root logger level: %s. java.util.logging support is now disabled.
+ERR_CONFIG_HTTPENDPOINT_INITIALIZATION_FAILED_738=An error occurred while \
+ trying to initialize an instance of class %s as an HTTP endpoint as defined \
+ in configuration entry %s: %s
+ERR_CONFIG_HTTPENDPOINT_UNABLE_TO_START_739=An error occurred while \
+ starting the HTTP endpoint as defined in configuration entry %s: %s
+ERR_CONFIG_HTTPENDPOINT_INVALID_CONFIGURATION_741=The HTTP endpoint configuration \
+ defined in %s is invalid: %s
+ERR_CONFIG_REST2LDAP_MALFORMED_URL_742=Invalid configuration URL in the REST2LDAP endpoint \
+ configuration entry %s: %s
\ No newline at end of file
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/CollectClientConnectionsFilterTest.java b/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/AuthenticationFilterTest.java
similarity index 93%
rename from opendj-server-legacy/src/test/java/org/opends/server/protocols/http/CollectClientConnectionsFilterTest.java
rename to opendj-server-legacy/src/test/java/org/opends/server/protocols/http/AuthenticationFilterTest.java
index a1a05e4..4bd6a97 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/CollectClientConnectionsFilterTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/protocols/http/AuthenticationFilterTest.java
@@ -11,13 +11,13 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
- * Copyright 2013-2015 ForgeRock AS.
+ * Copyright 2013-2016 ForgeRock AS.
  */
 package org.opends.server.protocols.http;
 
 import static org.mockito.Mockito.mock;
+import static org.opends.server.protocols.http.AuthenticationFilter.*;
 import static org.assertj.core.api.Assertions.*;
-import static org.opends.server.protocols.http.CollectClientConnectionsFilter.*;
 
 import java.io.IOException;
 
@@ -33,20 +33,20 @@
 import org.testng.annotations.Test;
 
 @SuppressWarnings("javadoc")
-public class CollectClientConnectionsFilterTest extends DirectoryServerTestCase
+public class AuthenticationFilterTest extends DirectoryServerTestCase
 {
   private static final String USERNAME = "Aladdin";
   private static final String PASSWORD = "open sesame";
   private static final String BASE64_USERPASS = Base64.encode((USERNAME + ":" + PASSWORD).getBytes());
 
   private HTTPAuthenticationConfig authConfig;
-  private CollectClientConnectionsFilter filter;
+  private AuthenticationFilter filter;
 
   @BeforeMethod
   private void createConfigAndFilter()
   {
     authConfig = new HTTPAuthenticationConfig();
-    filter = new CollectClientConnectionsFilter(mock(ServerContext.class), null, authConfig);
+    filter = new AuthenticationFilter(authConfig, false);
   }
 
   @DataProvider(name = "Invalid HTTP basic auth strings")
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/tools/dsconfig/DsconfigOptionsTestCase.java b/opendj-server-legacy/src/test/java/org/opends/server/tools/dsconfig/DsconfigOptionsTestCase.java
index d8a5660..dfa9f05 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/tools/dsconfig/DsconfigOptionsTestCase.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/tools/dsconfig/DsconfigOptionsTestCase.java
@@ -11,7 +11,7 @@
  * Header, with the fields enclosed by brackets [] replaced by your own identifying
  * information: "Portions Copyright [year] [name of copyright owner]".
  *
- * Portions Copyright 2011-2015 ForgeRock AS.
+ * Copyright 2011-2016 ForgeRock AS.
  */
 package org.opends.server.tools.dsconfig;
 
@@ -62,7 +62,6 @@
       "--bindPassword" , "password",
       "--no-prompt",
       "--handler-name", "HTTP Connection Handler",
-      "--set", "authentication-required:false"
     };
     assertEquals(dsconfigMain(args), SUCCESS.get());
   }

--
Gitblit v1.10.0