Bug 543859 - [http whiteboard] Pass the OSGi HTTP Whiteboard R6 CT

Signed-off-by: Raymond Auge <raymond.auge@liferay.com>
diff --git a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java
index f00174c..4e08e8a 100644
--- a/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java
+++ b/bundles/org.eclipse.equinox.http.jetty/src/org/eclipse/equinox/http/jetty/internal/Activator.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2017 Cognos Incorporated, IBM Corporation and others.
+ * Copyright (c) 2005, 2019 Cognos Incorporated, IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -35,7 +35,7 @@
 	private static final String ORG_OSGI_SERVICE_HTTP_PORT = "org.osgi.service.http.port"; //$NON-NLS-1$
 	private static final String ORG_OSGI_SERVICE_HTTP_PORT_SECURE = "org.osgi.service.http.port.secure"; //$NON-NLS-1$
 
-	// controls whether start() should automatically start an Http Service based on BundleContext properties (default is true)
+	// controls whether start() should automatically start an Http Service based on BundleContext properties (default is false)
 	// Note: only used if the bundle is explicitly started (e.g. not "lazy" activated)
 	private static final String AUTOSTART = "org.eclipse.equinox.http.jetty.autostart"; //$NON-NLS-1$
 
@@ -59,8 +59,8 @@
 		EquinoxStdErrLog.setThresholdLogger(context.getProperty(LOG_STDERR_THRESHOLD));
 		httpServerManager = new HttpServerManager(jettyWorkDir);
 
-		boolean autostart = Details.getBoolean(context, AUTOSTART, true);
-		if (autostart && !isBundleLazyActivationPolicyUsed(context)) {
+		boolean autostart = Details.getBoolean(context, AUTOSTART, false);
+		if (autostart || !isBundleLazyActivationPolicyUsed(context)) {
 			Dictionary<String, Object> defaultSettings = createDefaultSettings(context);
 			httpServerManager.updated(DEFAULT_PID, defaultSettings);
 		}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
index 6304f11..7dec26c 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/META-INF/MANIFEST.MF
@@ -5,7 +5,6 @@
 Bundle-Version: 1.5.300.qualifier
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Eclipse-BundleShape: dir
-Bundle-Activator: org.eclipse.equinox.http.servlet.tests.bundle.Activator
 Require-Bundle: org.junit;bundle-version="4.0"
 Import-Package: javax.servlet;version="2.6.0",
  javax.servlet.http;version="2.6.0",
@@ -19,6 +18,8 @@
  org.eclipse.osgi.service.urlconversion;version="1.0.0",
  org.osgi.framework;version="1.6.0",
  org.osgi.framework.hooks.service;version="1.1.0",
+ org.osgi.framework.wiring;version="1.2.0",
+ org.osgi.resource;version="1.0.0",
  org.osgi.service.component,
  org.osgi.service.http;version="1.2.1",
  org.osgi.service.http.context;version="1.0.0",
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/AllTests.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/AllTests.java
index 40ffdcf..d2f0dfe 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/AllTests.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/AllTests.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 IBM Corporation and others.
+ * Copyright (c) 2016, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -15,12 +15,71 @@
 
 import org.eclipse.equinox.http.servlet.tests.DispatchingTest;
 import org.eclipse.equinox.http.servlet.tests.ServletTest;
+import org.eclipse.equinox.http.servlet.tests.Test_140_11_3;
+import org.eclipse.equinox.http.servlet.tests.Test_140_2_17to22;
+import org.eclipse.equinox.http.servlet.tests.Test_140_2_26to27;
+import org.eclipse.equinox.http.servlet.tests.Test_140_2_39to41;
+import org.eclipse.equinox.http.servlet.tests.Test_140_2_6_getResourcePaths;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_11to13;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_14to15;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_16;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_17to22;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_1_22to23;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_26to31;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_42to44;
+import org.eclipse.equinox.http.servlet.tests.Test_140_4_9;
+import org.eclipse.equinox.http.servlet.tests.Test_140_6_1;
+import org.eclipse.equinox.http.servlet.tests.Test_140_6_20to21_commonProperties;
+import org.eclipse.equinox.http.servlet.tests.Test_140_9_ServletContextDTO_custom_listener;
+import org.eclipse.equinox.http.servlet.tests.Test_140_9_ServletContextDTO_default_listener;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_bindUsingContextSelect;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_tieGoesToOldest;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_PATH_type;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_4xx;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_exception;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_error;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_request;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_5_HTTP_WHITEBOARD_FILTER_PATTERN;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_5_HTTP_WHITEBOARD_FILTER_REGEX;
+import org.eclipse.equinox.http.servlet.tests.Test_table_140_6_HTTP_WHITEBOARD_RESOURCE_validation;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(Suite.class)
-@SuiteClasses({DispatchingTest.class,ServletTest.class})
+@SuiteClasses({
+	DispatchingTest.class,
+	ServletTest.class,
+	Test_140_11_3.class,
+	Test_140_2_17to22.class,
+	Test_140_2_26to27.class,
+	Test_140_2_39to41.class,
+	Test_140_2_6_getResourcePaths.class,
+	Test_140_4_11to13.class,
+	Test_140_4_14to15.class,
+	Test_140_4_16.class,
+	Test_140_4_17to22.class,
+	Test_140_4_1_22to23.class,
+	Test_140_4_26to31.class,
+	Test_140_4_42to44.class,
+	Test_140_4_9.class,
+	Test_140_6_1.class,
+	Test_140_6_20to21_commonProperties.class,
+	Test_140_9_ServletContextDTO_custom_listener.class,
+	Test_140_9_ServletContextDTO_default_listener.class,
+	Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_bindUsingContextSelect.class,
+	Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_tieGoesToOldest.class,
+	Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_PATH_type.class,
+	Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED.class,
+	Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_4xx.class,
+	Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_exception.class,
+	Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_error.class,
+	Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_request.class,
+	Test_table_140_5_HTTP_WHITEBOARD_FILTER_PATTERN.class,
+	Test_table_140_5_HTTP_WHITEBOARD_FILTER_REGEX.class,
+	Test_table_140_6_HTTP_WHITEBOARD_RESOURCE_validation.class
+})
 public class AllTests {
 	// see @SuiteClasses
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
index 59dee05..1a8022c 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/testbase/BaseTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2018 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -14,6 +14,8 @@
 
 package org.eclipse.equinox.http.servlet.testbase;
 
+import static org.junit.Assert.assertNotNull;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
@@ -41,7 +43,6 @@
 
 import org.eclipse.equinox.http.jetty.JettyConstants;
 import org.eclipse.equinox.http.servlet.context.ContextPathCustomizer;
-import org.eclipse.equinox.http.servlet.tests.bundle.Activator;
 import org.eclipse.equinox.http.servlet.tests.bundle.BundleAdvisor;
 import org.eclipse.equinox.http.servlet.tests.bundle.BundleInstaller;
 import org.eclipse.equinox.http.servlet.tests.util.ServletRequestAdvisor;
@@ -50,6 +51,8 @@
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
 import org.osgi.framework.ServiceFactory;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
@@ -57,25 +60,38 @@
 import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.service.http.runtime.HttpServiceRuntime;
 import org.osgi.service.http.runtime.HttpServiceRuntimeConstants;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FailedErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FailedFilterDTO;
+import org.osgi.service.http.runtime.dto.FailedResourceDTO;
+import org.osgi.service.http.runtime.dto.FailedServletContextDTO;
+import org.osgi.service.http.runtime.dto.FailedServletDTO;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 import org.osgi.util.tracker.ServiceTracker;
 
 public class BaseTest {
 
+	public static final String	DEFAULT	= HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME;
+
 	@Before
 	public void setUp() throws Exception {
 		// Quiet logging for tests
 		System.setProperty("/.LEVEL", "OFF");
 		System.setProperty("org.eclipse.jetty.server.LEVEL", "OFF");
 		System.setProperty("org.eclipse.jetty.servlet.LEVEL", "OFF");
-
 		System.setProperty("org.osgi.service.http.port", "0");
 		System.setProperty("org.eclipse.equinox.http.jetty.context.sessioninactiveinterval", "1");
 		System.setProperty("org.eclipse.equinox.http.jetty.housekeeper.interval", "10");
 		BundleContext bundleContext = getBundleContext();
 		installer = new BundleInstaller(TEST_BUNDLES_BINARY_DIRECTORY, bundleContext);
 		advisor = new BundleAdvisor(bundleContext);
-		startBundles();
 		stopJetty();
+		startBundles();
 		runtimeTracker = new ServiceTracker<>(bundleContext, HttpServiceRuntime.class, null);
 		runtimeTracker.open();
 		runtimeTracker.waitForService(100);
@@ -84,20 +100,25 @@
 
 	@After
 	public void tearDown() throws Exception {
-		for (ServiceRegistration<? extends Object> serviceRegistration : registrations) {
-			serviceRegistration.unregister();
-		}
 		runtimeTracker.close();
-		stopJetty();
 		stopBundles();
 		requestAdvisor = null;
 		advisor = null;
-		registrations.clear();
 		try {
 			installer.shutdown();
 		} finally {
 			installer = null;
 		}
+
+		for (ServiceRegistration<? extends Object> serviceRegistration : registrations) {
+			try {
+				serviceRegistration.unregister();
+			}
+			catch (Exception e) {
+				// ignore
+			}
+		}
+		registrations.clear();
 	}
 
 	protected String doRequest(String action, Map<String, String> params) throws IOException {
@@ -125,7 +146,7 @@
 	}
 
 	protected BundleContext getBundleContext() {
-		return Activator.getBundleContext();
+		return FrameworkUtil.getBundle(BaseTest.class).getBundleContext();
 	}
 
 	protected String getContextPath() {
@@ -145,7 +166,7 @@
 		}
 		return value;
 	}
-	
+
 	protected void setJettyProperty(String key, String value) {
 		String qualifiedKey = JETTY_PROPERTY_PREFIX + key;
 		System.setProperty(qualifiedKey, value);
@@ -176,7 +197,7 @@
 		else if (Collection.class.isInstance(property)) {
 			List<String> list = new ArrayList<String>();
 			for (@SuppressWarnings("rawtypes")
-				 Iterator i = ((Collection)property).iterator(); i.hasNext();) {
+				Iterator i = ((Collection)property).iterator(); i.hasNext();) {
 
 				Object o = i.next();
 				if (String.class.isInstance(o)) {
@@ -193,9 +214,6 @@
 	}
 
 	protected void startBundles() throws BundleException {
-		for (String bundle : BUNDLES) {
-			advisor.startBundle(bundle);
-		}
 	}
 
 	protected void startJetty() throws Exception {
@@ -217,49 +235,44 @@
 		String contextPath = getContextPath();
 		requestAdvisor = new ServletRequestAdvisor(port, contextPath);
 	}
-	
+
 	protected void startJettyWithSSL(String port, String ksPath, String ksPassword, String keyPassword) throws Exception {
 		if(port == null) {
-			throw new IllegalArgumentException("Port cannot be null");		
+			throw new IllegalArgumentException("Port cannot be null");
 		}
 		if (ksPath == null) {
-			throw new IllegalArgumentException("Keystore path  cannot be null");			
+			throw new IllegalArgumentException("Keystore path  cannot be null");
 		}
 		setJettyProperty(JettyConstants.HTTP_ENABLED, "false");
 		setJettyProperty(JettyConstants.HTTPS_ENABLED, "true");
-		
+
 		setJettyProperty(JettyConstants.HTTPS_PORT, port);
-		
+
 		setJettyProperty(JettyConstants.SSL_KEYSTORE, ksPath);
-		
+
 		if(ksPassword != null) {
 			setJettyProperty(JettyConstants.SSL_PASSWORD, ksPassword);
 		}
 		if(keyPassword != null) {
 			setJettyProperty(JettyConstants.SSL_KEYPASSWORD, keyPassword);
 		}
-		
+
 		advisor.startBundle(EQUINOX_JETTY_BUNDLE);
 		String contextPath = getContextPath();
-		requestAdvisor = new ServletRequestAdvisor(port, contextPath, ksPath, ksPassword);	
+		requestAdvisor = new ServletRequestAdvisor(port, contextPath, ksPath, ksPassword);
 	}
 
-
 	protected void stopBundles() throws BundleException {
-		for (int i = BUNDLES.length - 1; i >= 0; i--) {
-			String bundle = BUNDLES[i];
-			advisor.stopBundle(bundle);
-		}
 	}
 
 	protected void stopJetty() throws BundleException {
 		advisor.stopBundle(EQUINOX_JETTY_BUNDLE);
 	}
-	
+
 	protected void stopJettyWithSSL() throws BundleException {
 		advisor.stopBundle(EQUINOX_JETTY_BUNDLE);
 		setJettyProperty(JettyConstants.HTTP_ENABLED, "true");
-		setJettyProperty(JettyConstants.HTTPS_ENABLED, "false");			
+		setJettyProperty(JettyConstants.HTTPS_ENABLED, "false");
 	}
 
 	protected void uninstallBundle(Bundle bundle) throws BundleException {
@@ -270,6 +283,177 @@
 		outputStream.write(string.getBytes(StandardCharsets.UTF_8));
 	}
 
+	protected FailedServletDTO getFailedServletDTOByName(String name) {
+		for (FailedServletDTO failedServletDTO : getFailedServletDTOs()) {
+			if (name.equals(failedServletDTO.name)) {
+				return failedServletDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedServletDTO[] getFailedServletDTOs() {
+		HttpServiceRuntime httpServiceRuntime = getHttpServiceRuntime();
+
+		return httpServiceRuntime.getRuntimeDTO().failedServletDTOs;
+	}
+
+	protected HttpServiceRuntime getHttpServiceRuntime() {
+		ServiceReference<HttpServiceRuntime> serviceReference =
+				getBundleContext().getServiceReference(HttpServiceRuntime.class);
+
+		assertNotNull(serviceReference);
+
+		return getBundleContext().getService(serviceReference);
+	}
+
+	protected long getServiceId(ServiceRegistration<?> sr) {
+		return (Long) sr.getReference().getProperty(Constants.SERVICE_ID);
+	}
+
+	protected RequestInfoDTO calculateRequestInfoDTO(String string) {
+		HttpServiceRuntime httpServiceRuntime = getHttpServiceRuntime();
+
+		return httpServiceRuntime.calculateRequestInfoDTO(string);
+	}
+
+	protected ServletDTO getServletDTOByName(String context, String name) {
+		ServletContextDTO servletContextDTO = getServletContextDTOByName(context);
+
+		if (servletContextDTO == null) {
+			return null;
+		}
+
+		for (ServletDTO servletDTO : servletContextDTO.servletDTOs) {
+			if (name.equals(servletDTO.name)) {
+				return servletDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected ServletContextDTO getServletContextDTOByName(String name) {
+		for (ServletContextDTO servletContextDTO : getServletContextDTOs()) {
+			if (name.equals(servletContextDTO.name)) {
+				return servletContextDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected ServletContextDTO[] getServletContextDTOs() {
+		return getHttpServiceRuntime().getRuntimeDTO().servletContextDTOs;
+	}
+
+	protected ErrorPageDTO getErrorPageDTOByName(String context, String name) {
+		ServletContextDTO servletContextDTO = getServletContextDTOByName(context);
+
+		if (servletContextDTO == null) {
+			return null;
+		}
+
+		for (ErrorPageDTO errorPageDTO : servletContextDTO.errorPageDTOs) {
+			if (name.equals(errorPageDTO.name)) {
+				return errorPageDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedErrorPageDTO getFailedErrorPageDTOByName(String name) {
+		for (FailedErrorPageDTO failedErrorPageDTO : getFailedErrorPageDTOs()) {
+			if (name.equals(failedErrorPageDTO.name)) {
+				return failedErrorPageDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedErrorPageDTO[] getFailedErrorPageDTOs() {
+		HttpServiceRuntime httpServiceRuntime = getHttpServiceRuntime();
+
+		return httpServiceRuntime.getRuntimeDTO().failedErrorPageDTOs;
+	}
+
+	protected FilterDTO getFilterDTOByName(String contextName, String name) {
+		ServletContextDTO servletContextDTO = getServletContextDTOByName(contextName);
+
+		if (servletContextDTO == null) {
+			return null;
+		}
+
+		for (FilterDTO filterDTO : servletContextDTO.filterDTOs) {
+			if (name.equals(filterDTO.name)) {
+				return filterDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedFilterDTO getFailedFilterDTOByName(String name) {
+		for (FailedFilterDTO failedFilterDTO : getFailedFilterDTOs()) {
+			if (name.equals(failedFilterDTO.name)) {
+				return failedFilterDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedFilterDTO[] getFailedFilterDTOs() {
+		return getHttpServiceRuntime().getRuntimeDTO().failedFilterDTOs;
+	}
+
+	protected FailedServletContextDTO getFailedServletContextDTOByName(String name) {
+		for (FailedServletContextDTO failedServletContextDTO : getFailedServletContextDTOs()) {
+			if (name.equals(failedServletContextDTO.name)) {
+				return failedServletContextDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedServletContextDTO[] getFailedServletContextDTOs() {
+		return getHttpServiceRuntime().getRuntimeDTO().failedServletContextDTOs;
+	}
+
+	protected ResourceDTO getResourceDTOByServiceId(String contextName, long serviceId) {
+		ServletContextDTO servletContextDTO = getServletContextDTOByName(contextName);
+
+		if (servletContextDTO == null) {
+			return null;
+		}
+
+		for (ResourceDTO resourceDTO : servletContextDTO.resourceDTOs) {
+			if (serviceId == resourceDTO.serviceId) {
+				return resourceDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedResourceDTO getFailedResourceDTOByServiceId(long serviceId) {
+		for (FailedResourceDTO failedResourceDTO : getFailedResourceDTOs()) {
+			if (serviceId == failedResourceDTO.serviceId) {
+				return failedResourceDTO;
+			}
+		}
+
+		return null;
+	}
+
+	protected FailedResourceDTO[] getFailedResourceDTOs() {
+		return getHttpServiceRuntime().getRuntimeDTO().failedResourceDTOs;
+	}
+
 	protected static final String PROTOTYPE = "prototype/";
 	protected static final String CONFIGURE = "configure";
 	protected static final String UNREGISTER = "unregister";
@@ -418,4 +602,4 @@
 
 	};
 
-}
\ No newline at end of file
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
index f738af0..2a835c7 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/ServletTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2017 IBM Corporation and others.
+ * Copyright (c) 2011, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -100,12 +100,14 @@
 import org.eclipse.equinox.http.servlet.tests.util.BaseServletRequestAttributeListener;
 import org.eclipse.equinox.http.servlet.tests.util.BaseServletRequestListener;
 import org.eclipse.equinox.http.servlet.tests.util.BufferedServlet;
+import org.eclipse.equinox.http.servlet.tests.util.TestServletPrototype;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestName;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
@@ -126,6 +128,20 @@
 	@Rule
 	public TestName testName = new TestName();
 
+	@Override
+	protected void startBundles() throws BundleException {
+		for (String bundle : BUNDLES) {
+			advisor.startBundle(bundle);
+		}
+	}
+
+	@Override
+	protected void stopBundles() throws BundleException {
+		for (int i = BUNDLES.length - 1; i >= 0; i--) {
+			String bundle = BUNDLES[i];
+			advisor.stopBundle(bundle);
+		}
+	}
 
 	@Test
 	public void test_ErrorPage1() throws Exception {
@@ -215,6 +231,12 @@
 
 	@Test
 	public void test_ErrorPage5() throws Exception {
+		BundleContext bundleContext = getBundleContext();
+		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
+		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
+		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
+		registrations.add(bundleContext.registerService(Servlet.class, testDriver, serviceProps));
+
 		Dictionary<String, Object> errorProps = new Hashtable<String, Object>();
 		errorProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "E5.4xx");
 		errorProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, "4xx");
@@ -3082,7 +3104,7 @@
 
 	@Test
 	public void test_ServletContextHelper12() throws Exception {
-		String expected1 = "a,b,1";
+		String expected1 = "a,b,null";
 
 		BundleContext bundleContext = getBundleContext();
 		Bundle bundle = bundleContext.getBundle();
@@ -3420,7 +3442,7 @@
 
 			Dictionary<String, Object> props = new Hashtable<String, Object>();
 			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "SA");
-			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/*");
+			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, new String[] {"/*", "/"});
 			registrations.add(getBundleContext().registerService(Servlet.class, servletA, props));
 			props = new Hashtable<String, Object>();
 
@@ -3528,7 +3550,7 @@
 
 			Dictionary<String, Object> props = new Hashtable<String, Object>();
 			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "SA");
-			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/*");
+			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, new String[] {"/*", "/"});
 			registrations.add(getBundleContext().registerService(Servlet.class, servletA, props));
 			props = new Hashtable<String, Object>();
 
@@ -4154,6 +4176,12 @@
 
 	@Test
 	public void testWBServletChangeInitParams() throws Exception{
+		BundleContext bundleContext = getBundleContext();
+		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
+		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
+		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
+		registrations.add(bundleContext.registerService(Servlet.class, testDriver, serviceProps));
+
 			String actual;
 
 			Map<String, String> params = new HashMap<String, String>();
@@ -4174,6 +4202,12 @@
 
 	@Test
 	public void testWBServletChangePattern() throws Exception{
+		BundleContext bundleContext = getBundleContext();
+		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
+		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
+		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
+		registrations.add(bundleContext.registerService(Servlet.class, testDriver, serviceProps));
+
 		String actual;
 
 		Map<String, String> params = new HashMap<String, String>();
@@ -4194,6 +4228,12 @@
 
 	@Test
 	public void testWBServletChangeRanking() throws Exception{
+		BundleContext bundleContext = getBundleContext();
+		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
+		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
+		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
+		registrations.add(bundleContext.registerService(Servlet.class, testDriver, serviceProps));
+
 		String actual;
 
 		// Configure two servlets with the second one registered ranking higher
@@ -4234,6 +4274,12 @@
 
 	@Test
 	public void testWBServletDefaultContextAdaptor1() throws Exception{
+		BundleContext bundleContext = getBundleContext();
+		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
+		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
+		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
+		registrations.add(bundleContext.registerService(Servlet.class, testDriver, serviceProps));
+
 		Dictionary<String, String> helperProps = new Hashtable<String, String>();
 		helperProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "testContext" + testName.getMethodName());
 		helperProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/testContext");
@@ -4264,6 +4310,7 @@
 
 			actual = requestAdvisor.request(testName.getMethodName());
 			Assert.assertEquals(testName.getMethodName(), actual);
+			doRequest(UNREGISTER, params);
 		} finally {
 			helperReg.unregister();
 			if (pathAdaptorReg != null) {
@@ -4274,6 +4321,12 @@
 
 	@Test
 	public void testWBServletDefaultContextAdaptor2() throws Exception{
+		BundleContext bundleContext = getBundleContext();
+		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
+		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
+		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
+		registrations.add(bundleContext.registerService(Servlet.class, testDriver, serviceProps));
+
 		Dictionary<String, String> helperProps = new Hashtable<String, String>();
 		helperProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "testContext" + testName.getMethodName());
 		helperProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/testContext");
@@ -4305,6 +4358,7 @@
 
 			actual = requestAdvisor.request("testContext/" + testName.getMethodName());
 			Assert.assertEquals(testName.getMethodName(), actual);
+			doRequest(UNREGISTER, params);
 		} finally {
 			helperReg.unregister();
 			if (pathAdaptorReg != null) {
@@ -4315,6 +4369,12 @@
 
 	@Test
 	public void testWBServletDefaultContextAdaptor3() throws Exception{
+		BundleContext bundleContext = getBundleContext();
+		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
+		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
+		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
+		registrations.add(bundleContext.registerService(Servlet.class, testDriver, serviceProps));
+
 		// test the ContextPathCustomizer with a ServletContextHelper that has a '/' context path
 		Dictionary<String, String> helperProps = new Hashtable<String, String>();
 		helperProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "testContext" + testName.getMethodName());
@@ -4347,6 +4407,7 @@
 
 			actual = requestAdvisor.request(testName.getMethodName());
 			Assert.assertEquals(testName.getMethodName(), actual);
+			doRequest(UNREGISTER, params);
 		} finally {
 			helperReg.unregister();
 			if (pathAdaptorReg != null) {
@@ -4396,7 +4457,7 @@
 		String actual = requestAdvisor.request(testName.getMethodName());
 		Assert.assertEquals(expected, actual);
 	}
-	
+
 	@Test
 	public void testHTTPSEndpoint() throws Exception {
 		stopJetty();
@@ -4405,13 +4466,13 @@
 		if (!keyStoreFile.exists()) {
 			Files.copy(keyStoreURL.openStream(), keyStoreFile.toPath());
 		}
-		
+
 		startJettyWithSSL("8443", keyStoreFile.getAbsolutePath(), "secret", "secret");
-		
+
 		Bundle bundle = installBundle(TEST_BUNDLE_1);
 		try {
 			bundle.start();
-			
+
 			String actual = requestAdvisor.requestHttps("TestServlet10");
 			assertEquals("Expected output not found", "a", actual);
 		} finally {
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_11_3.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_11_3.java
new file mode 100644
index 0000000..1322555
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_11_3.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+
+public class Test_140_11_3 extends BaseTest {
+
+	public static final String	SERVICE_NAMESPACE					= "osgi.service";
+	public static final String	CAPABILITY_OBJECTCLASS_ATTRIBUTE	= "objectClass";
+
+	@Test
+	public void test_140_11_3() throws Exception {
+		BundleContext context = getBundleContext();
+
+		ServiceReference<HttpServiceRuntime> srA = context.getServiceReference(HttpServiceRuntime.class);
+
+		BundleWiring bundleWiring = srA.getBundle().adapt(BundleWiring.class);
+
+		List<BundleCapability> capabilities = bundleWiring.getCapabilities(SERVICE_NAMESPACE);
+
+		boolean found = false;
+
+		for (Capability capability : capabilities) {
+			Map<String, Object> attributes = capability.getAttributes();
+			@SuppressWarnings("unchecked")
+			List<String> objectClasses = (List<String>) attributes.get(CAPABILITY_OBJECTCLASS_ATTRIBUTE);
+
+			if ((objectClasses != null) && objectClasses.contains(HttpServiceRuntime.class.getName())) {
+				Map<String, String> directives = capability.getDirectives();
+
+				String uses = directives.get("uses");
+
+				List<String> packages = Arrays.asList(uses.split(","));
+
+				assertTrue(packages.contains("org.osgi.service.http.runtime"));
+				assertTrue(packages.contains("org.osgi.service.http.runtime.dto"));
+
+				found = true;
+			}
+		}
+
+		assertTrue(found);
+	}
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_17to22.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_17to22.java
new file mode 100644
index 0000000..ab18025
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_17to22.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertNull;
+
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockSCL;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.service.FindHook;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_2_17to22 extends BaseTest {
+
+	@Test
+	public void test_140_2_17to22() throws Exception {
+		final BundleContext context = getBundleContext();
+
+		FindHook findHook = new FindHook() {
+
+			@Override
+			public void find(
+					BundleContext bundleContext, String name, String filter,
+					boolean allServices, Collection<ServiceReference<?>> references) {
+
+				if (bundleContext != context) {
+					return;
+				}
+
+				// don't show default ServletContextHelper
+				for (Iterator<ServiceReference<?>> iterator = references.iterator(); iterator.hasNext();) {
+					ServiceReference<?> sr = iterator.next();
+
+					if (DEFAULT.equals(sr.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME))) {
+						iterator.remove();
+					}
+				}
+			}
+
+		};
+
+		registrations.add(context.registerService(FindHook.class, findHook, null));
+
+		AtomicReference<ServletContext> sc1 = new AtomicReference<ServletContext>();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		ServiceRegistration<ServletContextListener> serviceRegistration = context.registerService(ServletContextListener.class, new MockSCL(sc1), properties);
+		registrations.add(serviceRegistration);
+
+		assertNull(sc1.get());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_26to27.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_26to27.java
new file mode 100644
index 0000000..6acfd87
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_26to27.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServlet;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_2_26to27 extends BaseTest {
+
+	@SuppressWarnings("serial")
+	@Test
+	public void test_140_2_26to27() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "foo");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/foo");
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "foobar");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/foo");
+		properties.put(Constants.SERVICE_RANKING, new Integer(Integer.MAX_VALUE));
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(osgi.http.whiteboard.context.name=foo)");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "first");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/bar/someServlet");
+		registrations.add(context.registerService(Servlet.class, new HttpServlet() {}, properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(osgi.http.whiteboard.context.name=foobar)");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "second");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/bar/someServlet");
+		registrations.add(context.registerService(Servlet.class, new HttpServlet() {}, properties));
+
+		ServletContextDTO servletContextDTO = getServletContextDTOByName("foobar");
+
+		assertNotNull(servletContextDTO);
+
+		HttpServiceRuntime httpServiceRuntime = getHttpServiceRuntime();
+
+		RequestInfoDTO requestInfoDTO = httpServiceRuntime.calculateRequestInfoDTO("/foo/bar/someServlet");
+
+		assertNotNull(requestInfoDTO);
+		assertNotNull(requestInfoDTO.servletDTO);
+		assertEquals(servletContextDTO.serviceId, requestInfoDTO.servletDTO.servletContextId);
+		assertEquals("second", requestInfoDTO.servletDTO.name);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_39to41.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_39to41.java
new file mode 100644
index 0000000..fd33c46
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_39to41.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedServletContextDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_2_39to41 extends BaseTest {
+
+	@Test
+	public void test_140_2_39to41() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "foo");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/foo");
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "foo");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/foo/bar");
+		properties.put(Constants.SERVICE_RANKING, new Integer(1000));
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		FailedServletContextDTO failedServletContextDTO = getFailedServletContextDTOByName("foo");
+
+		assertNotNull(failedServletContextDTO);
+		assertEquals(
+				DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE,
+				failedServletContextDTO.failureReason);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_6_getResourcePaths.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_6_getResourcePaths.java
new file mode 100644
index 0000000..b089403
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_2_6_getResourcePaths.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockSCL;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_2_6_getResourcePaths extends BaseTest {
+
+	@Test
+	public void test_140_2_6_getResourcePaths() throws Exception {
+		BundleContext context = getBundleContext();
+
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		ServletContextHelper servletContextHelper = new ServletContextHelper() {
+
+			@Override
+			public Set<String> getResourcePaths(String path) {
+				invoked.set(true);
+
+				return null;
+			}
+
+		};
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "context1");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/context1");
+		registrations.add(context.registerService(ServletContextHelper.class, servletContextHelper, properties));
+
+		AtomicReference<ServletContext> sc1 = new AtomicReference<ServletContext>();
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(osgi.http.whiteboard.context.name=context1)");
+		registrations.add(context.registerService(ServletContextListener.class, new MockSCL(sc1), properties));
+
+		ServletContext servletContext = sc1.get();
+
+		assertNotNull(servletContext);
+
+		servletContext.getResourcePaths("/META-INF/");
+
+		assertTrue(invoked.get());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_11to13.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_11to13.java
new file mode 100644
index 0000000..d396846
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_11to13.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.runtime.dto.FailedServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_11to13 extends BaseTest {
+
+	@Test
+	public void test_140_4_11to13() throws Exception {
+		BundleContext context = getBundleContext();
+
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		Servlet servlet = new HttpServlet() {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response)
+					throws ServletException, IOException {
+
+				invoked.set(true);
+
+				PrintWriter writer = response.getWriter();
+				writer.write((request.getContextPath() == null) ? "" : request.getContextPath());
+				writer.write(":");
+				writer.write((request.getServletPath() == null) ? "" : request.getServletPath());
+				writer.write(":");
+				writer.write((request.getPathInfo() == null) ? "" : request.getPathInfo());
+			}
+
+		};
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "");
+		registrations.add(context.registerService(Servlet.class, servlet, properties));
+
+		FailedServletDTO[] failedServletDTOs = getFailedServletDTOs();
+
+		assertEquals(0, failedServletDTOs.length);
+
+		assertEquals("::/", requestAdvisor.request(""));
+		assertTrue(invoked.get());
+		assertEquals("404", requestAdvisor.request("a.xhtml", null).get("responseCode").get(0));
+		assertEquals("404", requestAdvisor.request("some/path/a.xhtml", null).get("responseCode").get(0));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_14to15.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_14to15.java
new file mode 100644
index 0000000..1316e18
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_14to15.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.runtime.dto.FailedServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_14to15 extends BaseTest {
+
+	@Test
+	public void test_140_4_14to15() throws Exception {
+		BundleContext context = getBundleContext();
+
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		Servlet servlet = new HttpServlet() {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response)
+					throws ServletException, IOException {
+
+				invoked.set(true);
+
+				PrintWriter writer = response.getWriter();
+				writer.write((request.getContextPath() == null) ? "" : request.getContextPath());
+				writer.write(":");
+				writer.write((request.getServletPath() == null) ? "" : request.getServletPath());
+				writer.write(":");
+				writer.write((request.getPathInfo() == null) ? "" : request.getPathInfo());
+			}
+
+		};
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/");
+		registrations.add(context.registerService(Servlet.class, servlet, properties));
+
+		FailedServletDTO[] failedServletDTOs = getFailedServletDTOs();
+
+		assertEquals(0, failedServletDTOs.length);
+
+		assertEquals(":/a.html:", requestAdvisor.request("a.html"));
+		assertTrue(invoked.get());
+		invoked.set(false);
+		assertEquals(":/a.xhtml:", requestAdvisor.request("a.xhtml"));
+		assertTrue(invoked.get());
+		invoked.set(false);
+		assertEquals(":/some/path/a.xhtml:", requestAdvisor.request("some/path/a.xhtml"));
+		assertTrue(invoked.get());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_16.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_16.java
new file mode 100644
index 0000000..a9ac513
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_16.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_16 extends BaseTest {
+
+	@Test
+	public void test_140_4_16() throws Exception {
+		BundleContext context = getBundleContext();
+
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		Servlet servlet = new HttpServlet() {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response)
+					throws ServletException, IOException {
+
+				invoked.set(true);
+
+				PrintWriter writer = response.getWriter();
+				writer.write((request.getContextPath() == null) ? "" : request.getContextPath());
+				writer.write(":");
+				writer.write((request.getServletPath() == null) ? "" : request.getServletPath());
+				writer.write(":");
+				writer.write((request.getPathInfo() == null) ? "" : request.getPathInfo());
+			}
+
+		};
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, new String[] {"/a", "/fee/fi/foo/fum"});
+		registrations.add(context.registerService(Servlet.class, servlet, properties));
+
+		assertEquals(":/a:", requestAdvisor.request("a"));
+		assertTrue(invoked.get());
+		invoked.set(false);
+		assertEquals(":/fee/fi/foo/fum:", requestAdvisor.request("fee/fi/foo/fum"));
+		assertTrue(invoked.get());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_17to22.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_17to22.java
new file mode 100644
index 0000000..bd5139c
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_17to22.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_17to22 extends BaseTest {
+
+	@Test
+	public void test_140_4_17to22() throws Exception {
+		@SuppressWarnings("serial")
+		class AServlet extends HttpServlet {
+
+			public AServlet(String content) {
+				this.content = content;
+			}
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response)
+					throws ServletException, IOException {
+
+				response.getWriter().write(content);
+			}
+
+			private final String	content;
+
+		}
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		ServiceRegistration<Servlet> srA = getBundleContext().registerService(Servlet.class, new AServlet("a"), properties);
+		registrations.add(srA);
+
+		assertEquals("a", requestAdvisor.request("a"));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "b");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		ServiceRegistration<Servlet> srB = getBundleContext().registerService(Servlet.class, new AServlet("b"), properties);
+		registrations.add(srB);
+
+		assertEquals("a", requestAdvisor.request("a"));
+
+		FailedServletDTO failedServletDTO = getFailedServletDTOByName("b");
+
+		assertNotNull(failedServletDTO);
+		assertEquals(
+				DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE,
+				failedServletDTO.failureReason);
+		assertEquals(
+				getServiceId(srB),
+				failedServletDTO.serviceId);
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "c");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		properties.put(Constants.SERVICE_RANKING, 1000);
+		registrations.add(getBundleContext().registerService(Servlet.class, new AServlet("c"), properties));
+
+		assertEquals("c", requestAdvisor.request("a"));
+
+		failedServletDTO = getFailedServletDTOByName("a");
+
+		assertNotNull(failedServletDTO);
+		assertEquals(
+				DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE,
+				failedServletDTO.failureReason);
+		assertEquals(
+				getServiceId(srA),
+				failedServletDTO.serviceId);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_1_22to23.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_1_22to23.java
new file mode 100644
index 0000000..9fcdaa9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_1_22to23.java
@@ -0,0 +1,81 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_1_22to23 extends BaseTest {
+
+	@Test
+	public void test_140_4_1_22to23() throws Exception {
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		class AServlet extends HttpServlet {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				response.sendError(HttpServletResponse.SC_BAD_GATEWAY, "a");
+			}
+
+		}
+
+		@SuppressWarnings("serial")
+		class BServlet extends HttpServlet {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				invoked.set(true);
+				throw new ServletException();
+			}
+
+		}
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		registrations.add(getBundleContext().registerService(Servlet.class, new AServlet(), properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "b");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, HttpServletResponse.SC_BAD_GATEWAY + "");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/b");
+		registrations.add(getBundleContext().registerService(Servlet.class, new BServlet(), properties));
+
+		Map<String, List<String>> response = requestAdvisor.request("a", null);
+		assertTrue(!"a".equals(response.get("responseBody").get(0)));
+		assertTrue(invoked.get());
+		assertEquals(HttpServletResponse.SC_BAD_GATEWAY + "", response.get("responseCode").get(0));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_26to31.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_26to31.java
new file mode 100644
index 0000000..2fe58e9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_26to31.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.HttpService;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_26to31 extends BaseTest {
+
+	@Test
+	public void test_140_4_26to31() throws Exception {
+		BundleContext context = getBundleContext();
+
+		@SuppressWarnings("serial")
+		class AServlet extends HttpServlet {
+
+			public AServlet(String content) {
+				this.content = content;
+			}
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response)
+					throws ServletException, IOException {
+
+				response.getWriter().write(content);
+			}
+
+			private final String	content;
+
+		}
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		//properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(osgi.http.whiteboard.context.name=org_eclipse_equinox_http_servlet_internal_HttpServiceImpl_DefaultHttpContext-0)");
+		properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
+		ServiceRegistration<Servlet> srA = context.registerService(Servlet.class, new AServlet("a"), properties);
+		registrations.add(srA);
+
+		RequestInfoDTO requestInfoDTO = calculateRequestInfoDTO("/a");
+
+		assertNotNull(requestInfoDTO);
+		assertNotNull(requestInfoDTO.servletDTO);
+		assertEquals("a", requestInfoDTO.servletDTO.name);
+		assertEquals(
+				getServiceId(srA),
+				requestInfoDTO.servletDTO.serviceId);
+		assertEquals("a", requestAdvisor.request("a"));
+
+		HttpService httpService = getHttpService();
+
+		if (httpService == null) {
+			return;
+		}
+
+		httpService.registerServlet("/a", new AServlet("b"), null, null);
+
+		try {
+			requestInfoDTO = calculateRequestInfoDTO("/a");
+
+			assertNotNull(requestInfoDTO);
+			assertNotNull(requestInfoDTO.servletDTO);
+			assertFalse(getServiceId(srA) == requestInfoDTO.servletDTO.serviceId);
+			assertEquals("b", requestAdvisor.request("a"));
+		} finally {
+			httpService.unregister("/a");
+		}
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_42to44.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_42to44.java
new file mode 100644
index 0000000..d56aa73
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_42to44.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_42to44 extends BaseTest {
+
+	@Test
+	public void test_140_4_42to44() throws Exception {
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		class AServlet extends HttpServlet {
+
+			@Override
+			public void init(ServletConfig config) throws ServletException {
+				invoked.set(true);
+
+				throw new ServletException();
+			}
+
+		}
+
+		@SuppressWarnings("serial")
+		class BServlet extends HttpServlet {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				response.getWriter().write("failed");
+			}
+
+		}
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		registrations.add(getBundleContext().registerService(Servlet.class, new AServlet(), properties));
+
+		FailedServletDTO failedServletDTO = getFailedServletDTOByName("a");
+		assertNotNull(failedServletDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT, failedServletDTO.failureReason);
+		assertTrue(invoked.get());
+
+		Map<String, List<String>> response = requestAdvisor.request("a", null);
+		// init failed, no servlet
+		assertEquals("404", response.get("responseCode").get(0));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		registrations.add(getBundleContext().registerService(Servlet.class, new BServlet(), properties));
+
+		response = requestAdvisor.request("a", null);
+		// BServlet handles the request
+		assertEquals("200", response.get("responseCode").get(0));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_9.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_9.java
new file mode 100644
index 0000000..08ac3ef
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_4_9.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_4_9 extends BaseTest {
+
+	@Test
+	public void test_140_4_9() throws Exception {
+		BundleContext context = getBundleContext();
+
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		Servlet servlet = new HttpServlet() {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response)
+					throws ServletException, IOException {
+
+				invoked.set(true);
+
+				response.getWriter().write("a");
+			}
+
+		};
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/*");
+		registrations.add(context.registerService(Servlet.class, servlet, properties));
+
+		assertEquals("a", requestAdvisor.request("a"));
+		assertTrue(invoked.get());
+		invoked.set(false);
+		assertEquals("a", requestAdvisor.request("b.html"));
+		assertTrue(invoked.get());
+		invoked.set(false);
+		assertEquals("a", requestAdvisor.request("some/path/b.html"));
+		assertTrue(invoked.get());
+		assertEquals("404", requestAdvisor.request("", null).get("responseCode").get(0));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_6_1.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_6_1.java
new file mode 100644
index 0000000..a89c91e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_6_1.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.servlet.Servlet;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockServlet;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedResourceDTO;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_6_1 extends BaseTest {
+
+	@Test
+	public void test_140_6_1() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/*");
+		registrations.add(context.registerService(Servlet.class, new MockServlet().content("b"), properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, "/*");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, "/org/eclipse/equinox/http/servlet/tests");
+		ServiceRegistration<Object> sr = context.registerService(Object.class, new Object(), properties);
+		registrations.add(sr);
+
+		FailedResourceDTO failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNotNull(failedResourceDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE, failedResourceDTO.failureReason);
+
+		ResourceDTO resourceDTO = getResourceDTOByServiceId(
+				DEFAULT,
+				getServiceId(sr));
+		assertNull(resourceDTO);
+		assertEquals("b", requestAdvisor.request("index.txt"));
+
+		properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
+		sr.setProperties(properties);
+
+		resourceDTO = getResourceDTOByServiceId(
+				DEFAULT,
+				getServiceId(sr));
+		assertNotNull(resourceDTO);
+		assertEquals("a", requestAdvisor.request("index.txt"));
+
+		failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNull(failedResourceDTO);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_6_20to21_commonProperties.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_6_20to21_commonProperties.java
new file mode 100644
index 0000000..ee473cf
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_6_20to21_commonProperties.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedResourceDTO;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_6_20to21_commonProperties extends BaseTest {
+
+	@Test
+	public void test_140_6_20to21_commonProperties() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, "/other.txt");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, "/org/eclipse/equinox/http/servlet/tests/index.txt");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(osgi.http.whiteboard.context.name=foo)");
+		ServiceRegistration<Object> sr = context.registerService(Object.class, new Object(), properties);
+		registrations.add(sr);
+
+		FailedResourceDTO failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNotNull(failedResourceDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_NO_SERVLET_CONTEXT_MATCHING, failedResourceDTO.failureReason);
+
+		ResourceDTO resourceDTO = getResourceDTOByServiceId(
+				DEFAULT,
+				getServiceId(sr));
+		assertNull(resourceDTO);
+		assertEquals("404", requestAdvisor.request("other.txt", null).get("responseCode").get(0));
+
+		properties.remove(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT);
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, "(some=foo)");
+		sr.setProperties(properties);
+
+		failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNull(failedResourceDTO);
+
+		resourceDTO = getResourceDTOByServiceId(
+				DEFAULT,
+				getServiceId(sr));
+		assertNull(resourceDTO);
+		assertEquals("404", requestAdvisor.request("other.txt", null).get("responseCode").get(0));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_9_ServletContextDTO_custom_listener.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_9_ServletContextDTO_custom_listener.java
new file mode 100644
index 0000000..aaeefc5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_9_ServletContextDTO_custom_listener.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockSCL;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_9_ServletContextDTO_custom_listener extends BaseTest {
+
+	@Test
+	public void test_140_9_ServletContextDTO_custom_listener() throws Exception {
+		BundleContext context = getBundleContext();
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/a");
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		ServletContextDTO servletContextDTO = getServletContextDTOByName("a");
+		assertNotNull(servletContextDTO);
+
+		AtomicReference<ServletContext> sc1 = new AtomicReference<ServletContext>();
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(osgi.http.whiteboard.context.name=a)");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		ServiceRegistration<?> sr = context.registerService(ServletContextListener.class, new MockSCL(sc1), properties);
+		registrations.add(sr);
+
+		servletContextDTO = getServletContextDTOByName("a");
+		assertEquals(1, servletContextDTO.listenerDTOs.length);
+		assertEquals(getServiceId(sr), servletContextDTO.listenerDTOs[0].serviceId);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_9_ServletContextDTO_default_listener.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_9_ServletContextDTO_default_listener.java
new file mode 100644
index 0000000..d039ba4
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_140_9_ServletContextDTO_default_listener.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockSCL;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_140_9_ServletContextDTO_default_listener extends BaseTest {
+
+	@Test
+	public void test_140_9_ServletContextDTO_default_listener() throws Exception {
+		BundleContext context = getBundleContext();
+		ServletContextDTO servletContextDTO = getServletContextDTOByName(DEFAULT);
+		assertNotNull(servletContextDTO);
+
+		AtomicReference<ServletContext> sc1 = new AtomicReference<ServletContext>();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		ServiceRegistration<?> sr = context.registerService(ServletContextListener.class, new MockSCL(sc1), properties);
+		registrations.add(sr);
+
+		servletContextDTO = getServletContextDTOByName(DEFAULT);
+		assertEquals(1, servletContextDTO.listenerDTOs.length);
+		assertEquals(getServiceId(sr), servletContextDTO.listenerDTOs[0].serviceId);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_bindUsingContextSelect.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_bindUsingContextSelect.java
new file mode 100644
index 0000000..b9a011f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_bindUsingContextSelect.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockSCL;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_bindUsingContextSelect extends BaseTest {
+
+	@Test
+	public void test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_bindUsingContextSelect() throws Exception {
+		BundleContext context = getBundleContext();
+		String contextName = "context1";
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, contextName);
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/context1");
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		AtomicReference<ServletContext> sc1 = new AtomicReference<ServletContext>();
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(osgi.http.whiteboard.context.name=" + contextName + ")");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		registrations.add(context.registerService(ServletContextListener.class, new MockSCL(sc1), properties));
+
+		assertEquals(contextName, sc1.get().getServletContextName());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_tieGoesToOldest.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_tieGoesToOldest.java
new file mode 100644
index 0000000..6ceff6f
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_tieGoesToOldest.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockSCL;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_tieGoesToOldest extends BaseTest {
+
+	@Test
+	public void test_table_140_1_HTTP_WHITEBOARD_CONTEXT_NAME_tieGoesToOldest() throws Exception {
+		BundleContext context = getBundleContext();
+		String contextPath = "/context1";
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, DEFAULT);
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, contextPath);
+		properties.put(Constants.SERVICE_RANKING, new Integer(1000));
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, DEFAULT);
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/otherContext");
+		properties.put(Constants.SERVICE_RANKING, new Integer(1000));
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		AtomicReference<ServletContext> sc1 = new AtomicReference<ServletContext>();
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER, "true");
+		registrations.add(context.registerService(ServletContextListener.class, new MockSCL(sc1), properties));
+
+		assertEquals(DEFAULT, sc1.get().getServletContextName());
+		assertEquals(contextPath, sc1.get().getContextPath());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_PATH_type.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_PATH_type.java
new file mode 100644
index 0000000..8ad80a5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_PATH_type.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedServletContextDTO;
+import org.osgi.service.http.runtime.dto.RuntimeDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_1_HTTP_WHITEBOARD_CONTEXT_PATH_type extends BaseTest {
+
+	@Test
+	public void test_table_140_1_HTTP_WHITEBOARD_CONTEXT_PATH_type() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, "context");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, Boolean.FALSE);
+		registrations.add(context.registerService(ServletContextHelper.class, new ServletContextHelper() {}, properties));
+
+		HttpServiceRuntime httpServiceRuntime = getHttpServiceRuntime();
+
+		RuntimeDTO runtimeDTO = httpServiceRuntime.getRuntimeDTO();
+
+		FailedServletContextDTO[] failedServletContextDTOs = runtimeDTO.failedServletContextDTOs;
+
+		assertNotNull(failedServletContextDTOs);
+		assertEquals(1, failedServletContextDTOs.length);
+		assertEquals(
+				DTOConstants.FAILURE_REASON_VALIDATION_FAILED,
+				failedServletContextDTOs[0].failureReason);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED.java
new file mode 100644
index 0000000..0308440
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED extends BaseTest {
+
+	@Test
+	public void test_table_140_4_HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED() throws Exception {
+		BundleContext context = getBundleContext();
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		class AServlet extends HttpServlet {
+
+			final ExecutorService	executor	= Executors.newCachedThreadPool();
+
+			@Override
+			protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+				doGetAsync(req.startAsync());
+			}
+
+			private void doGetAsync(final AsyncContext asyncContext) {
+				executor.submit(new Callable<Void>() {
+					@Override
+					public Void call() throws Exception {
+						try {
+							invoked.set(true);
+
+							PrintWriter writer = asyncContext.getResponse().getWriter();
+
+							writer.print("a");
+						} finally {
+							asyncContext.complete();
+						}
+
+						return null;
+					}
+				});
+			}
+
+		}
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED, "true");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		ServiceRegistration<Servlet> srA = context.registerService(Servlet.class, new AServlet(), properties);
+		registrations.add(srA);
+
+		RequestInfoDTO requestInfoDTO = calculateRequestInfoDTO("/a");
+
+		assertNotNull(requestInfoDTO);
+		assertNotNull(requestInfoDTO.servletDTO);
+		assertTrue(requestInfoDTO.servletDTO.asyncSupported);
+		assertTrue(getServiceId(srA) == requestInfoDTO.servletDTO.serviceId);
+		assertEquals("a", requestInfoDTO.servletDTO.name);
+		assertEquals("a", requestAdvisor.request("a"));
+		assertTrue(invoked.get());
+		invoked.set(false);
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED, "false");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "b");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/b");
+		ServiceRegistration<Servlet> srB = context.registerService(Servlet.class, new AServlet(), properties);
+		registrations.add(srB);
+
+		assertEquals("500", requestAdvisor.request("b", null).get("responseCode").get(0));
+		assertFalse(invoked.get());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_4xx.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_4xx.java
new file mode 100644
index 0000000..a5f81b9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_4xx.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FailedErrorPageDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_4xx extends BaseTest {
+
+	@Test
+	public void test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_4xx() throws Exception {
+		BundleContext context = getBundleContext();
+
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		class AServlet extends HttpServlet {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				response.sendError(HttpServletResponse.SC_FORBIDDEN, "a");
+			}
+
+		}
+
+		@SuppressWarnings("serial")
+		class BServlet extends HttpServlet {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				invoked.set(true);
+				String message = (String) request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
+				response.getWriter().write((message == null) ? "" : message);
+			}
+
+		}
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		registrations.add(context.registerService(Servlet.class, new AServlet(), properties));
+
+		// Register the 4xx (b)
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "b");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, "4xx");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/b");
+		registrations.add(context.registerService(Servlet.class, new BServlet(), properties));
+
+		ServletDTO servletDTO = getServletDTOByName(DEFAULT, "b");
+		assertNotNull(servletDTO);
+		ErrorPageDTO errorPageDTO = getErrorPageDTOByName(DEFAULT, "b");
+		assertNotNull(errorPageDTO);
+		assertTrue(Arrays.binarySearch(errorPageDTO.errorCodes, HttpServletResponse.SC_FORBIDDEN) >= 0);
+
+		Map<String, List<String>> response = requestAdvisor.request("a", null);
+		assertEquals("a", response.get("responseBody").get(0));
+		assertTrue(invoked.get());
+		assertEquals(HttpServletResponse.SC_FORBIDDEN + "", response.get("responseCode").get(0));
+
+		// register a 4xx which will be shadowed (c)
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "c");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, "4xx");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/c");
+		registrations.add(context.registerService(Servlet.class, new BServlet(), properties));
+
+		FailedErrorPageDTO failedErrorPageDTO = getFailedErrorPageDTOByName("c");
+		assertNotNull(failedErrorPageDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE, failedErrorPageDTO.failureReason);
+
+		// register a specific 404 which shouldn't shadow 4xx (b)
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "d");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, "404");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/d");
+		properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
+		registrations.add(context.registerService(Servlet.class, new BServlet(), properties));
+
+		failedErrorPageDTO = getFailedErrorPageDTOByName("b");
+		assertNull(failedErrorPageDTO);
+		failedErrorPageDTO = getFailedErrorPageDTOByName("d");
+		assertNull(failedErrorPageDTO);
+		errorPageDTO = getErrorPageDTOByName(DEFAULT, "d");
+		assertNotNull(errorPageDTO);
+		assertEquals(404, errorPageDTO.errorCodes[0]);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_exception.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_exception.java
new file mode 100644
index 0000000..fb825e1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_exception.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_exception extends BaseTest {
+
+	@Test
+	public void test_table_140_4_HTTP_WHITEBOARD_SERVLET_ERROR_PAGE_exception() throws Exception {
+		BundleContext context = getBundleContext();
+
+		final AtomicBoolean invoked = new AtomicBoolean(false);
+
+		@SuppressWarnings("serial")
+		class AException extends ServletException {
+		}
+
+		@SuppressWarnings("serial")
+		class AServlet extends HttpServlet {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				throw new AException();
+			}
+
+		}
+
+		@SuppressWarnings("serial")
+		class BServlet extends HttpServlet {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				invoked.set(true);
+				String exception = (String) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE);
+				response.getWriter().write((exception == null) ? "" : exception);
+			}
+
+		}
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		registrations.add(context.registerService(Servlet.class, new AServlet(), properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "b");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, ServletException.class.getName());
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/error");
+		registrations.add(context.registerService(Servlet.class, new BServlet(), properties));
+
+		ServletDTO servletDTO = getServletDTOByName(DEFAULT, "b");
+		assertNotNull(servletDTO);
+		ErrorPageDTO errorPageDTO = getErrorPageDTOByName(DEFAULT, "b");
+		assertNotNull(errorPageDTO);
+		assertTrue(Arrays.binarySearch(errorPageDTO.exceptions, ServletException.class.getName()) >= 0);
+
+		Map<String, List<String>> response = requestAdvisor.request("a", null);
+		assertEquals(AException.class.getName(), response.get("responseBody").get(0));
+		assertTrue(invoked.get());
+		assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR + "", response.get("responseCode").get(0));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_error.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_error.java
new file mode 100644
index 0000000..ab253f1
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_error.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.io.IOException;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockFilter;
+import org.eclipse.equinox.http.servlet.tests.util.MockServlet;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_error extends BaseTest {
+
+	@Test
+	public void test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_error() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER, "ERROR");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET, "a");
+		ServiceRegistration<?> srA = context.registerService(Filter.class, new MockFilter().around("b"), properties);
+		registrations.add(srA);
+
+		FilterDTO filterDTO = getFilterDTOByName(DEFAULT, "a");
+		assertNotNull(filterDTO);
+		assertEquals(1, filterDTO.dispatcher.length);
+		assertEquals("ERROR", filterDTO.dispatcher[0]);
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, "4xx");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		ServiceRegistration<?> srB = context.registerService(Servlet.class, new MockServlet().content("a"), properties);
+		registrations.add(srB);
+
+		RequestInfoDTO requestInfoDTO = calculateRequestInfoDTO("/a");
+		assertNotNull(requestInfoDTO);
+		assertEquals(0, requestInfoDTO.filterDTOs.length);
+		assertEquals("a", requestAdvisor.request("a"));
+
+		@SuppressWarnings("serial")
+		MockServlet mockServlet = new MockServlet() {
+
+			@Override
+			protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+				response.sendError(HttpServletResponse.SC_NOT_FOUND);
+			}
+
+		};
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "b");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/b");
+		ServiceRegistration<?> srC = context.registerService(Servlet.class, mockServlet, properties);
+		registrations.add(srC);
+
+		Map<String, List<String>> response = requestAdvisor.request("b", null);
+		assertEquals("bab", response.get("responseBody").get(0));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_request.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_request.java
new file mode 100644
index 0000000..19f922b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_request.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockFilter;
+import org.eclipse.equinox.http.servlet.tests.util.MockServlet;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_request extends BaseTest {
+
+	@Test
+	public void test_table_140_5_HTTP_WHITEBOARD_FILTER_DISPATCHER_request() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/a");
+		ServiceRegistration<?> srA = context.registerService(Filter.class, new MockFilter().around("b"), properties);
+		registrations.add(srA);
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/a");
+		ServiceRegistration<?> srB = context.registerService(Servlet.class, new MockServlet().content("a"), properties);
+		registrations.add(srB);
+
+		RequestInfoDTO requestInfoDTO = calculateRequestInfoDTO("/a");
+		assertNotNull(requestInfoDTO);
+		assertEquals(1, requestInfoDTO.filterDTOs.length);
+		FilterDTO filterDTO = requestInfoDTO.filterDTOs[0];
+		assertEquals(getServiceId(srA), filterDTO.serviceId);
+		assertEquals(1, filterDTO.dispatcher.length);
+		assertEquals("REQUEST", filterDTO.dispatcher[0]);
+
+		assertEquals("bab", requestAdvisor.request("a"));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER, "REQUEST");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/a");
+		srA.setProperties(properties);
+
+		assertEquals("bab", requestAdvisor.request("a"));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_PATTERN.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_PATTERN.java
new file mode 100644
index 0000000..7e06520
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_PATTERN.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockFilter;
+import org.eclipse.equinox.http.servlet.tests.util.MockServlet;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedFilterDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_5_HTTP_WHITEBOARD_FILTER_PATTERN extends BaseTest {
+
+	@Test
+	public void test_table_140_5_HTTP_WHITEBOARD_FILTER_PATTERN() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, new String[] {"", "/"});
+		registrations.add(context.registerService(Servlet.class, new MockServlet().content("a"), properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/**");
+		ServiceRegistration<?> sr = context.registerService(Filter.class, new MockFilter().around("b"), properties);
+		registrations.add(sr);
+
+		FailedFilterDTO failedFilterDTO = getFailedFilterDTOByName("a");
+		assertNotNull(failedFilterDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_VALIDATION_FAILED, failedFilterDTO.failureReason);
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "/*");
+		sr.setProperties(properties);
+
+		failedFilterDTO = getFailedFilterDTOByName("a");
+		assertNull(failedFilterDTO);
+
+		RequestInfoDTO requestInfoDTO = calculateRequestInfoDTO("/a");
+		assertNotNull(requestInfoDTO);
+		assertEquals(1, requestInfoDTO.filterDTOs.length);
+		assertEquals("bab", requestAdvisor.request("a"));
+		assertEquals("bab", requestAdvisor.request("a.html"));
+		assertEquals("bab", requestAdvisor.request("some/path/b.html"));
+		assertEquals("a", requestAdvisor.request(""));
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "");
+		sr.setProperties(properties);
+
+		requestInfoDTO = calculateRequestInfoDTO("/");
+		assertNotNull(requestInfoDTO);
+		assertEquals(1, requestInfoDTO.filterDTOs.length);
+		assertEquals("a", requestAdvisor.request("a"));
+		assertEquals("a", requestAdvisor.request("a.html"));
+		assertEquals("a", requestAdvisor.request("some/path/b.html"));
+		assertEquals("bab", requestAdvisor.request(""));
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, "*.html");
+		sr.setProperties(properties);
+
+		requestInfoDTO = calculateRequestInfoDTO("/a.html");
+		assertNotNull(requestInfoDTO);
+		assertEquals(1, requestInfoDTO.filterDTOs.length);
+		assertEquals("a", requestAdvisor.request("a"));
+		assertEquals("bab", requestAdvisor.request("a.html"));
+		assertEquals("bab", requestAdvisor.request("some/path/b.html"));
+		assertEquals("a", requestAdvisor.request(""));
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_REGEX.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_REGEX.java
new file mode 100644
index 0000000..d2d89dd
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_5_HTTP_WHITEBOARD_FILTER_REGEX.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.regex.Pattern;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.eclipse.equinox.http.servlet.tests.util.MockFilter;
+import org.eclipse.equinox.http.servlet.tests.util.MockServlet;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedFilterDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_5_HTTP_WHITEBOARD_FILTER_REGEX extends BaseTest {
+
+	@Test
+	public void test_table_140_5_HTTP_WHITEBOARD_FILTER_REGEX() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, new String[] {"", "/"});
+		registrations.add(context.registerService(Servlet.class, new MockServlet().content("a"), properties));
+
+		properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME, "a");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX, "**");
+		ServiceRegistration<?> sr = context.registerService(Filter.class, new MockFilter().around("b"), properties);
+		registrations.add(sr);
+
+		FailedFilterDTO failedFilterDTO = getFailedFilterDTOByName("a");
+		assertNotNull(failedFilterDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_VALIDATION_FAILED, failedFilterDTO.failureReason);
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX, "/.+");
+		sr.setProperties(properties);
+
+		failedFilterDTO = getFailedFilterDTOByName("a");
+		assertNull(failedFilterDTO);
+
+		RequestInfoDTO requestInfoDTO = calculateRequestInfoDTO("/a");
+		assertNotNull(requestInfoDTO);
+		assertEquals(1, requestInfoDTO.filterDTOs.length);
+		assertEquals("bab", requestAdvisor.request("a"));
+		assertEquals("bab", requestAdvisor.request("a.html"));
+		assertEquals("bab", requestAdvisor.request("some/path/b.html"));
+		assertEquals("a", requestAdvisor.request(""));
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX, "/?");
+		sr.setProperties(properties);
+
+		requestInfoDTO = calculateRequestInfoDTO("/");
+		assertNotNull(requestInfoDTO);
+		assertEquals(1, requestInfoDTO.filterDTOs.length);
+		assertEquals("a", requestAdvisor.request("a"));
+		assertEquals("a", requestAdvisor.request("a.html"));
+		assertEquals("a", requestAdvisor.request("some/path/b.html"));
+		assertEquals("bab", requestAdvisor.request(""));
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX, ".*\\.html");
+		sr.setProperties(properties);
+
+		requestInfoDTO = calculateRequestInfoDTO("/a.html");
+		assertNotNull(requestInfoDTO);
+		assertEquals(1, requestInfoDTO.filterDTOs.length);
+		assertEquals("a", requestAdvisor.request("a"));
+		assertEquals("bab", requestAdvisor.request("a.html"));
+		assertEquals("bab", requestAdvisor.request("some/path/b.html"));
+		assertEquals("a", requestAdvisor.request(""));
+	}
+
+	@Test
+	public void patternCheck() {
+		assertTrue(Pattern.compile("/.*").matcher("/").matches());
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_6_HTTP_WHITEBOARD_RESOURCE_validation.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_6_HTTP_WHITEBOARD_RESOURCE_validation.java
new file mode 100644
index 0000000..9aab343
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/Test_table_140_6_HTTP_WHITEBOARD_RESOURCE_validation.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.eclipse.equinox.http.servlet.testbase.BaseTest;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+import org.osgi.service.http.runtime.dto.FailedResourceDTO;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class Test_table_140_6_HTTP_WHITEBOARD_RESOURCE_validation extends BaseTest {
+
+	@Test
+	public void test_table_140_6_HTTP_WHITEBOARD_RESOURCE_validation() throws Exception {
+		BundleContext context = getBundleContext();
+
+		Dictionary<String, Object> properties = new Hashtable<String, Object>();
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, 34l);
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, "/org/eclipse/equinox/http/servlet/tests");
+		ServiceRegistration<Object> sr = context.registerService(Object.class, new Object(), properties);
+		registrations.add(sr);
+
+		FailedResourceDTO failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNotNull(failedResourceDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_VALIDATION_FAILED, failedResourceDTO.failureReason);
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, "/*");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, 45);
+		sr.setProperties(properties);
+
+		failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNotNull(failedResourceDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_VALIDATION_FAILED, failedResourceDTO.failureReason);
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, "/*");
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, new String[] {"/a", "/b"});
+		sr.setProperties(properties);
+
+		failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNotNull(failedResourceDTO);
+		assertEquals(DTOConstants.FAILURE_REASON_VALIDATION_FAILED, failedResourceDTO.failureReason);
+
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, new String[] {"/a", "/b"});
+		properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, "/org/eclipse/equinox/http/servlet/tests/index.txt");
+		sr.setProperties(properties);
+
+		failedResourceDTO = getFailedResourceDTOByServiceId(getServiceId(sr));
+		assertNull(failedResourceDTO);
+
+		ResourceDTO resourceDTO = getResourceDTOByServiceId(
+				DEFAULT,
+				getServiceId(sr));
+		assertNotNull(resourceDTO);
+		assertEquals(2, resourceDTO.patterns.length);
+	}
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/bundle/Activator.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/bundle/Activator.java
deleted file mode 100644
index 1794535..0000000
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/bundle/Activator.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2011 IBM Corporation and others.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- *     IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.equinox.http.servlet.tests.bundle;
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-
-import javax.servlet.Servlet;
-
-import org.eclipse.equinox.http.servlet.tests.util.TestServletPrototype;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
-
-/*
- * The Bundle-Activator for the bundle. Ideally this class is kept as small as
- * possible. 
- */
-public class Activator extends Object implements BundleActivator {
-	private static Activator INSTANCE;
-
-	public static BundleContext getBundleContext() {
-		return Activator.INSTANCE != null ? Activator.INSTANCE.bundleContext : null;
-	}
-	
-	private BundleContext bundleContext;
-	
-	public Activator() {
-		super();
-		Activator.INSTANCE = this;
-	}
-
-	public void start(BundleContext bundleContext) throws Exception {
-		this.bundleContext = bundleContext;
-		Dictionary<String, Object> serviceProps = new Hashtable<String, Object>();
-		serviceProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/prototype/*");
-		TestServletPrototype testDriver = new TestServletPrototype(bundleContext);
-		bundleContext.registerService(Servlet.class, testDriver, serviceProps);
-	}
-	
-	public void stop(BundleContext bundleContext) throws Exception {
-		this.bundleContext = null;
-	}
-}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/index.txt b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/index.txt
new file mode 100644
index 0000000..2e65efe
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/index.txt
@@ -0,0 +1 @@
+a
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockFilter.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockFilter.java
new file mode 100644
index 0000000..30a3a4a
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockFilter.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests.util;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+public class MockFilter implements Filter {
+
+	@Override
+	public void destroy() {
+	}
+
+	@Override
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+			throws IOException, ServletException {
+
+		if (before != null) {
+			response.getWriter().write(before);
+		}
+		chain.doFilter(request, response);
+		if (after != null) {
+			response.getWriter().write(after);
+		}
+	}
+
+	@Override
+	public void init(FilterConfig config) throws ServletException {
+	}
+
+	public MockFilter after(String after) {
+		this.after = after;
+
+		return this;
+	}
+
+	public MockFilter around(String around) {
+		before(around);
+		after(around);
+
+		return this;
+	}
+
+	public MockFilter before(String before) {
+		this.before = before;
+
+		return this;
+	}
+
+	private String	after;
+	private String	before;
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockSCL.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockSCL.java
new file mode 100644
index 0000000..c2ea2ba
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockSCL.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests.util;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+public class MockSCL implements ServletContextListener {
+
+	public MockSCL(AtomicReference<ServletContext> sc) {
+		this.sc = sc;
+	}
+
+	@Override
+	public void contextDestroyed(ServletContextEvent arg0) {
+	}
+
+	@Override
+	public void contextInitialized(ServletContextEvent event) {
+		sc.set(event.getServletContext());
+	}
+
+	public ServletContext getSC() {
+		return sc.get();
+	}
+
+	private final AtomicReference<ServletContext> sc;
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockServlet.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockServlet.java
new file mode 100644
index 0000000..ea1348e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/MockServlet.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - tests
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.tests.util;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@SuppressWarnings("serial")
+public class MockServlet extends HttpServlet {
+
+	@Override
+	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+		if (content != null) {
+			response.getWriter().write(content);
+		}
+		if (code != null) {
+			response.sendError(code, errorMessage);
+		}
+		if (exception != null) {
+			if (exception instanceof IOException) {
+				throw (IOException) exception;
+			}
+			throw (ServletException) exception;
+		}
+	}
+
+	public MockServlet content(String content) {
+		this.content = content;
+
+		return this;
+	}
+
+	public MockServlet error(int code, String errorMessage) {
+		this.code = new Integer(code);
+		this.errorMessage = errorMessage;
+
+		return this;
+	}
+
+	public MockServlet exception(Exception exception) {
+		if (!(exception instanceof ServletException) &&
+				!(exception instanceof IOException)) {
+			this.exception = new ServletException(exception);
+		}
+
+		this.exception = exception;
+
+		return this;
+	}
+
+	private Integer	code;
+	private String	content;
+	private String	errorMessage;
+	private Exception exception;
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
index f422420..6d03d40 100644
--- a/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
+++ b/bundles/org.eclipse.equinox.http.servlet.tests/src/org/eclipse/equinox/http/servlet/tests/util/ServletRequestAdvisor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2014 IBM Corporation and others.
+ * Copyright (c) 2011, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -114,8 +114,8 @@
 		HttpURLConnection connection = (HttpURLConnection)url.openConnection();
 
 		connection.setInstanceFollowRedirects(false);
-		connection.setConnectTimeout(150 * 1000);
-		connection.setReadTimeout(150 * 1000);
+		connection.setConnectTimeout(150 * 100000);
+		connection.setReadTimeout(150 * 100000);
 		connection.connect();
 
 		InputStream stream = connection.getInputStream();
diff --git a/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
index d6285ac..2c4d643 100644
--- a/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.http.servlet/META-INF/MANIFEST.MF
@@ -40,6 +40,6 @@
   uses:="org.osgi.service.http",
  osgi.service;
   objectClass:List<String>="org.osgi.service.http.runtime.HttpServiceRuntime";
-  uses:="org.osgi.service.http.runtime"
+  uses:="org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto"
 Automatic-Module-Name: org.eclipse.equinox.http.servlet
 
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultServletContextHelper.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultServletContextHelper.java
new file mode 100644
index 0000000..04fc3e6
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultServletContextHelper.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 27, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - initial API and implementation and/or initial
+ *                    documentation
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.internal;
+
+import java.net.URL;
+import org.eclipse.equinox.http.servlet.internal.util.Const;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.context.ServletContextHelper;
+
+public class DefaultServletContextHelper extends ServletContextHelper implements HttpContext {
+	private final Bundle bundle;
+	public DefaultServletContextHelper(Bundle bundle) {
+		super(bundle);
+		this.bundle = bundle;
+	}
+
+	@Override
+	public URL getResource(String name) {
+		if (name != null) {
+			if (name.startsWith(Const.SLASH)) {
+				name = name.substring(1);
+			}
+
+			return bundle.getResource(name);
+		}
+		return null;
+	}
+
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultServletContextHelperFactory.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultServletContextHelperFactory.java
new file mode 100644
index 0000000..ba1358b
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/DefaultServletContextHelperFactory.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 27, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - initial API and implementation and/or initial
+ *                    documentation
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.internal;
+
+import org.osgi.framework.*;
+import org.osgi.service.http.context.ServletContextHelper;
+
+public class DefaultServletContextHelperFactory implements ServiceFactory<ServletContextHelper> {
+	@Override
+	public ServletContextHelper getService(
+		Bundle bundle,
+		ServiceRegistration<ServletContextHelper> registration) {
+		return new DefaultServletContextHelper(bundle);
+	}
+
+	@Override
+	public void ungetService(
+		Bundle bundle,
+		ServiceRegistration<ServletContextHelper> registration,
+		ServletContextHelper service) {
+		// do nothing
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
index 0ba9420..296235d 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2017 Cognos Incorporated, IBM Corporation and others.
+ * Copyright (c) 2005, 2019 Cognos Incorporated, IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -16,59 +16,41 @@
 
 package org.eclipse.equinox.http.servlet.internal;
 
-import java.io.IOException;
-import java.net.URL;
 import java.security.*;
 import java.util.Dictionary;
-import javax.servlet.*;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
 import org.eclipse.equinox.http.servlet.ExtendedHttpService;
+import org.eclipse.equinox.http.servlet.internal.context.WrappedHttpContext;
+import org.eclipse.equinox.http.servlet.internal.util.Const;
 import org.eclipse.equinox.http.servlet.internal.util.Throw;
-import org.osgi.framework.Bundle;
+import org.osgi.framework.*;
 import org.osgi.service.http.*;
+import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
 public class HttpServiceImpl implements HttpService, ExtendedHttpService {
 
+	private static AtomicLong legacyIdGenerator = new AtomicLong(0);
+
 	final Bundle bundle; //The bundle associated with this instance of http service
 	final HttpServiceRuntimeImpl httpServiceRuntime;
-	private boolean shutdown = false; // We prevent use of this instance if HttpServiceFactory.ungetService has called unregisterAliases.
+	private volatile boolean shutdown = false; // We prevent use of this instance if HttpServiceFactory.ungetService has called unregisterAliases.
 
-	class DefaultHttpContext implements HttpContext {
-		/**
-		 * @throws IOException  
-		 */
-		@Override
-		public boolean handleSecurity(
-			HttpServletRequest request, HttpServletResponse response)
-			throws IOException {
-			return true;
-		}
-
-		@Override
-		public URL getResource(String name) {
-			if (name != null) {
-				if (name.startsWith("/")) { //$NON-NLS-1$
-					name = name.substring(1);
-				}
-
-				return bundle.getResource(name);
-			}
-			return null;
-		}
-
-		@Override
-		public String getMimeType(String name) {
-			return null;
-		}
-		
-	}
+	private final ConcurrentMap<HttpContext, ServiceReference<DefaultServletContextHelper>> contextMap = new ConcurrentHashMap<HttpContext, ServiceReference<DefaultServletContextHelper>>();
+	private final ServiceReference<DefaultServletContextHelper> defaultHttpContextReference;
 
 	public HttpServiceImpl(
 		Bundle bundle, HttpServiceRuntimeImpl httpServiceRuntime) {
 
 		this.bundle = bundle;
 		this.httpServiceRuntime = httpServiceRuntime;
+		defaultHttpContextReference = this.bundle.getBundleContext().getServiceReference(DefaultServletContextHelper.class);
 	}
 
 	/**
@@ -77,27 +59,31 @@
 	public synchronized HttpContext createDefaultHttpContext() {
 		checkShutdown();
 
-		return new DefaultHttpContext();
+		DefaultServletContextHelper defaultServletContextHelper = bundle.getBundleContext().getService(defaultHttpContextReference);
+
+		contextMap.putIfAbsent(defaultServletContextHelper, defaultHttpContextReference);
+
+		return defaultServletContextHelper;
 	}
 
 	/**
-	 * @throws ServletException 
+	 * @throws ServletException
 	 * @see ExtendedHttpService#registerFilter(String, Filter, Dictionary, HttpContext)
 	 */
 	public synchronized void registerFilter(
-			final String alias, final Filter filter, 
+			final String alias, final Filter filter,
 			final Dictionary<String, String> initparams,
 			HttpContext httpContext)
 		throws ServletException {
 
 		checkShutdown();
-
-		final HttpContext finalHttpContext = httpContext == null ? createDefaultHttpContext() : httpContext;
+		httpContext = httpContext == null ? createDefaultHttpContext() : registerContext(httpContext);
+		final ServiceReference<DefaultServletContextHelper> serviceReference = contextMap.get(httpContext);
 		try {
 			AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
 				@Override
 				public Void run() throws ServletException {
-					httpServiceRuntime.registerHttpServiceFilter(bundle, alias, filter, initparams, finalHttpContext);
+					httpServiceRuntime.registerHttpServiceFilter(bundle, alias, filter, initparams, serviceReference);
 					return null;
 				}
 			});
@@ -109,7 +95,7 @@
 	}
 
 	/**
-	 * @throws NamespaceException 
+	 * @throws NamespaceException
 	 * @see HttpService#registerResources(String, String, HttpContext)
 	 */
 	public synchronized void registerResources(
@@ -117,12 +103,13 @@
 		throws NamespaceException {
 
 		checkShutdown();
-		final HttpContext finalHttpContext = httpContext == null ? createDefaultHttpContext() : httpContext;
+		httpContext = httpContext == null ? createDefaultHttpContext() : registerContext(httpContext);
+		final ServiceReference<DefaultServletContextHelper> serviceReference = contextMap.get(httpContext);
 		try {
 			AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
 				@Override
 				public Void run() throws NamespaceException {
-					httpServiceRuntime.registerHttpServiceResources(bundle, alias, name, finalHttpContext);
+					httpServiceRuntime.registerHttpServiceResources(bundle, alias, name, serviceReference);
 					return null;
 				}
 			});
@@ -133,22 +120,23 @@
 	}
 
 	/**
-	 * @throws ServletException 
-	 * @throws NamespaceException 
+	 * @throws ServletException
+	 * @throws NamespaceException
 	 * @see HttpService#registerServlet(String, Servlet, Dictionary, HttpContext)
 	 */
 	public synchronized void registerServlet(
-			final String alias, final Servlet servlet, 
+			final String alias, final Servlet servlet,
 			final Dictionary initparams, HttpContext httpContext)
 		throws ServletException, NamespaceException {
 
 		checkShutdown();
-		final HttpContext finalHttpContext = httpContext == null ? createDefaultHttpContext() : httpContext;
+		httpContext = httpContext == null ? createDefaultHttpContext() : registerContext(httpContext);
+		final ServiceReference<DefaultServletContextHelper> serviceReference = contextMap.get(httpContext);
 		try {
 			AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
 				@Override
 				public Void run() throws NamespaceException, ServletException {
-					httpServiceRuntime.registerHttpServiceServlet(bundle, alias, servlet, initparams, finalHttpContext);
+					httpServiceRuntime.registerHttpServiceServlet(bundle, alias, servlet, initparams, serviceReference);
 					return null;
 				}
 			});
@@ -188,4 +176,29 @@
 				"Service instance is already shutdown"); //$NON-NLS-1$
 		}
 	}
+
+	private long generateLegacyId() {
+		return legacyIdGenerator.getAndIncrement();
+	}
+
+	private HttpContext registerContext(HttpContext httpContext) {
+		ServiceReference<? extends HttpContext> serviceReference = contextMap.get(httpContext);
+
+		if (serviceReference == null) {
+			Dictionary<String, Object> props = new Hashtable<String, Object>();
+			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, httpContext.getClass().getName().replaceAll("[^a-zA-Z_0-9\\-]", "_") + "-" + generateLegacyId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/"); //$NON-NLS-1$
+			props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, httpServiceRuntime.getTargetFilter());
+			props.put(Const.EQUINOX_LEGACY_CONTEXT_HELPER, Boolean.TRUE);
+			props.put(Const.EQUINOX_LEGACY_HTTP_CONTEXT_INITIATING_ID, bundle.getBundleId());
+
+			@SuppressWarnings("unchecked")
+			ServiceRegistration<DefaultServletContextHelper> registration = (ServiceRegistration<DefaultServletContextHelper>)bundle.getBundleContext().registerService(ServletContextHelper.class.getName(), new WrappedHttpContext(httpContext, bundle), props);
+
+			contextMap.put(httpContext, registration.getReference());
+		}
+
+		return httpContext;
+	}
+
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceObjectRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceObjectRegistration.java
index 72348f2..c78adbf 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceObjectRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceObjectRegistration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) Dec 1, 2014 Liferay, Inc.
+ * Copyright (c) 2014, 2019 Liferay, Inc.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -9,27 +9,24 @@
  * SPDX-License-Identifier: EPL-2.0
  *
  * Contributors:
- *    Liferay, Inc. - initial API and implementation and/or initial 
+ *    Liferay, Inc. - initial API and implementation and/or initial
  *                    documentation
  ******************************************************************************/
 
 package org.eclipse.equinox.http.servlet.internal;
 
-import org.eclipse.equinox.http.servlet.internal.context.HttpContextHelperFactory;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.ServiceRegistration;
 
 public class HttpServiceObjectRegistration {
 	public final Object serviceKey;
 	public final ServiceRegistration<?> registration;
-	public final HttpContextHelperFactory factory;
 	public final Bundle bundle;
 	public HttpServiceObjectRegistration(
 		Object serviceKey, ServiceRegistration<?> registration,
-		HttpContextHelperFactory factory, Bundle bundle) {
+		Bundle bundle) {
 		this.serviceKey = serviceKey;
 		this.registration = registration;
-		this.factory = factory;
 		this.bundle = bundle;
 	}
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
index 1ad919b..a69f111 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/HttpServiceRuntimeImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2016 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -16,9 +16,8 @@
 
 import java.io.IOException;
 import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.Map.Entry;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicReference;
 import javax.servlet.*;
 import javax.servlet.Filter;
@@ -26,7 +25,10 @@
 import javax.servlet.http.HttpSessionListener;
 import org.eclipse.equinox.http.servlet.context.ContextPathCustomizer;
 import org.eclipse.equinox.http.servlet.dto.ExtendedFailedServletDTO;
-import org.eclipse.equinox.http.servlet.internal.context.*;
+import org.eclipse.equinox.http.servlet.internal.context.ContextController;
+import org.eclipse.equinox.http.servlet.internal.context.DispatchTargets;
+import org.eclipse.equinox.http.servlet.internal.dto.ExtendedErrorPageDTO;
+import org.eclipse.equinox.http.servlet.internal.dto.ExtendedFailedServletContextDTO;
 import org.eclipse.equinox.http.servlet.internal.error.*;
 import org.eclipse.equinox.http.servlet.internal.servlet.HttpSessionTracker;
 import org.eclipse.equinox.http.servlet.internal.servlet.Match;
@@ -34,7 +36,6 @@
 import org.eclipse.equinox.http.servlet.session.HttpSessionInvalidator;
 import org.osgi.framework.*;
 import org.osgi.framework.dto.ServiceReferenceDTO;
-import org.osgi.service.http.HttpContext;
 import org.osgi.service.http.NamespaceException;
 import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.service.http.runtime.HttpServiceRuntime;
@@ -86,7 +87,8 @@
 		defaultContextProps.put(Constants.SERVICE_RANKING, Integer.MIN_VALUE);
 		defaultContextProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, Const.SLASH);
 		defaultContextProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, this.targetFilter);
-		defaultContextReg = consumingContext.registerService(ServletContextHelper.class, new DefaultServletContextHelperFactory(), defaultContextProps);
+		defaultContextReg = consumingContext.registerService(
+			new String [] {ServletContextHelper.class.getName(), DefaultServletContextHelper.class.getName()}, new DefaultServletContextHelperFactory(), defaultContextProps);
 	}
 
 	@Override
@@ -98,27 +100,9 @@
 			return result;
 		}
 
-		String contextName = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
-		String contextPath = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH);
-
 		try {
-			if (contextName == null) {
-				throw new IllegalContextNameException(
-					HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + " is null. Ignoring!", //$NON-NLS-1$
-					DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
-			}
-
-			if (contextPath == null) {
-				throw new IllegalContextPathException(
-					HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + " is null. Ignoring!", //$NON-NLS-1$
-					DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
-			}
-
-			contextPath = adaptContextPath(contextPath, serviceReference);
-
 			ContextController contextController = new ContextController(
-				trackingContext, consumingContext, serviceReference, new ProxyContext(contextName, parentServletContext),
-				this, contextName, contextPath, httpSessionTracker);
+				trackingContext, consumingContext, serviceReference, parentServletContext, this);
 
 			controllerMap.put(serviceReference, contextController);
 
@@ -127,18 +111,18 @@
 		catch (HttpWhiteboardFailureException hwfe) {
 			parentServletContext.log(hwfe.getMessage(), hwfe);
 
-			recordFailedServletContextDTO(serviceReference, contextName, contextPath, hwfe.getFailureReason());
+			recordFailedServletContextDTO(serviceReference, 0, hwfe.getFailureReason());
 		}
 		catch (Exception e) {
 			parentServletContext.log(e.getMessage(), e);
 
-			recordFailedServletContextDTO(serviceReference, contextName, contextPath, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
+			recordFailedServletContextDTO(serviceReference, 0, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
 		}
 
 		return result;
 	}
 
-	private String adaptContextPath(String contextPath, ServiceReference<ServletContextHelper> helper) {
+	public String adaptContextPath(String contextPath, ServiceReference<ServletContextHelper> helper) {
 		ContextPathCustomizer pathAdaptor = contextPathCustomizerHolder.getHighestRanked();
 		if (pathAdaptor != null) {
 			String contextPrefix = pathAdaptor.getContextPathPrefix(helper);
@@ -164,8 +148,16 @@
 		return null;
 	}
 
+	public boolean isDefaultContext(ContextController contextController) {
+		ServiceReference<?> thisReference = defaultContextReg.getReference();
+		ServiceReference<ServletContextHelper> contextReference = contextController.getServiceReference();
+		if (thisReference == null) throw new NullPointerException("Default Context Service reference is null. " + this); //$NON-NLS-1$
+		if (contextReference == null) throw new NullPointerException("Context Service reference is null. " + contextController); //$NON-NLS-1$
+		return thisReference.equals(contextReference);
+	}
+
 	@Override
-	public RequestInfoDTO calculateRequestInfoDTO(String path) {
+	public synchronized RequestInfoDTO calculateRequestInfoDTO(String path) {
 		RequestInfoDTO requestInfoDTO = new RequestInfoDTO();
 
 		requestInfoDTO.path = path;
@@ -180,7 +172,7 @@
 		return requestInfoDTO;
 	}
 
-	public void destroy() {
+	public synchronized void destroy() {
 		invalidatorReg.unregister();
 		defaultContextReg.unregister();
 
@@ -197,14 +189,13 @@
 		failedServletDTOs.clear();
 
 		httpSessionTracker.clear();
+		registeredObjects.clear();
 
 		httpSessionTracker = null;
 		attributes = null;
 		trackingContext = null;
 		consumingContext = null;
-		legacyIdGenerator = null;
 		parentServletContext = null;
-		registeredObjects = null;
 		contextServiceTracker = null;
 		contextPathCustomizerHolder = null;
 	}
@@ -229,26 +220,43 @@
 				requestInfoDTO);
 		}
 
-		if (dispatchTargets == null) {
+		if (dispatchTargets == null && !Const.SLASH.equals(pathString)) {
 			// regex match
 			dispatchTargets = getDispatchTargets(
 				requestURI, null, queryString, Match.REGEX, requestInfoDTO);
 		}
 
 		if (dispatchTargets == null) {
-			// handle '/' aliases
+			// handle with servlet mapped to '/'
+			// the servletpath is the requestURI minus the contextpath and the pathinfo is null
 			dispatchTargets = getDispatchTargets(
 				requestURI, null, queryString, Match.DEFAULT_SERVLET,
 				requestInfoDTO);
 		}
 
+		if (dispatchTargets == null && Const.SLASH.equals(pathString)) {
+			// handle with servlet mapped to '' (empty string)
+			// the pathinfo is '/' and the servletpath and contextpath are the empty string ("")
+			dispatchTargets = getDispatchTargets(
+				requestURI, null, queryString, Match.CONTEXT_ROOT,
+				requestInfoDTO);
+		}
+
 		return dispatchTargets;
 	}
 
+	public HttpSessionTracker getHttpSessionTracker() {
+		return httpSessionTracker;
+	}
+
 	public Set<Object> getRegisteredObjects() {
 		return registeredObjects;
 	}
 
+	public String getTargetFilter() {
+		return targetFilter;
+	}
+
 	public List<String> getHttpServiceEndpoints() {
 		return StringPlus.from(
 			attributes.get(
@@ -256,24 +264,33 @@
 	}
 
 	@Override
-	public RuntimeDTO getRuntimeDTO() {
+	public synchronized RuntimeDTO getRuntimeDTO() {
 		RuntimeDTO runtimeDTO = new RuntimeDTO();
 
-		runtimeDTO.serviceDTO = getServiceDTO();
-
-		// TODO FailedErrorDTOs
-
-		runtimeDTO.failedErrorPageDTOs = null;
+		runtimeDTO.failedErrorPageDTOs = getFailedErrorPageDTOs();
 		runtimeDTO.failedFilterDTOs = getFailedFilterDTOs();
 		runtimeDTO.failedListenerDTOs = getFailedListenerDTOs();
 		runtimeDTO.failedResourceDTOs = getFailedResourceDTOs();
 		runtimeDTO.failedServletContextDTOs = getFailedServletContextDTO();
 		runtimeDTO.failedServletDTOs = getFailedServletDTOs();
+		runtimeDTO.serviceDTO = getServiceDTO();
 		runtimeDTO.servletContextDTOs = getServletContextDTOs();
 
 		return runtimeDTO;
 	}
 
+	private FailedErrorPageDTO[] getFailedErrorPageDTOs() {
+		Collection<FailedErrorPageDTO> fepDTOs = failedErrorPageDTOs.values();
+
+		List<FailedErrorPageDTO> copies = new ArrayList<FailedErrorPageDTO>();
+
+		for (FailedErrorPageDTO failedErrorPageDTO : fepDTOs) {
+			copies.add(DTOUtil.clone(failedErrorPageDTO));
+		}
+
+		return copies.toArray(new FailedErrorPageDTO[0]);
+	}
+
 	private ServiceReferenceDTO getServiceDTO() {
 		ServiceReferenceDTO[] services = consumingContext.getBundle().adapt(ServiceReferenceDTO[].class);
 		for (ServiceReferenceDTO serviceDTO : services) {
@@ -303,22 +320,31 @@
 			return true;
 		}
 
-		org.osgi.framework.Filter targetFilter;
+		org.osgi.framework.Filter whiteboardTargetFilter;
 
 		try {
-			targetFilter = FrameworkUtil.createFilter(target);
+			whiteboardTargetFilter = FrameworkUtil.createFilter(target);
 		}
 		catch (InvalidSyntaxException ise) {
 			throw new IllegalArgumentException(ise);
 		}
 
-		if (targetFilter.matches(attributes)) {
+		if (whiteboardTargetFilter.matches(attributes)) {
 			return true;
 		}
 
 		return false;
 	}
 
+	public boolean matchesAnyContext(ServiceReference<?> serviceReference) {
+		for (ContextController contextController : controllerMap.values()) {
+			if (contextController.matches(serviceReference)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
 	@Override
 	public synchronized void modifiedService(
 		ServiceReference<ServletContextHelper> serviceReference,
@@ -336,10 +362,16 @@
 
 		ContextController contextController = contextControllerRef.get();
 		if (contextController != null) {
+			Iterator<Entry<ServiceReference<ServletContextHelper>, ExtendedFailedServletContextDTO>> iterator = failedServletContextDTOs.entrySet().iterator();
+			while (iterator.hasNext()) {
+				if (iterator.next().getValue().shadowingServiceId == contextController.getServiceId()) {
+					iterator.remove();
+				}
+			}
 			contextController.destroy();
 		}
-		controllerMap.remove(serviceReference);
 		failedServletContextDTOs.remove(serviceReference);
+		controllerMap.remove(serviceReference);
 		trackingContext.ungetService(serviceReference);
 	}
 
@@ -373,8 +405,8 @@
 		return null;
 	}
 
-	long generateLegacyId() {
-		return legacyIdGenerator.getAndIncrement();
+	public Collection<ContextController> getContextControllers() {
+		return controllerMap.values();
 	}
 
 	public DispatchTargets getDispatchTargets(
@@ -398,9 +430,9 @@
 		String servletPath = requestURI;
 		String pathInfo = null;
 
-		if (match == Match.DEFAULT_SERVLET) {
-			pathInfo = servletPath;
-			servletPath = Const.SLASH;
+		if (match == Match.CONTEXT_ROOT) {
+			pathInfo = Const.SLASH;
+			servletPath = Const.BLANK;
 		}
 
 		do {
@@ -415,7 +447,7 @@
 				}
 			}
 
-			if (match == Match.EXACT) {
+			if ((match == Match.EXACT) || (match == Match.CONTEXT_ROOT) || (match == Match.DEFAULT_SERVLET)) {
 				break;
 			}
 
@@ -444,7 +476,7 @@
 			copies.add(DTOUtil.clone(failedFilterDTO));
 		}
 
-		return copies.toArray(new FailedFilterDTO[copies.size()]);
+		return copies.toArray(new FailedFilterDTO[0]);
 	}
 
 	private FailedListenerDTO[] getFailedListenerDTOs() {
@@ -456,10 +488,10 @@
 			copies.add(DTOUtil.clone(failedListenerDTO));
 		}
 
-		return copies.toArray(new FailedListenerDTO[copies.size()]);
+		return copies.toArray(new FailedListenerDTO[0]);
 	}
 
-	private FailedResourceDTO[] getFailedResourceDTOs() {
+	public FailedResourceDTO[] getFailedResourceDTOs() {
 		Collection<FailedResourceDTO> frDTOs = failedResourceDTOs.values();
 
 		List<FailedResourceDTO> copies = new ArrayList<FailedResourceDTO>();
@@ -468,11 +500,11 @@
 			copies.add(DTOUtil.clone(failedResourceDTO));
 		}
 
-		return copies.toArray(new FailedResourceDTO[copies.size()]);
+		return copies.toArray(new FailedResourceDTO[0]);
 	}
 
 	private FailedServletContextDTO[] getFailedServletContextDTO() {
-		Collection<FailedServletContextDTO> fscDTOs = failedServletContextDTOs.values();
+		Collection<ExtendedFailedServletContextDTO> fscDTOs = failedServletContextDTOs.values();
 
 		List<FailedServletContextDTO> copies = new ArrayList<FailedServletContextDTO>();
 
@@ -480,7 +512,7 @@
 			copies.add(DTOUtil.clone(failedServletContextDTO));
 		}
 
-		return copies.toArray(new FailedServletContextDTO[copies.size()]);
+		return copies.toArray(new FailedServletContextDTO[0]);
 	}
 
 	private FailedServletDTO[] getFailedServletDTOs() {
@@ -492,28 +524,27 @@
 			copies.add(DTOUtil.clone(failedServletDTO));
 		}
 
-		return copies.toArray(new FailedServletDTO[copies.size()]);
+		return copies.toArray(new FailedServletDTO[0]);
 	}
 
-	private ServletContextDTO[] getServletContextDTOs() {
+	public ServletContextDTO[] getServletContextDTOs() {
 		List<ServletContextDTO> servletContextDTOs = new ArrayList<ServletContextDTO>();
 
 		for (ContextController contextController : controllerMap.values()) {
 			servletContextDTOs.add(contextController.getServletContextDTO());
 		}
 
-		return servletContextDTOs.toArray(
-			new ServletContextDTO[servletContextDTOs.size()]);
+		return servletContextDTOs.toArray(new ServletContextDTO[0]);
 	}
 
 	public void registerHttpServiceFilter(
-		Bundle bundle, String alias, Filter filter, Dictionary<String, String> initparams, HttpContext httpContext) throws ServletException {
+		Bundle bundle, String alias, Filter filter, Dictionary<String, String> initparams, ServiceReference<? extends ServletContextHelper> serviceReference) {
 
 		if (alias == null) {
-			throw new IllegalArgumentException("Alias cannot be null");
+			throw new IllegalArgumentException("Alias cannot be null"); //$NON-NLS-1$
 		}
 		if (filter == null) {
-			throw new IllegalArgumentException("Filter cannot be null");
+			throw new IllegalArgumentException("Filter cannot be null"); //$NON-NLS-1$
 		}
 
 		ContextController.checkPattern(alias);
@@ -539,7 +570,6 @@
 			if ((initparams != null) && (initparams.get(Const.FILTER_NAME) != null)) {
 				filterName = initparams.get(Const.FILTER_NAME);
 			}
-			HttpContextHelperFactory factory = getOrRegisterHttpContextHelperFactory(bundle, httpContext);
 
 			HttpServiceObjectRegistration objectRegistration = null;
 			ServiceRegistration<Filter> registration = null;
@@ -548,8 +578,7 @@
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, targetFilter);
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN, alias);
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME, filterName);
-				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, "(" + Const.EQUINOX_LEGACY_CONTEXT_HELPER + "=true)"); //$NON-NLS-1$ //$NON-NLS-2$
-				props.put(Const.EQUINOX_LEGACY_CONTEXT_SELECT, factory.getFilter());
+				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, getFilter(serviceReference));
 				props.put(Const.EQUINOX_LEGACY_TCCL_PROP, Thread.currentThread().getContextClassLoader());
 				props.put(Constants.SERVICE_RANKING, findFilterPriority(initparams));
 				fillInitParams(props, initparams, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_INIT_PARAM_PREFIX);
@@ -560,7 +589,7 @@
 				// check that init got called and did not throw an exception
 				filterFactory.checkForError();
 
-				objectRegistration = new HttpServiceObjectRegistration(filter, registration, factory, bundle);
+				objectRegistration = new HttpServiceObjectRegistration(filter, registration, bundle);
 				Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
 				if (objectRegistrations == null) {
 					objectRegistrations = new HashSet<HttpServiceObjectRegistration>();
@@ -571,8 +600,6 @@
 			} finally {
 				if (objectRegistration == null || !legacyMappings.containsKey(objectRegistration.serviceKey)) {
 					// something bad happened above (likely going to throw a runtime exception)
-					// need to clean up the factory reference
-					decrementFactoryUseCount(factory);
 					if (registration != null) {
 						registration.unregister();
 					}
@@ -617,17 +644,17 @@
 		}
 
 		throw new IllegalArgumentException(
-			"filter-priority must be an integer between -1000 and 1000 but " +
-				"was: " + filterPriority);
+			"filter-priority must be an integer between -1000 and 1000 but " + //$NON-NLS-1$
+				"was: " + filterPriority); //$NON-NLS-1$
 	}
 
 	public void registerHttpServiceResources(
-		Bundle bundle, String alias, String name, HttpContext httpContext) throws NamespaceException {
+		Bundle bundle, String alias, String name, ServiceReference<? extends ServletContextHelper> serviceReference) throws NamespaceException {
 		if (alias == null) {
-			throw new IllegalArgumentException("Alias cannot be null");
+			throw new IllegalArgumentException("Alias cannot be null"); //$NON-NLS-1$
 		}
 		if (name == null) {
-			throw new IllegalArgumentException("Name cannot be null");
+			throw new IllegalArgumentException("Name cannot be null"); //$NON-NLS-1$
 		}
 		String pattern = alias;
 		if (pattern.startsWith("/*.")) { //$NON-NLS-1$
@@ -643,9 +670,9 @@
 
 		synchronized (legacyMappings) {
 			HttpServiceObjectRegistration objectRegistration = null;
-			HttpContextHelperFactory factory = getOrRegisterHttpContextHelperFactory(bundle, httpContext);
+			ServiceRegistration<?> registration = null;
 			try {
-				String fullAlias = getFullAlias(alias, factory);
+				String fullAlias = getFullAlias(alias, serviceReference);
 				HttpServiceObjectRegistration existing = legacyMappings.get(fullAlias);
 				if (existing != null) {
 					throw new PatternInUseException(alias);
@@ -654,11 +681,11 @@
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, targetFilter);
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN, pattern);
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX, name);
-				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, factory.getFilter());
+				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, getFilter(serviceReference));
 				props.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
 				props.put(Const.EQUINOX_LEGACY_TCCL_PROP, Thread.currentThread().getContextClassLoader());
-				ServiceRegistration<?> registration = bundle.getBundleContext().registerService(String.class, "resource", props); //$NON-NLS-1$
-				objectRegistration = new HttpServiceObjectRegistration(fullAlias, registration, factory, bundle);
+				registration = bundle.getBundleContext().registerService(String.class, "resource", props); //$NON-NLS-1$
+				objectRegistration = new HttpServiceObjectRegistration(fullAlias, registration, bundle);
 
 				Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
 				if (objectRegistrations == null) {
@@ -678,19 +705,26 @@
 				if (objectRegistration == null || !legacyMappings.containsKey(objectRegistration.serviceKey)) {
 					// something bad happened above (likely going to throw a runtime exception)
 					// need to clean up the factory reference
-					decrementFactoryUseCount(factory);
+					if (registration != null) {
+						registration.unregister();
+					}
 				}
 			}
 		}
 	}
 
+	private Object getFilter(ServiceReference<? extends ServletContextHelper> serviceReference) {
+		String ctxName = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
+		return String.format("(%s=%s)", HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, ctxName); //$NON-NLS-1$
+	}
+
 	public void registerHttpServiceServlet(
-		Bundle bundle, String alias, Servlet servlet, Dictionary<String, String> initparams, HttpContext httpContext) throws NamespaceException, ServletException{
+		Bundle bundle, String alias, Servlet servlet, Dictionary<String, String> initparams, ServiceReference<? extends ServletContextHelper> serviceReference) throws NamespaceException, ServletException{
 		if (alias == null) {
-			throw new IllegalArgumentException("Alias cannot be null");
+			throw new IllegalArgumentException("Alias cannot be null"); //$NON-NLS-1$
 		}
 		if (servlet == null) {
-			throw new IllegalArgumentException("Servlet cannot be null");
+			throw new IllegalArgumentException("Servlet cannot be null"); //$NON-NLS-1$
 		}
 
 		// check the pattern against the original input
@@ -713,9 +747,8 @@
 			}
 			HttpServiceObjectRegistration objectRegistration = null;
 			ServiceRegistration<Servlet> registration = null;
-			HttpContextHelperFactory factory = getOrRegisterHttpContextHelperFactory(bundle, httpContext);
 			try {
-				String fullAlias = getFullAlias(alias, factory);
+				String fullAlias = getFullAlias(alias, serviceReference);
 				HttpServiceObjectRegistration existing = legacyMappings.get(fullAlias);
 				if (existing != null) {
 					throw new PatternInUseException(alias);
@@ -729,7 +762,7 @@
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, targetFilter);
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, pattern);
 				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME, servletName);
-				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, factory.getFilter());
+				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT, getFilter(serviceReference));
 				props.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
 				props.put(Const.EQUINOX_LEGACY_TCCL_PROP, Thread.currentThread().getContextClassLoader());
 				fillInitParams(props, initparams, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
@@ -739,7 +772,7 @@
 				// check that init got called and did not throw an exception
 				legacyServlet.checkForError();
 
-				objectRegistration = new HttpServiceObjectRegistration(fullAlias, registration, factory, bundle);
+				objectRegistration = new HttpServiceObjectRegistration(fullAlias, registration, bundle);
 
 				Set<HttpServiceObjectRegistration> objectRegistrations = bundleRegistrations.get(bundle);
 				if (objectRegistrations == null) {
@@ -760,7 +793,6 @@
 				if (objectRegistration == null || !legacyMappings.containsKey(objectRegistration.serviceKey)) {
 					// something bad happened above (likely going to throw a runtime exception)
 					// need to clean up the factory reference
-					decrementFactoryUseCount(factory);
 					if (registration != null) {
 						registration.unregister();
 					}
@@ -769,8 +801,9 @@
 		}
 	}
 
-	private String getFullAlias(String alias, HttpContextHelperFactory factory) {
-		AtomicReference<ContextController> controllerRef = contextServiceTracker.getService(factory.getServiceReference());
+	private String getFullAlias(String alias, ServiceReference<? extends ServletContextHelper> serviceReference) {
+		@SuppressWarnings("unchecked")
+		AtomicReference<ContextController> controllerRef = contextServiceTracker.getService((ServiceReference<ServletContextHelper>)serviceReference);
 		if (controllerRef != null) {
 			ContextController controller = controllerRef.get();
 			if (controller != null) {
@@ -802,7 +835,6 @@
 			} catch (IllegalStateException e) {
 				// ignore; already unregistered
 			}
-			decrementFactoryUseCount(objectRegistration.factory);
 			legacyMappings.remove(aliasCustomization);
 
 		}
@@ -824,7 +856,6 @@
 			} catch (IllegalStateException e) {
 				// ignore; already unregistered
 			}
-			decrementFactoryUseCount(objectRegistration.factory);
 			legacyMappings.remove(filter);
 		}
 	}
@@ -840,43 +871,12 @@
 					} catch (IllegalStateException e) {
 						// ignore; already unregistered
 					}
-					decrementFactoryUseCount(objectRegistration.factory);
 					legacyMappings.remove(objectRegistration.serviceKey);
 				}
 			}
 		}
 	}
 
-	private HttpContextHelperFactory getOrRegisterHttpContextHelperFactory(Bundle initiatingBundle, HttpContext httpContext) {
-		if (httpContext == null) {
-			throw new NullPointerException("A null HttpContext is not allowed."); //$NON-NLS-1$
-		}
-		synchronized (httpContextHelperFactories) {
-			HttpContextHelperFactory factory = httpContextHelperFactories.get(httpContext);
-			if (factory == null) {
-				factory = new HttpContextHelperFactory(httpContext);
-				Dictionary<String, Object> props = new Hashtable<String, Object>();
-				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, httpContext.getClass().getName().replaceAll("[^a-zA-Z_0-9\\-]", "_") + "-" + generateLegacyId()); //$NON-NLS-1$
-				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/"); //$NON-NLS-1$
-				props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET, targetFilter);
-				props.put(Const.EQUINOX_LEGACY_CONTEXT_HELPER, Boolean.TRUE);
-				props.put(Const.EQUINOX_LEGACY_HTTP_CONTEXT_INITIATING_ID, initiatingBundle.getBundleId());
-				factory.setRegistration(consumingContext.registerService(ServletContextHelper.class, factory, props));
-				httpContextHelperFactories.put(httpContext, factory);
-			}
-			factory.incrementUseCount();
-			return factory;
-		}
-	}
-
-	private void decrementFactoryUseCount(HttpContextHelperFactory factory) {
-		synchronized (httpContextHelperFactories) {
-			if (factory.decrementUseCount() == 0) {
-				httpContextHelperFactories.remove(factory.getHttpContext());
-			}
-		}
-	}
-
 	private static org.osgi.framework.Filter createResourceFilter(BundleContext context) {
 		StringBuilder sb = new StringBuilder();
 
@@ -980,6 +980,17 @@
 		return resourceServiceFilter;
 	}
 
+	public void recordFailedErrorPageDTO(
+		ServiceReference<?> serviceReference,
+		FailedErrorPageDTO failedErrorPageDTO) {
+
+		if (failedErrorPageDTOs.containsKey(serviceReference)) {
+			return;
+		}
+
+		failedErrorPageDTOs.put(serviceReference, failedErrorPageDTO);
+	}
+
 	public void recordFailedFilterDTO(
 		ServiceReference<Filter> serviceReference,
 		FailedFilterDTO failedFilterDTO) {
@@ -1003,7 +1014,7 @@
 	}
 
 	public void recordFailedResourceDTO(
-		ServiceReference<Object> serviceReference, FailedResourceDTO failedResourceDTO) {
+		ServiceReference<?> serviceReference, FailedResourceDTO failedResourceDTO) {
 
 		if (failedResourceDTOs.containsKey(serviceReference)) {
 			return;
@@ -1012,30 +1023,30 @@
 		failedResourceDTOs.put(serviceReference, failedResourceDTO);
 	}
 
-	private void recordFailedServletContextDTO(
-		ServiceReference<ServletContextHelper> serviceReference, String contextName,
-		String contextPath, int failureReason) {
+	public void recordFailedServletContextDTO(
+		ServiceReference<ServletContextHelper> serviceReference, long shadowingServiceId, int failureReason) {
 
-		FailedServletContextDTO failedServletContextDTO = new FailedServletContextDTO();
+		ExtendedFailedServletContextDTO failedServletContextDTO = new ExtendedFailedServletContextDTO();
 
 		failedServletContextDTO.attributes = Collections.emptyMap();
-		failedServletContextDTO.contextPath = contextPath;
-		failedServletContextDTO.errorPageDTOs = new ErrorPageDTO[0];
+		failedServletContextDTO.contextPath = String.valueOf(serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH));
+		failedServletContextDTO.errorPageDTOs = new ExtendedErrorPageDTO[0];
 		failedServletContextDTO.failureReason = failureReason;
 		failedServletContextDTO.filterDTOs = new FilterDTO[0];
 		failedServletContextDTO.initParams = ServiceProperties.parseInitParams(
 			serviceReference, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_INIT_PARAM_PREFIX);
 		failedServletContextDTO.listenerDTOs = new ListenerDTO[0];
-		failedServletContextDTO.name = contextName;
+		failedServletContextDTO.name = String.valueOf(serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME));
 		failedServletContextDTO.resourceDTOs = new ResourceDTO[0];
 		failedServletContextDTO.serviceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
 		failedServletContextDTO.servletDTOs = new ServletDTO[0];
+		failedServletContextDTO.shadowingServiceId = shadowingServiceId;
 
 		failedServletContextDTOs.put(serviceReference, failedServletContextDTO);
 	}
 
 	public void recordFailedServletDTO(
-		ServiceReference<Servlet> serviceReference,
+		ServiceReference<?> serviceReference,
 		ExtendedFailedServletDTO failedServletDTO) {
 
 		if (failedServletDTOs.containsKey(serviceReference)) {
@@ -1045,6 +1056,12 @@
 		failedServletDTOs.put(serviceReference, failedServletDTO);
 	}
 
+	public void removeFailedErrorPageDTO(
+		ServiceReference<Servlet> serviceReference) {
+
+		failedErrorPageDTOs.remove(serviceReference);
+	}
+
 	public void removeFailedFilterDTO(
 		ServiceReference<Filter> serviceReference) {
 
@@ -1063,13 +1080,13 @@
 		failedResourceDTOs.remove(serviceReference);
 	}
 
-	public void removeFailedServletDTOs(
+	public void removeFailedServletDTO(
 		ServiceReference<Servlet> serviceReference) {
 
 		failedServletDTOs.remove(serviceReference);
 	}
 
-	public void fireSessionIdChanged(String oldSessionId) {
+	public synchronized void fireSessionIdChanged(String oldSessionId) {
 		for (ContextController contextController : controllerMap.values()) {
 			contextController.fireSessionIdChanged(oldSessionId);
 		}
@@ -1081,7 +1098,7 @@
 
 	private Map<String, Object> attributes;
 	private final String targetFilter;
-	private final ServiceRegistration<ServletContextHelper> defaultContextReg;
+	private final ServiceRegistration<?> defaultContextReg;
 	private ServletContext parentServletContext;
 
 	private BundleContext trackingContext;
@@ -1093,32 +1110,30 @@
 	private final org.osgi.framework.Filter listenerServiceFilter;
 
 	// BEGIN of old HttpService support
-	private Map<HttpContext, HttpContextHelperFactory> httpContextHelperFactories =
-		Collections.synchronizedMap(new HashMap<HttpContext, HttpContextHelperFactory>());
-	private Map<Object, HttpServiceObjectRegistration> legacyMappings =
+	private final Map<Object, HttpServiceObjectRegistration> legacyMappings =
 		Collections.synchronizedMap(new HashMap<Object, HttpServiceObjectRegistration>());
-	private Map<Bundle, Set<HttpServiceObjectRegistration>> bundleRegistrations =
+	private final Map<Bundle, Set<HttpServiceObjectRegistration>> bundleRegistrations =
 		new HashMap<Bundle, Set<HttpServiceObjectRegistration>>();
-	private Map<Bundle, Map<String, String>> bundleAliasCustomizations = new HashMap<Bundle, Map<String,String>>();
+	private final Map<Bundle, Map<String, String>> bundleAliasCustomizations = new HashMap<Bundle, Map<String,String>>();
 	// END of old HttpService support
 
-	private ConcurrentMap<ServiceReference<ServletContextHelper>, ContextController> controllerMap =
-		new ConcurrentHashMap<ServiceReference<ServletContextHelper>, ContextController>();
+	private final ConcurrentMap<ServiceReference<ServletContextHelper>, ContextController> controllerMap =
+		new ConcurrentSkipListMap<ServiceReference<ServletContextHelper>, ContextController>(Collections.reverseOrder());
 
+	private final ConcurrentMap<ServiceReference<?>, FailedErrorPageDTO> failedErrorPageDTOs =
+		new ConcurrentHashMap<ServiceReference<?>, FailedErrorPageDTO>();
 	private final ConcurrentMap<ServiceReference<Filter>, FailedFilterDTO> failedFilterDTOs =
 		new ConcurrentHashMap<ServiceReference<Filter>, FailedFilterDTO>();
 	private final ConcurrentMap<ServiceReference<EventListener>, FailedListenerDTO> failedListenerDTOs =
 		new ConcurrentHashMap<ServiceReference<EventListener>, FailedListenerDTO>();
-	private final ConcurrentMap<ServiceReference<Object>, FailedResourceDTO> failedResourceDTOs =
-		new ConcurrentHashMap<ServiceReference<Object>, FailedResourceDTO>();
-	private final ConcurrentMap<ServiceReference<ServletContextHelper>, FailedServletContextDTO> failedServletContextDTOs =
-		new ConcurrentHashMap<ServiceReference<ServletContextHelper>, FailedServletContextDTO>();
-	private final ConcurrentMap<ServiceReference<Servlet>, ExtendedFailedServletDTO> failedServletDTOs =
-		new ConcurrentHashMap<ServiceReference<Servlet>, ExtendedFailedServletDTO>();
+	private final ConcurrentMap<ServiceReference<?>, FailedResourceDTO> failedResourceDTOs =
+		new ConcurrentHashMap<ServiceReference<?>, FailedResourceDTO>();
+	private final ConcurrentMap<ServiceReference<ServletContextHelper>, ExtendedFailedServletContextDTO> failedServletContextDTOs =
+		new ConcurrentHashMap<ServiceReference<ServletContextHelper>, ExtendedFailedServletContextDTO>();
+	private final ConcurrentMap<ServiceReference<?>, ExtendedFailedServletDTO> failedServletDTOs =
+		new ConcurrentHashMap<ServiceReference<?>, ExtendedFailedServletDTO>();
 
-	private AtomicLong legacyIdGenerator = new AtomicLong(0);
-
-	private Set<Object> registeredObjects = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
+	private final Set<Object> registeredObjects = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>());
 
 	private ServiceTracker<ServletContextHelper, AtomicReference<ContextController>> contextServiceTracker;
 	private ServiceTracker<ContextPathCustomizer, ContextPathCustomizer> contextPathAdaptorTracker;
@@ -1126,29 +1141,6 @@
 	private HttpSessionTracker httpSessionTracker;
 	private final ServiceRegistration<HttpSessionInvalidator> invalidatorReg;
 
-	static class DefaultServletContextHelperFactory implements ServiceFactory<ServletContextHelper> {
-		@Override
-		public ServletContextHelper getService(
-			Bundle bundle,
-			ServiceRegistration<ServletContextHelper> registration) {
-			return new DefaultServletContextHelper(bundle);
-		}
-
-		@Override
-		public void ungetService(
-			Bundle bundle,
-			ServiceRegistration<ServletContextHelper> registration,
-			ServletContextHelper service) {
-			// do nothing
-		}
-	}
-
-	static class DefaultServletContextHelper extends ServletContextHelper {
-		public DefaultServletContextHelper(Bundle b) {
-			super(b);
-		}
-	}
-
 	static class LegacyServiceObject {
 		final AtomicReference<Exception> error = new AtomicReference<Exception>(new ServletException("The init() method was never called.")); //$NON-NLS-1$
 		public void checkForError() {
@@ -1333,4 +1325,16 @@
 		}
 	}
 
+	public boolean failedResourceDTO(ServiceReference<?> serviceReference) {
+		return failedResourceDTOs.containsKey(serviceReference);
+	}
+
+	public boolean failedServletDTO(ServiceReference<?> serviceReference) {
+		return failedServletDTOs.containsKey(serviceReference);
+	}
+
+	public boolean failedErrorPageDTO(ServiceReference<?> serviceReference) {
+		return failedErrorPageDTOs.containsKey(serviceReference);
+	}
+
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
index 6d87145..16f115b 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/ContextController.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 Raymond Augé and others.
+ * Copyright (c) 2016, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -21,12 +21,16 @@
 import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 import javax.servlet.*;
 import javax.servlet.Filter;
 import javax.servlet.http.*;
+import org.eclipse.equinox.http.servlet.dto.ExtendedFailedServletDTO;
 import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl;
 import org.eclipse.equinox.http.servlet.internal.customizer.*;
+import org.eclipse.equinox.http.servlet.internal.dto.ExtendedErrorPageDTO;
+import org.eclipse.equinox.http.servlet.internal.dto.ExtendedErrorPageDTO.ErrorCodeType;
 import org.eclipse.equinox.http.servlet.internal.error.*;
 import org.eclipse.equinox.http.servlet.internal.registration.*;
 import org.eclipse.equinox.http.servlet.internal.registration.FilterRegistration;
@@ -50,20 +54,28 @@
 		final Bundle bundle;
 		final long serviceId;
 		final int serviceRanking;
+		final ClassLoader legacyTCCL;
 		public ServiceHolder(ServiceObjects<S> serviceObjects) {
 			this.serviceObjects = serviceObjects;
 			this.bundle = serviceObjects.getServiceReference().getBundle();
 			this.service = serviceObjects.getService();
-			this.serviceId = (Long) serviceObjects.getServiceReference().getProperty(Constants.SERVICE_ID);
+			this.legacyTCCL = (ClassLoader)serviceObjects.getServiceReference().getProperty(Const.EQUINOX_LEGACY_TCCL_PROP);
+			Long serviceIdProp = (Long)serviceObjects.getServiceReference().getProperty(Constants.SERVICE_ID);
+			if (legacyTCCL != null) {
+				// this is a legacy registration; use a negative id for the DTO
+				serviceIdProp = -serviceIdProp;
+			}
+			this.serviceId = serviceIdProp;
 			Integer rankProp = (Integer) serviceObjects.getServiceReference().getProperty(Constants.SERVICE_RANKING);
 			this.serviceRanking = rankProp == null ? 0 : rankProp.intValue();
 		}
-		public ServiceHolder(S service, Bundle bundle, long serviceId, int serviceRanking) {
+		public ServiceHolder(S service, Bundle bundle, long serviceId, int serviceRanking, ClassLoader legacyTCCL) {
 			this.service = service;
 			this.bundle = bundle;
 			this.serviceObjects = null;
 			this.serviceId = serviceId;
 			this.serviceRanking = serviceRanking;
+			this.legacyTCCL = legacyTCCL;
 		}
 		public S get() {
 			return service;
@@ -72,6 +84,11 @@
 		public Bundle getBundle() {
 			return bundle;
 		}
+
+		public ClassLoader getLegacyTCCL() {
+			return legacyTCCL;
+		}
+
 		public void release() {
 			if (serviceObjects != null && service != null) {
 				try {
@@ -111,40 +128,21 @@
 
 	public ContextController(
 		BundleContext trackingContextParam, BundleContext consumingContext,
-		ServiceReference<ServletContextHelper> servletContextHelperRef,
-		ProxyContext proxyContext, HttpServiceRuntimeImpl httpServiceRuntime,
-		String contextName, String contextPath, HttpSessionTracker httpSessionTracker) {
-
-		validate(contextName, contextPath);
-
-		this.servletContextHelperRef = servletContextHelperRef;
-
-		long serviceId = (Long)servletContextHelperRef.getProperty(Constants.SERVICE_ID);
-
-		StringBuilder filterBuilder = new StringBuilder();
-		filterBuilder.append('(');
-		filterBuilder.append(Constants.SERVICE_ID);
-		filterBuilder.append('=');
-		filterBuilder.append(serviceId);
-		filterBuilder.append(')');
-		this.servletContextHelperRefFilter = filterBuilder.toString();
-		this.proxyContext = proxyContext;
-		this.httpServiceRuntime = httpServiceRuntime;
-		this.contextName = contextName;
-
-		if (contextPath.equals(Const.SLASH)) {
-			contextPath = Const.BLANK;
-		}
-
-		this.contextPath = contextPath;
-		this.contextServiceId = serviceId;
-
-		this.initParams = ServiceProperties.parseInitParams(
-			servletContextHelperRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_INIT_PARAM_PREFIX, proxyContext.getServletContext());
+		ServiceReference<ServletContextHelper> serviceReference,
+		ServletContext parentServletContext, HttpServiceRuntimeImpl httpServiceRuntime) {
 
 		this.trackingContext = trackingContextParam;
 		this.consumingContext = consumingContext;
-		this.httpSessionTracker = httpSessionTracker;
+		this.serviceReference = serviceReference;
+		this.httpServiceRuntime = httpServiceRuntime;
+		this.contextName = validateName();
+		this.contextPath = validatePath();
+		this.proxyContext = new ProxyContext(contextName, parentServletContext);
+		this.contextServiceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
+		this.servletContextHelperRefFilter = createFilter(contextServiceId);
+
+		this.initParams = ServiceProperties.parseInitParams(
+			serviceReference, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_INIT_PARAM_PREFIX, parentServletContext);
 
 		listenerServiceTracker = new ServiceTracker<EventListener, AtomicReference<ListenerRegistration>>(
 			trackingContext, httpServiceRuntime.getListenerFilter(),
@@ -184,12 +182,13 @@
 		boolean addedRegisteredObject = false;
 		try {
 			if (filter == null) {
-				throw new IllegalArgumentException("Filter cannot be null");
+				throw new IllegalArgumentException("Filter cannot be null"); //$NON-NLS-1$
 			}
 			addedRegisteredObject = httpServiceRuntime.getRegisteredObjects().add(filter);
-			if (addedRegisteredObject) {
-				registration = doAddFilterRegistration(filterHolder, filterRef);
+			if (!addedRegisteredObject) {
+				throw new HttpWhiteboardFailureException("Multiple registration of instance detected. Prototype scope is recommended: " + filterRef, DTOConstants.FAILURE_REASON_SERVICE_IN_USE); //$NON-NLS-1$
 			}
+			registration = doAddFilterRegistration(filterHolder, filterRef);
 		} finally {
 			if (registration == null) {
 				filterHolder.release();
@@ -203,21 +202,13 @@
 
 	private FilterRegistration doAddFilterRegistration(ServiceHolder<Filter> filterHolder, ServiceReference<Filter> filterRef) throws ServletException {
 
-		ClassLoader legacyTCCL = (ClassLoader)filterRef.getProperty(Const.EQUINOX_LEGACY_TCCL_PROP);
 		boolean asyncSupported = ServiceProperties.parseBoolean(
 			filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED);
 
 		List<String> dispatcherList = StringPlus.from(
 			filterRef.getProperty(
 				HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER));
-		String[] dispatchers = dispatcherList.toArray(
-			new String[dispatcherList.size()]);
-		Long serviceId = (Long)filterRef.getProperty(
-			Constants.SERVICE_ID);
-		if (legacyTCCL != null) {
-			// this is a legacy registration; use a negative id for the DTO
-			serviceId = -serviceId;
-		}
+		String[] dispatchers = dispatcherList.toArray(new String[0]);
 		Integer filterPriority = (Integer)filterRef.getProperty(
 			Constants.SERVICE_RANKING);
 		if (filterPriority == null) {
@@ -228,15 +219,15 @@
 		List<String> patternList = StringPlus.from(
 			filterRef.getProperty(
 				HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN));
-		String[] patterns = patternList.toArray(new String[patternList.size()]);
+		String[] patterns = patternList.toArray(new String[0]);
 		List<String> regexList = StringPlus.from(
 			filterRef.getProperty(
 				HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_REGEX));
-		String[] regexs = regexList.toArray(new String[regexList.size()]);
+		String[] regexs = regexList.toArray(new String[0]);
 		List<String> servletList = StringPlus.from(
 			filterRef.getProperty(
 				HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET));
-		String[] servletNames = servletList.toArray(new String[servletList.size()]);
+		String[] servletNames = servletList.toArray(new String[0]);
 
 		String name = ServiceProperties.parseName(filterRef.getProperty(
 			HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME), filterHolder.get());
@@ -247,8 +238,8 @@
 			((regexs == null) || (regexs.length == 0)) &&
 			((servletNames == null) || (servletNames.length == 0))) {
 
-			throw new IllegalArgumentException(
-				"Patterns, regex or servletNames must contain a value.");
+			throw new HttpWhiteboardFailureException(
+				"Patterns, regex or servletNames must contain a value.", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
 		}
 
 		if (patterns != null) {
@@ -257,8 +248,14 @@
 			}
 		}
 
+		if (regexs != null) {
+			for (String regex : regexs) {
+				checkRegex(regex);
+			}
+		}
+
 		if (filter == null) {
-			throw new IllegalArgumentException("Filter cannot be null");
+			throw new HttpWhiteboardFailureException("Filter cannot be null", DTOConstants.FAILURE_REASON_SERVICE_NOT_GETTABLE); //$NON-NLS-1$
 		}
 
 		if (name == null) {
@@ -281,7 +278,7 @@
 		filterDTO.name = name;
 		filterDTO.patterns = sort(patterns);
 		filterDTO.regexs = regexs;
-		filterDTO.serviceId = serviceId;
+		filterDTO.serviceId = filterHolder.serviceId;
 		filterDTO.servletContextId = contextServiceId;
 		filterDTO.servletNames = sort(servletNames);
 
@@ -291,7 +288,7 @@
 		ServletContext servletContext = createServletContext(
 			filterHolder.getBundle(), curServletContextHelper);
 		FilterRegistration newRegistration  = new FilterRegistration(
-			filterHolder, filterDTO, filterPriority, this, legacyTCCL);
+			filterHolder, filterDTO, filterPriority, this);
 		FilterConfig filterConfig = new FilterConfigImpl(
 			name, filterInitParams, servletContext);
 
@@ -310,7 +307,7 @@
 		ListenerRegistration registration = null;
 		try {
 			if (listener == null) {
-				throw new IllegalArgumentException("EventListener cannot be null");
+				throw new IllegalArgumentException("EventListener cannot be null"); //$NON-NLS-1$
 			}
 			registration = doAddListenerRegistration(listenerHolder, listenerRef);
 		} finally {
@@ -325,25 +322,24 @@
 		ServiceHolder<EventListener> listenerHolder,
 		ServiceReference<EventListener> listenerRef) throws ServletException {
 
-
 		EventListener eventListener = listenerHolder.get();
 		List<Class<? extends EventListener>> classes = getListenerClasses(listenerRef);
 
 		if (classes.isEmpty()) {
 			throw new IllegalArgumentException(
-				"EventListener does not implement a supported type.");
+				"EventListener does not implement a supported type."); //$NON-NLS-1$
 		}
 
 		for (ListenerRegistration listenerRegistration : listenerRegistrations) {
 			if (listenerRegistration.getT().equals(eventListener)) {
 				throw new ServletException(
-					"EventListener has already been registered.");
+					"EventListener has already been registered."); //$NON-NLS-1$
 			}
 		}
 
 		ListenerDTO listenerDTO = new ListenerDTO();
 
-		listenerDTO.serviceId = (Long) listenerRef.getProperty(Constants.SERVICE_ID);
+		listenerDTO.serviceId = listenerHolder.serviceId;
 		listenerDTO.servletContextId = contextServiceId;
 		listenerDTO.types = asStringArray(classes);
 
@@ -377,24 +373,29 @@
 		ClassLoader legacyTCCL = (ClassLoader)resourceRef.getProperty(Const.EQUINOX_LEGACY_TCCL_PROP);
 		Integer rankProp = (Integer) resourceRef.getProperty(Constants.SERVICE_RANKING);
 		int serviceRanking = rankProp == null ? 0 : rankProp.intValue();
-		List<String> patternList = StringPlus.from(
-			resourceRef.getProperty(
-				HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN));
-		String[] patterns = patternList.toArray(new String[patternList.size()]);
+		Object patternObj = resourceRef.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN);
+		if (!(patternObj instanceof String) &&
+			!(patternObj instanceof String[]) &&
+			!(patternObj instanceof Collection)) {
+			throw new HttpWhiteboardFailureException("Expect pattern to be String+ (String | String[] | Collection<String>)", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
+		}
+		List<String> patternList = StringPlus.from(patternObj);
+		String[] patterns = patternList.toArray(new String[0]);
 		Long serviceId = (Long)resourceRef.getProperty(
 			Constants.SERVICE_ID);
 		if (legacyTCCL != null) {
 			// this is a legacy registration; use a negative id for the DTO
 			serviceId = -serviceId;
 		}
-		String prefix = (String)resourceRef.getProperty(
+		Object prefixObj = resourceRef.getProperty(
 			HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX);
 
-		checkPrefix(prefix);
+		checkPrefix(prefixObj);
+		String prefix = (String)prefixObj;
 
 		if ((patterns == null) || (patterns.length < 1)) {
 			throw new IllegalArgumentException(
-				"Patterns must contain a value.");
+				"Patterns must contain a value."); //$NON-NLS-1$
 		}
 
 		for (String pattern : patterns) {
@@ -417,8 +418,8 @@
 		ServletContext servletContext = createServletContext(
 			bundle, curServletContextHelper);
 		ResourceRegistration resourceRegistration = new ResourceRegistration(
-			new ServiceHolder<Servlet>(servlet, bundle, serviceId, serviceRanking),
-			resourceDTO, curServletContextHelper, this, legacyTCCL);
+			resourceRef, new ServiceHolder<Servlet>(servlet, bundle, serviceId, serviceRanking, legacyTCCL),
+			resourceDTO, curServletContextHelper, this);
 		ServletConfig servletConfig = new ServletConfigImpl(
 			resourceRegistration.getName(), new HashMap<String, String>(),
 			servletContext);
@@ -432,13 +433,14 @@
 			return Throw.unchecked(t);
 		}
 
+		recordEndpointShadowing(resourceRegistration);
+
 		endpointRegistrations.add(resourceRegistration);
 
 		return resourceRegistration;
 	}
 
-	public ServletRegistration addServletRegistration(ServiceReference<Servlet> servletRef) throws ServletException {
-
+	public ServletRegistration addServletRegistration(ServiceReference<Servlet> servletRef) {
 		checkShutdown();
 
 		ServiceHolder<Servlet> servletHolder = new ServiceHolder<Servlet>(consumingContext.getServiceObjects(servletRef));
@@ -447,12 +449,13 @@
 		boolean addedRegisteredObject = false;
 		try {
 			if (servlet == null) {
-				throw new IllegalArgumentException("Servlet cannot be null");
+				throw new IllegalArgumentException("Servlet cannot be null"); //$NON-NLS-1$
 			}
 			addedRegisteredObject = httpServiceRuntime.getRegisteredObjects().add(servlet);
-			if (addedRegisteredObject) {
-				registration = doAddServletRegistration(servletHolder, servletRef);
+			if (!addedRegisteredObject) {
+				throw new HttpWhiteboardFailureException("Multiple registration of instance detected. Prototype scope is recommended: " + servletRef, DTOConstants.FAILURE_REASON_SERVICE_IN_USE); //$NON-NLS-1$
 			}
+			registration = doAddServletRegistration(servletHolder, servletRef);
 		} finally {
 			if (registration == null) {
 				// Always attempt to release here; even though destroy() may have been called
@@ -468,24 +471,17 @@
 		return registration;
 	}
 
-	private ServletRegistration doAddServletRegistration(ServiceHolder<Servlet> servletHolder, ServiceReference<Servlet> servletRef) throws ServletException {
-
+	private ServletRegistration doAddServletRegistration(ServiceHolder<Servlet> servletHolder, ServiceReference<Servlet> servletRef) {
 		boolean asyncSupported = ServiceProperties.parseBoolean(
 			servletRef,	HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED);
-		ClassLoader legacyTCCL = (ClassLoader)servletRef.getProperty(Const.EQUINOX_LEGACY_TCCL_PROP);
 		List<String> errorPageList = StringPlus.from(
 			servletRef.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE));
-		String[] errorPages = errorPageList.toArray(new String[errorPageList.size()]);
+		String[] errorPages = errorPageList.toArray(new String[0]);
 		Map<String, String> servletInitParams = ServiceProperties.parseInitParams(
 			servletRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
 		List<String> patternList = StringPlus.from(
 			servletRef.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN));
-		String[] patterns = patternList.toArray(new String[patternList.size()]);
-		Long serviceId = (Long)servletRef.getProperty(Constants.SERVICE_ID);
-		if (legacyTCCL != null) {
-			// this is a legacy registration; use a negative id for the DTO
-			serviceId = -serviceId;
-		}
+		String[] patterns = patternList.toArray(new String[0]);
 		String servletNameFromProperties = (String)servletRef.getProperty(
 			HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
 		String generatedServletName = ServiceProperties.parseName(
@@ -535,14 +531,14 @@
 		servletDTO.multipartMaxRequestSize = (multipartMaxRequestSize != null ? multipartMaxRequestSize : -1L);
 		servletDTO.name = generatedServletName;
 		servletDTO.patterns = sort(patterns);
-		servletDTO.serviceId = serviceId;
+		servletDTO.serviceId = servletHolder.serviceId;
 		servletDTO.servletContextId = contextServiceId;
 		servletDTO.servletInfo = servletHolder.get().getServletInfo();
 
-		ErrorPageDTO errorPageDTO = null;
+		ExtendedErrorPageDTO errorPageDTO = null;
 
 		if ((errorPages != null) && (errorPages.length > 0)) {
-			errorPageDTO = new ErrorPageDTO();
+			errorPageDTO = new ExtendedErrorPageDTO();
 
 			errorPageDTO.asyncSupported = asyncSupported;
 			List<String> exceptions = new ArrayList<String>();
@@ -553,16 +549,21 @@
 			for(String errorPage : errorPages) {
 				try {
 					if ("4xx".equals(errorPage)) { //$NON-NLS-1$
+						errorPageDTO.erroCodeType = ErrorCodeType.RANGE_4XX;
 						for (long code = 400; code < 500; code++) {
 							errorCodeSet.add(code);
 						}
 					} else if ("5xx".equals(errorPage)) { //$NON-NLS-1$
+						errorPageDTO.erroCodeType = ErrorCodeType.RANGE_5XX;
 						for (long code = 500; code < 600; code++) {
 							errorCodeSet.add(code);
 						}
-					} else {
+					} else if (errorPage.length() == 3) {
+						errorPageDTO.erroCodeType = ErrorCodeType.SPECIFIC;
 						long code = Long.parseLong(errorPage);
 						errorCodeSet.add(code);
+					} else {
+						exceptions.add(errorPage);
 					}
 				}
 				catch (NumberFormatException nfe) {
@@ -577,10 +578,10 @@
 				i++;
 			}
 			errorPageDTO.errorCodes = errorCodes;
-			errorPageDTO.exceptions = exceptions.toArray(new String[exceptions.size()]);
+			errorPageDTO.exceptions = exceptions.toArray(new String[0]);
 			errorPageDTO.initParams = servletInitParams;
 			errorPageDTO.name = generatedServletName;
-			errorPageDTO.serviceId = serviceId;
+			errorPageDTO.serviceId = servletHolder.serviceId;
 			errorPageDTO.servletContextId = contextServiceId;
 			errorPageDTO.servletInfo = servletHolder.get().getServletInfo();
 		}
@@ -592,7 +593,7 @@
 			servletHolder.getBundle(), curServletContextHelper);
 		ServletRegistration servletRegistration = new ServletRegistration(
 			servletHolder, servletDTO, errorPageDTO, curServletContextHelper, this,
-			servletContext, legacyTCCL);
+			servletContext);
 		ServletConfig servletConfig = new ServletConfigImpl(
 			generatedServletName, servletInitParams, servletContext);
 
@@ -605,11 +606,105 @@
 			return Throw.unchecked(t);
 		}
 
+		recordEndpointShadowing(servletRegistration);
+		recordErrorPageShadowing(servletRegistration, errorPageDTO);
+
 		endpointRegistrations.add(servletRegistration);
 
 		return servletRegistration;
 	}
 
+	private void recordEndpointShadowing(EndpointRegistration<?> newRegistration) {
+		Set<EndpointRegistration<?>> shadowedRegs = new HashSet<EndpointRegistration<?>>();
+		for (EndpointRegistration<?> existingRegistration : endpointRegistrations) {
+			for (String newPattern : newRegistration.getPatterns()) {
+				for (String existingPattern : existingRegistration.getPatterns()) {
+					if (newPattern.equals(existingPattern)) {
+						// the new reg is shadowing an existing reg
+						if (newRegistration.compareTo(existingRegistration) < 0) {
+							shadowedRegs.add(existingRegistration);
+						}
+						// the new reg is shadowed by existing reg
+						else {
+							shadowedRegs.add(newRegistration);
+						}
+						// notice that we keep checking all the existing regs. more than one could be shadowed because reg's multi patterns
+					}
+				}
+			}
+		}
+		for (EndpointRegistration<?> shadowedReg : shadowedRegs) {
+			if (shadowedReg instanceof ServletRegistration) {
+				recordFailedServletDTO(shadowedReg.getServiceReference(), (ExtendedServletDTO)shadowedReg.getD(), DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+			}
+			else {
+				recordFailedResourceDTO(shadowedReg.getServiceReference(), (ResourceDTO)shadowedReg.getD(), DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+			}
+		}
+	}
+
+	private void recordErrorPageShadowing(ServletRegistration servletRegistration, ExtendedErrorPageDTO newErrorPageDTO) {
+		if (newErrorPageDTO == null) {
+			return;
+		}
+		Set<ServletRegistration> shadowedEPs = new HashSet<ServletRegistration>();
+		for (EndpointRegistration<?> existingRegistration : endpointRegistrations) {
+			if (!(existingRegistration instanceof ServletRegistration)) {
+				continue;
+			}
+			ServletRegistration existingSRegistration = (ServletRegistration)existingRegistration;
+			ExtendedErrorPageDTO existingErrorPageDTO = existingSRegistration.getErrorPageDTO();
+			if ((existingErrorPageDTO == null) ||
+				(((existingErrorPageDTO.erroCodeType == ErrorCodeType.RANGE_4XX) || (existingErrorPageDTO.erroCodeType == ErrorCodeType.RANGE_5XX)) && (newErrorPageDTO.erroCodeType == ErrorCodeType.SPECIFIC))) {
+				continue;
+			}
+			if (((existingErrorPageDTO.erroCodeType == ErrorCodeType.RANGE_4XX) && (newErrorPageDTO.erroCodeType == ErrorCodeType.RANGE_4XX)) ||
+				((existingErrorPageDTO.erroCodeType == ErrorCodeType.RANGE_5XX) && (newErrorPageDTO.erroCodeType == ErrorCodeType.RANGE_5XX))) {
+				if (servletRegistration.compareTo(existingSRegistration) < 0) {
+					shadowedEPs.add(existingSRegistration);
+				}
+				// the new reg is shadowed by existing reg
+				else {
+					shadowedEPs.add(servletRegistration);
+				}
+				continue;
+			}
+			for (long newErrorCode : newErrorPageDTO.errorCodes) {
+				for (long existingCode : existingErrorPageDTO.errorCodes) {
+					if (newErrorCode == existingCode) {
+						// the new reg is shadowing an existing reg
+						if (servletRegistration.compareTo(existingSRegistration) < 0) {
+							shadowedEPs.add(existingSRegistration);
+						}
+						// the new reg is shadowed by existing reg
+						else {
+							shadowedEPs.add(servletRegistration);
+						}
+						// notice that we keep checking all the existing regs. more than one could be shadowed because reg's multi patterns
+					}
+				}
+			}
+			for (String newException : newErrorPageDTO.exceptions) {
+				for (String existingException : existingErrorPageDTO.exceptions) {
+					if (newException.equals(existingException)) {
+						// the new reg is shadowing an existing reg
+						if (servletRegistration.compareTo(existingSRegistration) < 0) {
+							shadowedEPs.add(existingSRegistration);
+						}
+						// the new reg is shadowed by existing reg
+						else {
+							shadowedEPs.add(servletRegistration);
+						}
+						// notice that we keep checking all the existing regs. more than one could be shadowed because reg's multi patterns
+					}
+				}
+			}
+		}
+		for (ServletRegistration shadowedReg : shadowedEPs) {
+			recordFailedErrorPageDTO(shadowedReg.getServiceReference(), shadowedReg.getErrorPageDTO(), DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+		}
+	}
+
 	public void destroy() {
 		flushActiveSessions();
 		resourceServiceTracker.close();
@@ -920,16 +1015,13 @@
 			targetFilter = FrameworkUtil.createFilter(contextSelector);
 		}
 		catch (InvalidSyntaxException ise) {
-			throw new IllegalArgumentException(ise);
+			throw new HttpWhiteboardFailureException(ise.getMessage(), DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
 		}
 
 		return matches(targetFilter);
 	}
 
 	private boolean visibleContextHelper(ServiceReference<?> whiteBoardService) {
-		if (consumingContext.getBundle().equals(servletContextHelperRef.getBundle())) {
-			return true;
-		}
 		try {
 			if (whiteBoardService.getBundle().getBundleContext().getAllServiceReferences(ServletContextHelper.class.getName(), servletContextHelperRefFilter) != null) {
 				return true;
@@ -942,7 +1034,7 @@
 	}
 
 	public boolean matches(org.osgi.framework.Filter targetFilter) {
-		return targetFilter.match(servletContextHelperRef);
+		return targetFilter.match(serviceReference);
 	}
 
 	@Override
@@ -986,17 +1078,18 @@
 			return;
 		}
 
-		FilterDTO[] filterDTOs =
-			new FilterDTO[matchedFilterRegistrations.size()];
+		List<FilterDTO> filterDTOs = new ArrayList<FilterDTO>();
 
-		for (int i = 0; i < filterDTOs.length ; i++) {
+		for (int i = 0; i < matchedFilterRegistrations.size() ; i++) {
 			FilterRegistration filterRegistration =
 				matchedFilterRegistrations.get(i);
 
-			filterDTOs[i] = filterRegistration.getD();
+			if (Arrays.binarySearch(filterRegistration.getD().dispatcher, DispatcherType.REQUEST.toString()) > -1) {
+				filterDTOs.add(filterRegistration.getD());
+			}
 		}
 
-		requestInfoDTO.filterDTOs = filterDTOs;
+		requestInfoDTO.filterDTOs = filterDTOs.toArray(new FilterDTO[0]);
 	}
 
 	private String[] asStringArray(
@@ -1023,8 +1116,8 @@
 				DispatcherType.valueOf(type);
 			}
 			catch (IllegalArgumentException iae) {
-				throw new IllegalArgumentException(
-					"Invalid dispatcher '" + type + "'", iae);
+				throw new HttpWhiteboardFailureException(
+					"Invalid dispatcher '" + type + "'", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$ //$NON-NLS-2$
 			}
 		}
 
@@ -1035,28 +1128,53 @@
 
 	public static void checkPattern(String pattern) {
 		if (pattern == null) {
-			throw new IllegalArgumentException("Pattern cannot be null");
+			throw new HttpWhiteboardFailureException("Pattern cannot be null", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
 		}
 
 		if (pattern.indexOf("*.") == 0) { //$NON-NLS-1$
 			return;
 		}
 
-		if (!pattern.startsWith(Const.SLASH) ||
-			(pattern.endsWith(Const.SLASH) && !pattern.equals(Const.SLASH))) {
+		if (Const.BLANK.equals(pattern)) {
+			return;
+		}
 
-			throw new IllegalArgumentException(
-				"Invalid pattern '" + pattern + "'");
+		if (Const.SLASH.equals(pattern)) {
+			return;
+		}
+
+		if (!pattern.startsWith(Const.SLASH) ||
+			(pattern.endsWith(Const.SLASH) && !pattern.equals(Const.SLASH)) ||
+			pattern.contains("**")) { //$NON-NLS-1$
+
+			throw new HttpWhiteboardFailureException(
+				"Invalid pattern '" + pattern + "'", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 	}
 
-	private void checkPrefix(String prefix) {
-		if (prefix == null) {
-			throw new IllegalArgumentException("Prefix cannot be null");
+	private static void checkPrefix(Object prefixObj) {
+		if (prefixObj == null) {
+			throw new HttpWhiteboardFailureException("Prefix cannot be null.", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
 		}
 
+		if (!(prefixObj instanceof String)) {
+			throw new HttpWhiteboardFailureException("Prefix must be String.", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
+		}
+
+		String prefix = (String)prefixObj;
+
 		if (prefix.endsWith(Const.SLASH) && !prefix.equals(Const.SLASH)) {
-			throw new IllegalArgumentException("Invalid prefix '" + prefix + "'");
+			throw new HttpWhiteboardFailureException("Invalid prefix '" + prefix + "'", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	private static void checkRegex(String regex) {
+		try {
+			Pattern.compile(regex);
+		}
+		catch (PatternSyntaxException pse) {
+			throw new HttpWhiteboardFailureException(
+				"Invalid regex '" + regex + "'", DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$ //$NON-NLS-2$
 		}
 	}
 
@@ -1067,6 +1185,16 @@
 		}
 	}
 
+	private static String createFilter(long contextServiceId) {
+		StringBuilder filterBuilder = new StringBuilder();
+		filterBuilder.append('(');
+		filterBuilder.append(Constants.SERVICE_ID);
+		filterBuilder.append('=');
+		filterBuilder.append(contextServiceId);
+		filterBuilder.append(')');
+		return filterBuilder.toString();
+	}
+
 	private ServletContext createServletContext(
 		Bundle curBundle, ServletContextHelper curServletContextHelper) {
 
@@ -1086,25 +1214,28 @@
 
 		for (EndpointRegistration<?> endpointRegistration : endpointRegistrations) {
 			if (endpointRegistration instanceof ResourceRegistration) {
-				resourceDTOs.add(DTOUtil.clone((ResourceDTO)endpointRegistration.getD()));
+				if (!httpServiceRuntime.failedResourceDTO(endpointRegistration.getServiceReference())) {
+					resourceDTOs.add(DTOUtil.clone((ResourceDTO)endpointRegistration.getD()));
+				}
 			}
 			else {
 				ServletRegistration servletRegistration = (ServletRegistration)endpointRegistration;
-				servletDTOs.add(DTOUtil.clone(servletRegistration.getD()));
+				if (!httpServiceRuntime.failedServletDTO(servletRegistration.getServiceReference())) {
+					servletDTOs.add(DTOUtil.clone(servletRegistration.getD()));
+				}
 
 				ErrorPageDTO errorPageDTO = servletRegistration.getErrorPageDTO();
-				if (errorPageDTO != null) {
+				if ((errorPageDTO != null) &&
+					!httpServiceRuntime.failedErrorPageDTO(servletRegistration.getServiceReference())) {
+
 					errorPageDTOs.add(DTOUtil.clone(errorPageDTO));
 				}
 			}
 		}
 
-		servletContextDTO.errorPageDTOs = errorPageDTOs.toArray(
-			new ErrorPageDTO[errorPageDTOs.size()]);
-		servletContextDTO.resourceDTOs = resourceDTOs.toArray(
-			new ResourceDTO[resourceDTOs.size()]);
-		servletContextDTO.servletDTOs = servletDTOs.toArray(
-			new ServletDTO[servletDTOs.size()]);
+		servletContextDTO.errorPageDTOs = errorPageDTOs.toArray(new ErrorPageDTO[0]);
+		servletContextDTO.resourceDTOs = resourceDTOs.toArray(new ResourceDTO[0]);
+		servletContextDTO.servletDTOs = servletDTOs.toArray(new ServletDTO[0]);
 	}
 
 	private void collectFilterDTOs(
@@ -1116,8 +1247,7 @@
 			filterDTOs.add(DTOUtil.clone(filterRegistration.getD()));
 		}
 
-		servletContextDTO.filterDTOs = filterDTOs.toArray(
-			new FilterDTO[filterDTOs.size()]);
+		servletContextDTO.filterDTOs = filterDTOs.toArray(new FilterDTO[0]);
 	}
 
 	private void collectListenerDTOs(
@@ -1129,8 +1259,7 @@
 			listenerDTOs.add(DTOUtil.clone(listenerRegistration.getD()));
 		}
 
-		servletContextDTO.listenerDTOs = listenerDTOs.toArray(
-			new ListenerDTO[listenerDTOs.size()]);
+		servletContextDTO.listenerDTOs = listenerDTOs.toArray(new ListenerDTO[0]);
 	}
 
 	private Map<String, Object> getDTOAttributes(ServletContext servletContext) {
@@ -1148,9 +1277,9 @@
 	}
 
 	private List<Class<? extends EventListener>> getListenerClasses(
-		ServiceReference<EventListener> serviceReference) {
+		ServiceReference<EventListener> listenerReference) {
 
-		List<String> objectClassList = StringPlus.from(serviceReference.getProperty(Constants.OBJECTCLASS));
+		List<String> objectClassList = StringPlus.from(listenerReference.getProperty(Constants.OBJECTCLASS));
 
 		List<Class<? extends EventListener>> classes =
 			new ArrayList<Class<? extends EventListener>>();
@@ -1184,15 +1313,19 @@
 		return classes;
 	}
 
+	public ServiceReference<ServletContextHelper> getServiceReference() {
+		return serviceReference;
+	}
+
 	private ServletContextHelper getServletContextHelper(Bundle curBundle) {
 		BundleContext context = curBundle.getBundleContext();
-		return context.getService(servletContextHelperRef);
+		return context.getService(serviceReference);
 	}
 
 	public void ungetServletContextHelper(Bundle curBundle) {
 		BundleContext context = curBundle.getBundleContext();
 		try {
-			context.ungetService(servletContextHelperRef);
+			context.ungetService(serviceReference);
 		} catch (IllegalStateException e) {
 			// this can happen if the whiteboard bundle is in the process of stopping
 			// and the framework is in the middle of auto-unregistering any services
@@ -1233,7 +1366,7 @@
 		HttpSessionAdaptor httpSessionAdaptor = activeSessions.remove(sessionId);
 
 		if (httpSessionAdaptor != null) {
-			httpSessionTracker.removeHttpSessionAdaptor(sessionId, httpSessionAdaptor);
+			httpServiceRuntime.getHttpSessionTracker().removeHttpSessionAdaptor(sessionId, httpSessionAdaptor);
 		}
 	}
 
@@ -1304,27 +1437,169 @@
 			return previousHttpSessionAdaptor;
 		}
 
-		httpSessionTracker.addHttpSessionAdaptor(sessionId, httpSessionAdaptor);
+		httpServiceRuntime.getHttpSessionTracker().addHttpSessionAdaptor(sessionId, httpSessionAdaptor);
 
 		return null;
 	}
 
-	private void validate(String preValidationContextName, String preValidationContextPath) {
-		if (!contextNamePattern.matcher(preValidationContextName).matches()) {
+	public void recordFailedErrorPageDTO(
+		ServiceReference<?> servletReference, ExtendedErrorPageDTO errorPageDTO, int failureReason) {
+
+		FailedErrorPageDTO failedErrorPageDTO = new FailedErrorPageDTO();
+		failedErrorPageDTO.asyncSupported = errorPageDTO.asyncSupported;
+		failedErrorPageDTO.errorCodes = errorPageDTO.errorCodes;
+		failedErrorPageDTO.exceptions = errorPageDTO.exceptions;
+		failedErrorPageDTO.failureReason = failureReason;
+		failedErrorPageDTO.initParams = errorPageDTO.initParams;
+		failedErrorPageDTO.name = errorPageDTO.name;
+		failedErrorPageDTO.serviceId = errorPageDTO.serviceId;
+		failedErrorPageDTO.servletContextId = errorPageDTO.servletContextId;
+		failedErrorPageDTO.servletInfo = errorPageDTO.servletInfo;
+
+		getHttpServiceRuntime().recordFailedErrorPageDTO(servletReference, failedErrorPageDTO);
+	}
+
+	public void recordFailedResourceDTO(
+		ServiceReference<?> resourceReference, ResourceDTO resourceDTO, int failureReason) {
+
+		FailedResourceDTO failedResourceDTO = new FailedResourceDTO();
+		failedResourceDTO.failureReason = failureReason;
+		failedResourceDTO.patterns = resourceDTO.patterns;
+		failedResourceDTO.prefix = resourceDTO.prefix;
+		failedResourceDTO.serviceId = resourceDTO.serviceId;
+		failedResourceDTO.servletContextId = resourceDTO.servletContextId;
+
+		getHttpServiceRuntime().recordFailedResourceDTO(resourceReference, failedResourceDTO);
+	}
+
+	public void recordFailedServletDTO(
+		ServiceReference<?> servletReference, ExtendedServletDTO servletDTO, int failureReason) {
+
+		ExtendedFailedServletDTO failedServletDTO = new ExtendedFailedServletDTO();
+
+		if (servletDTO != null) {
+			failedServletDTO.asyncSupported = servletDTO.asyncSupported;
+			failedServletDTO.failureReason = failureReason;
+			failedServletDTO.initParams = servletDTO.initParams;
+			failedServletDTO.multipartEnabled = servletDTO.multipartEnabled;
+			failedServletDTO.multipartFileSizeThreshold = servletDTO.multipartFileSizeThreshold;
+			failedServletDTO.multipartLocation = servletDTO.multipartLocation;
+			failedServletDTO.multipartMaxFileSize = servletDTO.multipartMaxFileSize;
+			failedServletDTO.multipartMaxRequestSize = servletDTO.multipartMaxRequestSize;
+			failedServletDTO.name = servletDTO.name;
+			failedServletDTO.patterns = servletDTO.patterns;
+			failedServletDTO.serviceId = servletDTO.serviceId;
+			failedServletDTO.servletContextId = servletDTO.servletContextId;
+			failedServletDTO.servletInfo = servletDTO.servletInfo;
+		}
+		else {
+			failedServletDTO.asyncSupported = BooleanPlus.from(
+				servletReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED), false);
+			failedServletDTO.failureReason = failureReason;
+			failedServletDTO.initParams = ServiceProperties.parseInitParams(
+				servletReference, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
+			failedServletDTO.multipartEnabled = ServiceProperties.parseBoolean(
+				servletReference, Const.EQUINOX_HTTP_MULTIPART_ENABLED);
+			Integer multipartFileSizeThreshold = (Integer)servletReference.getProperty(
+				Const.EQUINOX_HTTP_MULTIPART_FILESIZETHRESHOLD);
+			if (multipartFileSizeThreshold != null) {
+				failedServletDTO.multipartFileSizeThreshold = multipartFileSizeThreshold;
+			}
+			failedServletDTO.multipartLocation = (String)servletReference.getProperty(
+				Const.EQUINOX_HTTP_MULTIPART_LOCATION);
+			Long multipartMaxFileSize = (Long)servletReference.getProperty(
+				Const.EQUINOX_HTTP_MULTIPART_MAXFILESIZE);
+			if (multipartMaxFileSize != null) {
+				failedServletDTO.multipartMaxFileSize = multipartMaxFileSize;
+			}
+			Long multipartMaxRequestSize = (Long)servletReference.getProperty(
+				Const.EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE);
+			if (multipartMaxRequestSize != null) {
+				failedServletDTO.multipartMaxRequestSize = multipartMaxRequestSize;
+			}
+			failedServletDTO.name = (String)servletReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
+			failedServletDTO.patterns = StringPlus.from(
+				servletReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN)).toArray(new String[0]);
+			failedServletDTO.serviceId = (Long)servletReference.getProperty(Constants.SERVICE_ID);
+			if (servletReference.getProperty(Const.EQUINOX_LEGACY_TCCL_PROP) != null) {
+				failedServletDTO.serviceId = -failedServletDTO.serviceId;
+			}
+			failedServletDTO.servletContextId = getServiceId();
+			failedServletDTO.servletInfo = Const.BLANK;
+		}
+
+		getHttpServiceRuntime().recordFailedServletDTO(servletReference, failedServletDTO);
+	}
+
+	private String validateName() {
+		Object contextNameObj = serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
+
+		if (contextNameObj == null) {
 			throw new IllegalContextNameException(
-				"The context name '" + preValidationContextName + "' does not follow Bundle-SymbolicName syntax.", //$NON-NLS-1$ //$NON-NLS-2$
+				HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + " is null. Ignoring!", //$NON-NLS-1$
+				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
+		}
+		else if (!(contextNameObj instanceof String)) {
+			throw new IllegalContextNameException(
+				HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + " is not String. Ignoring!", //$NON-NLS-1$
 				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
 		}
 
+		String name = (String)contextNameObj;
+
+		if (!contextNamePattern.matcher(name).matches()) {
+			throw new IllegalContextNameException(
+				"The context name '" + name + "' does not follow Bundle-SymbolicName syntax.", //$NON-NLS-1$ //$NON-NLS-2$
+				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
+		}
+
+		// Now check for naming conflicts
+		for (ContextController existingContext : httpServiceRuntime.getContextControllers()) {
+			if (name.equals(existingContext.getContextName())) {
+				if (serviceReference.compareTo(existingContext.serviceReference) < 0) {
+					throw new HttpWhiteboardFailureException("Context with same name exists. " + serviceReference, DTOConstants.FAILURE_REASON_VALIDATION_FAILED); //$NON-NLS-1$
+				}
+
+				httpServiceRuntime.recordFailedServletContextDTO(
+					existingContext.serviceReference, (Long)serviceReference.getProperty(Constants.SERVICE_ID), DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+			}
+		}
+
+		return name;
+	}
+
+	private String validatePath() {
+		Object contextPathObj = serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH);
+
+		if (contextPathObj == null) {
+			throw new IllegalContextPathException(
+				HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + " is null. Ignoring!", //$NON-NLS-1$
+				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
+		}
+		else if (!(contextPathObj instanceof String)) {
+			throw new IllegalContextPathException(
+				HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH + " is not String. Ignoring!", //$NON-NLS-1$
+				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
+		}
+
+		String path = (String)contextPathObj;
+
 		try {
 			@SuppressWarnings("unused")
-			URI uri = new URI(Const.HTTP, Const.LOCALHOST, preValidationContextPath, null);
+			URI uri = new URI(Const.HTTP, Const.LOCALHOST, path, null);
 		}
 		catch (URISyntaxException use) {
 			throw new IllegalContextPathException(
-				"The context path '" + preValidationContextPath + "' is not valid URI path syntax.", //$NON-NLS-1$ //$NON-NLS-2$
+				"The context path '" + path + "' is not valid URI path syntax.", //$NON-NLS-1$ //$NON-NLS-2$
 				DTOConstants.FAILURE_REASON_VALIDATION_FAILED);
 		}
+
+		path = httpServiceRuntime.adaptContextPath(path, serviceReference);
+		if (path.equals(Const.SLASH)) {
+			path = Const.BLANK;
+		}
+
+		return path;
 	}
 
 	private static final String[] DISPATCHER =
@@ -1347,10 +1622,9 @@
 	private final ConcurrentMap<String, HttpSessionAdaptor> activeSessions = new ConcurrentHashMap<String, HttpSessionAdaptor>();
 
 	private final HttpServiceRuntimeImpl httpServiceRuntime;
-	private final HttpSessionTracker httpSessionTracker;
 	private final Set<ListenerRegistration> listenerRegistrations = new HashSet<ListenerRegistration>();
 	private final ProxyContext proxyContext;
-	private final ServiceReference<ServletContextHelper> servletContextHelperRef;
+	private final ServiceReference<ServletContextHelper> serviceReference;
 	private final String servletContextHelperRefFilter;
 	private boolean shutdown;
 	private String string;
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/HttpContextHelperFactory.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/HttpContextHelperFactory.java
deleted file mode 100644
index 3961810..0000000
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/HttpContextHelperFactory.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*******************************************************************************
- * Copyright (c) Nov 21, 2014 Liferay, Inc.
- *
- * This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License 2.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-2.0/
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- *    Liferay, Inc. - initial API and implementation and/or initial 
- *                    documentation
- ******************************************************************************/
-
-package org.eclipse.equinox.http.servlet.internal.context;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.osgi.framework.*;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.context.ServletContextHelper;
-
-public class HttpContextHelperFactory
-	implements ServiceFactory<ServletContextHelper> {
-
-	final HttpContext httpContext;
-	final AtomicReference<ServiceRegistration<ServletContextHelper>> registrationRef =
-		new AtomicReference<ServiceRegistration<ServletContextHelper>>();
-	final AtomicReference<String> filterRef = new AtomicReference<String>();
-	final AtomicLong useCount = new AtomicLong(0);
-	
-	public HttpContextHelperFactory(HttpContext httpContext) {
-		this.httpContext = httpContext;
-	}
-
-	@Override
-	public ServletContextHelper getService(
-		Bundle bundle, ServiceRegistration<ServletContextHelper> registration) {
-		setRegistration(registration);
-		return new HttpContextHelper(bundle);
-	}
-
-	@Override
-	public void ungetService(
-		Bundle bundle, ServiceRegistration<ServletContextHelper> registration,
-		ServletContextHelper service) {
-		// nothing to do
-	}
-
-	public void setRegistration(ServiceRegistration<ServletContextHelper> registration) {
-		if (this.registrationRef.compareAndSet(null, registration)) {
-			StringBuilder filterBuilder = new StringBuilder();
-			filterBuilder.append('(');
-			filterBuilder.append(Constants.SERVICE_ID);
-			filterBuilder.append('=');
-			filterBuilder.append(registration.getReference().getProperty(Constants.SERVICE_ID));
-			filterBuilder.append(')');
-			filterRef.compareAndSet(null, filterBuilder.toString());
-		}
-	}
-
-	public ServiceReference<ServletContextHelper> getServiceReference() {
-		ServiceRegistration<ServletContextHelper> reg = registrationRef.get();
-		if (reg != null) {
-			try {
-				return reg.getReference();
-			} catch (IllegalStateException e) {
-				// do nothing
-			}
-		}
-		return null;
-	}
-
-	public String getFilter() {
-		return filterRef.get();
-	}
-
-
-	public long incrementUseCount() {
-		return useCount.incrementAndGet();
-	}
-
-	public long decrementUseCount() {
-		long result = useCount.decrementAndGet();
-		if (result == 0) {
-			ServiceRegistration<ServletContextHelper> registration = registrationRef.get();
-			if (registration != null) {
-				try {
-					registration.unregister();
-				} catch (IllegalStateException e) {
-					// ignore; already unregistered
-				}
-			}
-		}
-		return result;
-	}
-
-	public Object getHttpContext() {
-		return httpContext;
-	}
-
-	public class HttpContextHelper extends ServletContextHelper {
-		private final Bundle bundle;
-		
-		public HttpContextHelper(Bundle bundle) {
-			this.bundle = bundle;
-		}
-
-		@Override
-		public boolean handleSecurity(
-			HttpServletRequest request, HttpServletResponse response)
-			throws IOException {
-			return httpContext.handleSecurity(request, response);
-		}
-
-		@Override
-		public URL getResource(String name) {
-			return httpContext.getResource(name);
-		}
-
-		@Override
-		public String getMimeType(String name) {
-			return httpContext.getMimeType(name);
-		}
-
-		@Override
-		public Set<String> getResourcePaths(String path) {
-			if ((path == null) || (bundle == null)) {
-				return null;
-			}
-
-			final Enumeration<URL> enumeration = bundle.findEntries(
-				path, null, false);
-
-			if (enumeration == null) {
-				return null;
-			}
-
-			final Set<String> result = new HashSet<String>();
-
-			while (enumeration.hasMoreElements()) {
-				result.add(enumeration.nextElement().getPath());
-			}
-
-			return result;
-		}
-	}
-}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/WrappedHttpContext.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/WrappedHttpContext.java
new file mode 100644
index 0000000..51246f5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/context/WrappedHttpContext.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 27, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - initial API and implementation and/or initial
+ *                    documentation
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.internal.context;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.equinox.http.servlet.internal.DefaultServletContextHelper;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+
+public class WrappedHttpContext extends DefaultServletContextHelper {
+
+	private final HttpContext httpContext;
+	private final Bundle bundle;
+
+	public WrappedHttpContext(HttpContext httpContext, Bundle bundle) {
+		super(bundle);
+		this.httpContext = httpContext;
+		this.bundle = bundle;
+	}
+
+	@Override
+	public boolean handleSecurity(
+		HttpServletRequest request, HttpServletResponse response)
+		throws IOException {
+		return httpContext.handleSecurity(request, response);
+	}
+
+	@Override
+	public URL getResource(String name) {
+		return httpContext.getResource(name);
+	}
+
+	@Override
+	public String getMimeType(String name) {
+		return httpContext.getMimeType(name);
+	}
+
+	@Override
+	public Set<String> getResourcePaths(String path) {
+		if ((path == null) || (bundle == null)) {
+			return null;
+		}
+
+		final Enumeration<URL> enumeration = bundle.findEntries(
+			path, null, false);
+
+		if (enumeration == null) {
+			return null;
+		}
+
+		final Set<String> result = new HashSet<String>();
+
+		while (enumeration.hasMoreElements()) {
+			result.add(enumeration.nextElement().getPath());
+		}
+
+		return result;
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextFilterTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextFilterTrackerCustomizer.java
index 61b72b3..ee9efa4 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextFilterTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextFilterTrackerCustomizer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -50,11 +50,21 @@
 			return result;
 		}
 
-		if (!contextController.matches(serviceReference)) {
-			return result;
-		}
-
 		try {
+			if (!contextController.matches(serviceReference)) {
+				// Only the default context will perform the "does anyone match" checks.
+				if (httpServiceRuntime.isDefaultContext(contextController) &&
+					!httpServiceRuntime.matchesAnyContext(serviceReference)) {
+
+					throw new HttpWhiteboardFailureException(
+						"Doesn't match any contexts. " + serviceReference, DTOConstants.FAILURE_REASON_NO_SERVLET_CONTEXT_MATCHING); //$NON-NLS-1$
+				}
+
+				return result;
+			}
+
+			httpServiceRuntime.removeFailedFilterDTO(serviceReference);
+
 			result.set(contextController.addFilterRegistration(serviceReference));
 		}
 		catch (HttpWhiteboardFailureException hwfe) {
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java
index 8a8f18e..bb6ad17 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextListenerTrackerCustomizer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -50,11 +50,21 @@
 			return result;
 		}
 
-		if (!contextController.matches(serviceReference)) {
-			return result;
-		}
-
 		try {
+			if (!contextController.matches(serviceReference)) {
+				// Only the default context will perform the "does anyone match" checks.
+				if (httpServiceRuntime.isDefaultContext(contextController) &&
+					!httpServiceRuntime.matchesAnyContext(serviceReference)) {
+
+					throw new HttpWhiteboardFailureException(
+						"Doesn't match any contexts. " + serviceReference, DTOConstants.FAILURE_REASON_NO_SERVLET_CONTEXT_MATCHING); //$NON-NLS-1$
+				}
+
+				return result;
+			}
+
+			httpServiceRuntime.removeFailedListenerDTO(serviceReference);
+
 			String listener = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_LISTENER);
 
 			if (Boolean.FALSE.toString().equalsIgnoreCase(listener)) {
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java
index d7fa1e8..6147720 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextResourceTrackerCustomizer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -48,11 +48,21 @@
 			return result;
 		}
 
-		if (!contextController.matches(serviceReference)) {
-			return result;
-		}
-
 		try {
+			if (!contextController.matches(serviceReference)) {
+				// Only the default context will perform the "does anyone match" checks.
+				if (httpServiceRuntime.isDefaultContext(contextController) &&
+					!httpServiceRuntime.matchesAnyContext(serviceReference)) {
+
+					throw new HttpWhiteboardFailureException(
+						"Doesn't match any contexts. " + serviceReference, DTOConstants.FAILURE_REASON_NO_SERVLET_CONTEXT_MATCHING); //$NON-NLS-1$
+				}
+
+				return result;
+			}
+
+			httpServiceRuntime.removeFailedResourceDTO(serviceReference);
+
 			result.set(contextController.addResourceRegistration(serviceReference));
 		}
 		catch (HttpWhiteboardFailureException hwfe) {
@@ -100,7 +110,7 @@
 		failedResourceDTO.failureReason = failureReason;
 		failedResourceDTO.patterns = StringPlus.from(
 			serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN)).toArray(new String[0]);
-		failedResourceDTO.prefix = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX);
+		failedResourceDTO.prefix = String.valueOf(serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX));
 		failedResourceDTO.serviceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
 		failedResourceDTO.servletContextId = contextController.getServiceId();
 
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java
index b5fddfd..cf50444 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/ContextServletTrackerCustomizer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -16,15 +16,13 @@
 
 import java.util.concurrent.atomic.AtomicReference;
 import javax.servlet.Servlet;
-import org.eclipse.equinox.http.servlet.dto.ExtendedFailedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.HttpServiceRuntimeImpl;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.error.HttpWhiteboardFailureException;
 import org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration;
-import org.eclipse.equinox.http.servlet.internal.util.*;
-import org.osgi.framework.*;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.http.runtime.dto.DTOConstants;
-import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
 /**
  * @author Raymond Augé
@@ -50,22 +48,32 @@
 			return result;
 		}
 
-		if (!contextController.matches(serviceReference)) {
-			return result;
-		}
-
 		try {
+			if (!contextController.matches(serviceReference)) {
+				// Only the default context will perform the "does anyone match" checks.
+				if (httpServiceRuntime.isDefaultContext(contextController) &&
+					!httpServiceRuntime.matchesAnyContext(serviceReference)) {
+
+					throw new HttpWhiteboardFailureException(
+						"Doesn't match any contexts. " + serviceReference, DTOConstants.FAILURE_REASON_NO_SERVLET_CONTEXT_MATCHING); //$NON-NLS-1$
+				}
+
+				return result;
+			}
+
+			httpServiceRuntime.removeFailedServletDTO(serviceReference);
+
 			result.set(contextController.addServletRegistration(serviceReference));
 		}
 		catch (HttpWhiteboardFailureException hwfe) {
 			httpServiceRuntime.log(hwfe.getMessage(), hwfe);
 
-			recordFailedServletDTO(serviceReference, hwfe.getFailureReason());
+			contextController.recordFailedServletDTO(serviceReference, null, hwfe.getFailureReason());
 		}
 		catch (Throwable t) {
 			httpServiceRuntime.log(t.getMessage(), t);
 
-			recordFailedServletDTO(serviceReference, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
+			contextController.recordFailedServletDTO(serviceReference, null, DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT);
 		}
 
 		return result;
@@ -91,46 +99,8 @@
 			registration.destroy();
 		}
 
-		contextController.getHttpServiceRuntime().removeFailedServletDTOs(serviceReference);
-	}
-
-	private void recordFailedServletDTO(
-		ServiceReference<Servlet> serviceReference, int failureReason) {
-
-		ExtendedFailedServletDTO failedServletDTO = new ExtendedFailedServletDTO();
-
-		failedServletDTO.asyncSupported = BooleanPlus.from(
-			serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED), false);
-		failedServletDTO.failureReason = failureReason;
-		failedServletDTO.initParams = ServiceProperties.parseInitParams(
-			serviceReference, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX);
-		failedServletDTO.multipartEnabled = ServiceProperties.parseBoolean(
-			serviceReference, Const.EQUINOX_HTTP_MULTIPART_ENABLED);
-		Integer multipartFileSizeThreshold = (Integer)serviceReference.getProperty(
-			Const.EQUINOX_HTTP_MULTIPART_FILESIZETHRESHOLD);
-		if (multipartFileSizeThreshold != null) {
-			failedServletDTO.multipartFileSizeThreshold = multipartFileSizeThreshold;
-		}
-		failedServletDTO.multipartLocation = (String)serviceReference.getProperty(
-			Const.EQUINOX_HTTP_MULTIPART_LOCATION);
-		Long multipartMaxFileSize = (Long)serviceReference.getProperty(
-			Const.EQUINOX_HTTP_MULTIPART_MAXFILESIZE);
-		if (multipartMaxFileSize != null) {
-			failedServletDTO.multipartMaxFileSize = multipartMaxFileSize;
-		}
-		Long multipartMaxRequestSize = (Long)serviceReference.getProperty(
-			Const.EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE);
-		if (multipartMaxRequestSize != null) {
-			failedServletDTO.multipartMaxRequestSize = multipartMaxRequestSize;
-		}
-		failedServletDTO.name = (String)serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
-		failedServletDTO.patterns = StringPlus.from(
-			serviceReference.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN)).toArray(new String[0]);
-		failedServletDTO.serviceId = (Long)serviceReference.getProperty(Constants.SERVICE_ID);
-		failedServletDTO.servletContextId = contextController.getServiceId();
-		failedServletDTO.servletInfo = Const.BLANK;
-
-		contextController.getHttpServiceRuntime().recordFailedServletDTO(serviceReference, failedServletDTO);
+		contextController.getHttpServiceRuntime().removeFailedServletDTO(serviceReference);
+		contextController.getHttpServiceRuntime().removeFailedErrorPageDTO(serviceReference);
 	}
 
 	private ContextController contextController;
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/RegistrationServiceTrackerCustomizer.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/RegistrationServiceTrackerCustomizer.java
index 0ef3dd1..93954b7 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/RegistrationServiceTrackerCustomizer.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/customizer/RegistrationServiceTrackerCustomizer.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedErrorPageDTO.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedErrorPageDTO.java
new file mode 100644
index 0000000..b6f4b31
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedErrorPageDTO.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 28, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - initial API and implementation and/or initial
+ *                    documentation
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.internal.dto;
+
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+
+/**
+ * Internal Extended DTO model used for simplifying handling logic.
+ */
+public class ExtendedErrorPageDTO extends ErrorPageDTO {
+
+	public enum ErrorCodeType {
+		RANGE_4XX, RANGE_5XX, SPECIFIC
+	}
+
+	/**
+	 * Indicates the type of error codes defined. This is calculated by the system.
+	 */
+	public ErrorCodeType erroCodeType;
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedFailedServletContextDTO.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedFailedServletContextDTO.java
new file mode 100644
index 0000000..8db94fe
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/dto/ExtendedFailedServletContextDTO.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 29, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - initial API and implementation and/or initial
+ *                    documentation
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.internal.dto;
+
+import org.osgi.service.http.runtime.dto.FailedServletContextDTO;
+
+/**
+ * Internal Extended DTO model used for simplifying handling logic.
+ */
+public class ExtendedFailedServletContextDTO extends FailedServletContextDTO {
+
+	/**
+	 * Holds the serviceId of the service that shadowed this context.
+	 */
+	public long shadowingServiceId;
+
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/RegisteredFilterException.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/RegisteredFilterException.java
index 949ca3d..70e0e5c 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/RegisteredFilterException.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/error/RegisteredFilterException.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -15,16 +15,16 @@
 package org.eclipse.equinox.http.servlet.internal.error;
 
 import javax.servlet.Filter;
-import javax.servlet.ServletException;
+import org.osgi.service.http.runtime.dto.DTOConstants;
 
 /**
  * @author Raymond Augé
  */
-public class RegisteredFilterException extends ServletException {
+public class RegisteredFilterException extends HttpWhiteboardFailureException {
 
 	private static final long serialVersionUID = 4321327145573490998L;
 
 	public RegisteredFilterException(Filter filter) {
-		super("Filter has already been registered: " + filter);
+		super("Filter has already been registered: " + filter, DTOConstants.FAILURE_REASON_SERVICE_IN_USE); //$NON-NLS-1$
 	}
-}
\ No newline at end of file
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java
index d27fb84..4f4bd27 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/EndpointRegistration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -22,6 +22,7 @@
 import org.eclipse.equinox.http.servlet.internal.context.ContextController.ServiceHolder;
 import org.eclipse.equinox.http.servlet.internal.servlet.Match;
 import org.osgi.dto.DTO;
+import org.osgi.framework.ServiceReference;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.service.http.context.ServletContextHelper;
 
@@ -31,22 +32,22 @@
 public abstract class EndpointRegistration<D extends DTO>
 	extends MatchableRegistration<Servlet, D> implements Comparable<EndpointRegistration<?>>{
 
-	private final ServiceHolder<Servlet> servletHolder;
+	protected final ServiceHolder<Servlet> servletHolder;
 	private final ServletContextHelper servletContextHelper; //The context used during the registration of the servlet
 	private final ContextController contextController;
 	private final ClassLoader classLoader;
 
 	public EndpointRegistration(
 		ServiceHolder<Servlet> servletHolder, D d, ServletContextHelper servletContextHelper,
-		ContextController contextController, ClassLoader legacyTCCL) {
+		ContextController contextController) {
 
 		super(servletHolder.get(), d);
 		this.servletHolder = servletHolder;
 		this.servletContextHelper = servletContextHelper;
 		this.contextController = contextController;
-		if (legacyTCCL != null) {
+		if (servletHolder.getLegacyTCCL() != null) {
 			// legacy registrations used the current TCCL at registration time
-			classLoader = legacyTCCL;
+			classLoader = servletHolder.getLegacyTCCL();
 		} else {
 			classLoader = servletHolder.getBundle().adapt(BundleWiring.class).getClassLoader();
 		}
@@ -115,6 +116,8 @@
 		return servletContextHelper;
 	}
 
+	public abstract ServiceReference<?> getServiceReference();
+
 	@Override
 	public String match(
 		String name, String servletPath, String pathInfo, String extension,
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/FilterRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/FilterRegistration.java
index 669fb2f..8cc36da 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/FilterRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/FilterRegistration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2011, 2016 IBM Corporation and others.
+ * Copyright (c) 2011, 2019 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -25,8 +25,6 @@
 import org.eclipse.equinox.http.servlet.internal.servlet.FilterChainImpl;
 import org.eclipse.equinox.http.servlet.internal.servlet.Match;
 import org.eclipse.equinox.http.servlet.internal.util.Const;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.service.http.runtime.dto.FilterDTO;
 
@@ -45,35 +43,20 @@
 
 	public FilterRegistration(
 		ServiceHolder<Filter> filterHolder, FilterDTO filterDTO, int priority,
-		ContextController contextController, ClassLoader legacyTCCL) {
+		ContextController contextController) {
 
 		super(filterHolder.get(), filterDTO);
 		this.filterHolder = filterHolder;
 		this.priority = priority;
 		this.contextController = contextController;
 		this.compiledRegexs = getCompiledRegex(filterDTO);
-		if (legacyTCCL != null) {
+		if (filterHolder.getLegacyTCCL() != null) {
 			// legacy filter registrations used the current TCCL at registration time
-			classLoader = legacyTCCL;
+			classLoader = filterHolder.getLegacyTCCL();
 		} else {
 			classLoader = filterHolder.getBundle().adapt(BundleWiring.class).getClassLoader();
 		}
-		String legacyContextFilter = (String) filterHolder.getServiceReference().getProperty(Const.EQUINOX_LEGACY_CONTEXT_SELECT);
-		if (legacyContextFilter != null) {
-			// This is a legacy Filter registration.
-			// This filter tells us the real context controller,
-			// backed by an HttpContext that should be used to init/destroy this Filter
-			org.osgi.framework.Filter f = null;
-			try {
-				 f = FrameworkUtil.createFilter(legacyContextFilter);
-			}
-			catch (InvalidSyntaxException e) {
-				// nothing
-			}
-			initDestoyWithContextController = f == null || contextController.matches(f);
-		} else {
-			initDestoyWithContextController = true;
-		}
+		initDestoyWithContextController = true;
 		needDecode = MatchableRegistration.patternsRequireDecode(filterDTO.patterns);
 	}
 
@@ -243,23 +226,31 @@
 			pattern = pattern.substring(0, extensionMatchIndex + 2);
 		}
 
-		// first try prefix path matching; taking into account wild cards if necessary
-		if ((pattern.charAt(0) == '/')) {
-			if (isPathWildcardMatch(pattern, path)) {
-				if (extensionWithPrefixMatch != null) {
-					return extensionWithPrefixMatch.equals(extension);
+		if (pattern.isEmpty() && Const.SLASH.equals(path)) {
+			return true;
+		}
+		else if (!pattern.isEmpty()) {
+			// first try prefix path matching; taking into account wild cards if necessary
+			if (pattern.charAt(0) == '/') {
+				if (isPathWildcardMatch(pattern, path)) {
+					if (extensionWithPrefixMatch != null) {
+						return extensionWithPrefixMatch.equals(extension);
+					}
+					// special case for context path
+					if (Const.SLASH.equals(path) && Const.SLASH_STAR.equals(pattern)) {
+						return false;
+					}
+					return true;
 				}
-				return true;
+				return false;
 			}
-			return false;
+
+			// next try extension matching if requested
+			if (pattern.charAt(0) == '*') {
+				return pattern.substring(2).equals(extension);
+			}
 		}
 
-		// next try extension matching if requested
-		if (pattern.charAt(0) == '*') {
-			return pattern.substring(2).equals(extension);
-		}
-
-		// this is really an invalid case that should have gotten caught at registration time
 		return false;
 	}
 
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/MatchableRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/MatchableRegistration.java
index 296662d..4fc27ff 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/MatchableRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/MatchableRegistration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -70,12 +70,18 @@
 		if (match == Match.EXACT) {
 			return pattern.equals(servletPath);
 		}
+		if ((match == Match.CONTEXT_ROOT) && Const.BLANK.equals(pattern)) {
+			return  Const.BLANK.equals(servletPath) && Const.SLASH.equals(pathInfo);
+		}
+		if ((match == Match.DEFAULT_SERVLET) && Const.SLASH.equals(pattern)) {
+			return !servletPath.isEmpty() && pathInfo == null;
+		}
 
 		if (pattern.indexOf(Const.SLASH_STAR_DOT) == 0) {
 			pattern = pattern.substring(1);
 		}
 
-		if (pattern.charAt(0) == '/') {
+		if (!pattern.isEmpty() && pattern.charAt(0) == '/') {
 			if ((match == Match.DEFAULT_SERVLET) && (pattern.length() == 1)) {
 				return true;
 			}
@@ -117,4 +123,4 @@
 		}
 		return false;
 	}
-}
\ No newline at end of file
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ResourceRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ResourceRegistration.java
index 2d84f12..5a6e535 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ResourceRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ResourceRegistration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -17,6 +17,7 @@
 import javax.servlet.Servlet;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController.ServiceHolder;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.service.http.runtime.dto.ResourceDTO;
 /**
@@ -25,12 +26,13 @@
 public class ResourceRegistration extends EndpointRegistration<ResourceDTO> {
 
 	public ResourceRegistration(
-		ServiceHolder<Servlet> servletHolder, ResourceDTO resourceDTO,
+		ServiceReference<?> serviceReference, ServiceHolder<Servlet> servletHolder, ResourceDTO resourceDTO,
 		ServletContextHelper servletContextHelper,
-		ContextController contextController, ClassLoader legacyTCCL) {
+		ContextController contextController) {
 
-		super(servletHolder, resourceDTO, servletContextHelper, contextController, legacyTCCL);
+		super(servletHolder, resourceDTO, servletContextHelper, contextController);
 
+		this.serviceReference = serviceReference;
 		name = servletHolder.get().getClass().getName().concat("#").concat(getD().prefix); //$NON-NLS-1$
 		needDecode = MatchableRegistration.patternsRequireDecode(resourceDTO.patterns);
 	}
@@ -51,11 +53,17 @@
 	}
 
 	@Override
+	public ServiceReference<?> getServiceReference() {
+		return serviceReference;
+	}
+
+	@Override
 	public boolean needDecode() {
 		return needDecode;
 	}
 
 	private final boolean needDecode;
 	private final String name;
+	private final ServiceReference<?> serviceReference;
 
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java
index 49df333..9e61c8e 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/registration/ServletRegistration.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2016 Cognos Incorporated, IBM Corporation and others
+ * Copyright (c) 2005, 2019 Cognos Incorporated, IBM Corporation and others
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -23,11 +23,12 @@
 import org.eclipse.equinox.http.servlet.dto.ExtendedServletDTO;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.context.ContextController.ServiceHolder;
+import org.eclipse.equinox.http.servlet.internal.dto.ExtendedErrorPageDTO;
 import org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupport;
 import org.eclipse.equinox.http.servlet.internal.multipart.MultipartSupportFactory;
 import org.eclipse.equinox.http.servlet.internal.servlet.Match;
+import org.osgi.framework.ServiceReference;
 import org.osgi.service.http.context.ServletContextHelper;
-import org.osgi.service.http.runtime.dto.ErrorPageDTO;
 
 //This class wraps the servlet object registered in the HttpService.registerServlet call, to manage the context classloader when handleRequests are being asked.
 public class ServletRegistration extends EndpointRegistration<ExtendedServletDTO> {
@@ -51,11 +52,11 @@
 	}
 
 	public ServletRegistration(
-		ServiceHolder<Servlet> servletHolder, ExtendedServletDTO servletDTO, ErrorPageDTO errorPageDTO,
+		ServiceHolder<Servlet> servletHolder, ExtendedServletDTO servletDTO, ExtendedErrorPageDTO errorPageDTO,
 		ServletContextHelper servletContextHelper,
-		ContextController contextController, ServletContext servletContext, ClassLoader legacyTCCL) {
+		ContextController contextController, ServletContext servletContext) {
 
-		super(servletHolder, servletDTO, servletContextHelper, contextController, legacyTCCL);
+		super(servletHolder, servletDTO, servletContextHelper, contextController);
 
 		this.errorPageDTO = errorPageDTO;
 
@@ -72,7 +73,7 @@
 		needDecode = MatchableRegistration.patternsRequireDecode(servletDTO.patterns);
 	}
 
-	public ErrorPageDTO getErrorPageDTO() {
+	public ExtendedErrorPageDTO getErrorPageDTO() {
 		return errorPageDTO;
 	}
 
@@ -92,6 +93,11 @@
 	}
 
 	@Override
+	public ServiceReference<?> getServiceReference() {
+		return servletHolder.getServiceReference();
+	}
+
+	@Override
 	public String match(
 		String name, String servletPath, String pathInfo, String extension,
 		Match match) {
@@ -126,6 +132,6 @@
 	}
 
 	private final boolean needDecode;
-	private final ErrorPageDTO errorPageDTO;
+	private final ExtendedErrorPageDTO errorPageDTO;
 	private final MultipartSupport multipartSupport;
 }
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java
index bd5e63e..e7f5bc4 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpServletRequestWrapperImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2016 Cognos Incorporated, IBM Corporation and others.
+ * Copyright (c) 2005, 2019 Cognos Incorporated, IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -25,6 +25,7 @@
 import org.eclipse.equinox.http.servlet.internal.context.ContextController;
 import org.eclipse.equinox.http.servlet.internal.context.DispatchTargets;
 import org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration;
+import org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration;
 import org.eclipse.equinox.http.servlet.internal.util.Const;
 import org.eclipse.equinox.http.servlet.internal.util.EventListeners;
 import org.osgi.service.http.HttpContext;
@@ -443,6 +444,20 @@
 		return new ArrayList<Part>(getParts0().values());
 	}
 
+	public AsyncContext startAsync() throws IllegalStateException {
+		EndpointRegistration<?> endpointRegistration = dispatchTargets.peek().getServletRegistration();
+
+		if (endpointRegistration instanceof ServletRegistration) {
+			ServletRegistration servletRegistration = (ServletRegistration)endpointRegistration;
+
+			if (servletRegistration.getD().asyncSupported) {
+				return request.startAsync();
+			}
+		}
+
+		throw new IllegalStateException("Async not supported by " + endpointRegistration); //$NON-NLS-1$
+	}
+
 	private Map<String, Part> getParts0() throws IOException, ServletException {
 		org.eclipse.equinox.http.servlet.internal.registration.ServletRegistration servletRegistration = getServletRegistration();
 
@@ -474,4 +489,4 @@
 		return null;
 	}
 
-}
\ No newline at end of file
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java
index 2cfcd70..8bb8aeb 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/HttpSessionAdaptor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2016 Cognos Incorporated, IBM Corporation and others.
+ * Copyright (c) 2005, 2019 Cognos Incorporated, IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -85,7 +85,7 @@
 	/**@deprecated*/
 	public String[] getValueNames() {
 		Collection<String> result = getAttributeNames0();
-		return result.toArray(new String[result.size()]);
+		return result.toArray(new String[0]);
 	}
 
 	public void invalidate() {
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/Match.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/Match.java
index 6d512d6..ab69bf7 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/Match.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/Match.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -19,6 +19,6 @@
  */
 public enum Match {
 
-	EXACT, EXTENSION, REGEX, DEFAULT_SERVLET
+	EXACT, EXTENSION, REGEX, DEFAULT_SERVLET, CONTEXT_ROOT
 
-}
\ No newline at end of file
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ResponseStateHandler.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ResponseStateHandler.java
index 7aa55ac..c3f8207 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ResponseStateHandler.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/servlet/ResponseStateHandler.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2016 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -24,6 +24,7 @@
 import org.eclipse.equinox.http.servlet.internal.context.DispatchTargets;
 import org.eclipse.equinox.http.servlet.internal.registration.EndpointRegistration;
 import org.eclipse.equinox.http.servlet.internal.registration.FilterRegistration;
+import org.eclipse.equinox.http.servlet.internal.util.HttpStatus;
 
 /**
  * @author Raymond Augé
@@ -84,7 +85,9 @@
 
 			setException(e);
 
-			if (dispatchTargets.getDispatcherType() != DispatcherType.REQUEST) {
+			if (dispatchTargets.getDispatcherType() != DispatcherType.ERROR &&
+				dispatchTargets.getDispatcherType() != DispatcherType.REQUEST) {
+
 				throwException(e);
 			}
 		}
@@ -95,6 +98,43 @@
 				filterRegistration.removeReference();
 			}
 
+			if ((exception != null) && dispatchTargets.getDispatcherType() == DispatcherType.ERROR) {
+				// This was the error handler throwing an error.
+				// We have to handle this with a custom error page of our own.
+
+				PrintWriter writer = response.getWriter();
+
+				Integer status = (Integer)request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
+				String message = (String)request.getAttribute(RequestDispatcher.ERROR_MESSAGE);
+
+				if (message == null) {
+					message = exception.getMessage();
+				}
+
+				request.getServletContext().log(message, exception);
+
+				final HttpStatus httpStatus = HttpStatus.of(status);
+
+				writer.println("<!DOCTYPE html>"); //$NON-NLS-1$
+				writer.println("<html lang=\"en\"><head>"); //$NON-NLS-1$
+				writer.println("<meta charset=\"utf-8\" />"); //$NON-NLS-1$
+				writer.println("<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />"); //$NON-NLS-1$
+				writer.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />"); //$NON-NLS-1$
+				writer.print("<title>"); //$NON-NLS-1$
+				writer.print(httpStatus.value());
+				writer.print(" - "); //$NON-NLS-1$
+				writer.print(httpStatus.description());
+				writer.println("</title></head>"); //$NON-NLS-1$
+				writer.print("<body><div><h1>"); //$NON-NLS-1$
+				writer.print(httpStatus.value());
+				writer.print(" <small>"); //$NON-NLS-1$
+				writer.print(httpStatus.description());
+				writer.println("</small></h1>"); //$NON-NLS-1$
+				writer.print("<p>"); //$NON-NLS-1$
+				writer.print(message);
+				writer.println("</p></div></body></html>"); //$NON-NLS-1$
+			}
+
 			if (dispatchTargets.getDispatcherType() == DispatcherType.FORWARD) {
 				response.flushBuffer();
 
@@ -171,11 +211,24 @@
 		}
 
 		ContextController contextController = dispatchTargets.getContextController();
-		Class<? extends Exception> clazz = exception.getClass();
-		final String className = clazz.getName();
+		Class<?> clazz = exception.getClass();
+		final String originalClassName = clazz.getName();
+		String className = originalClassName;
 
-		final DispatchTargets errorDispatchTargets = contextController.getDispatchTargets(
-			className, null, null, null, null, null, Match.EXACT, null);
+		DispatchTargets errorDispatchTargets;
+
+		do {
+			errorDispatchTargets = contextController.getDispatchTargets(
+				className, null, null, null, null, null, Match.EXACT, null);
+
+			if (errorDispatchTargets != null) {
+				break;
+			}
+
+			clazz = clazz.getSuperclass();
+			className = clazz.getName();
+		}
+		while (Exception.class.isAssignableFrom(clazz));
 
 		if (errorDispatchTargets == null) {
 			throwException(exception);
@@ -196,7 +249,7 @@
 						if (attributeName.equals(RequestDispatcher.ERROR_EXCEPTION)) {
 							return exception;
 						} else if (attributeName.equals(RequestDispatcher.ERROR_EXCEPTION_TYPE)) {
-							return className;
+							return originalClassName;
 						} else if (attributeName.equals(RequestDispatcher.ERROR_MESSAGE)) {
 							return exception.getMessage();
 						} else if (attributeName.equals(RequestDispatcher.ERROR_REQUEST_URI)) {
@@ -340,4 +393,4 @@
 	HttpServletRequest request;
 	HttpServletResponse response;
 
-}
\ No newline at end of file
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
index c71f0f8..2a725ff 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/Const.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2016 Raymond Augé and others.
+ * Copyright (c) 2014, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -48,7 +48,6 @@
 	public static final String EQUINOX_HTTP_MULTIPART_MAXFILESIZE = "equinox.http.whiteboard.servlet.multipart.maxFileSize"; //$NON-NLS-1$
 	public static final String EQUINOX_HTTP_MULTIPART_MAXREQUESTSIZE = "equinox.http.whiteboard.servlet.multipart.maxRequestSize"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_TCCL_PROP = "equinox.legacy.tccl"; //$NON-NLS-1$
-	public static final String EQUINOX_LEGACY_CONTEXT_SELECT = "equinox.context.select"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_CONTEXT_HELPER = "equinox.legacy.context.helper"; //$NON-NLS-1$
 	public static final String EQUINOX_LEGACY_HTTP_CONTEXT_INITIATING_ID = "equinox.legacy.http.context.initiating.id"; //$NON-NLS-1$
 	public static final String UTF8 = "UTF-8"; //$NON-NLS-1$
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
index d22f413..59899f3 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/DTOUtil.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2015, 2016 Raymond Augé and others.
+ * Copyright (c) 2015, 2019 Raymond Augé and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -41,6 +41,22 @@
 		return clone;
 	}
 
+	public static FailedErrorPageDTO clone(FailedErrorPageDTO original) {
+		FailedErrorPageDTO clone = new FailedErrorPageDTO();
+
+		clone.asyncSupported = copy(original.asyncSupported);
+		clone.errorCodes = copy(original.errorCodes);
+		clone.exceptions = copy(original.exceptions);
+		clone.failureReason = copy(original.failureReason);
+		clone.initParams = copyStringMap(original.initParams);
+		clone.name = copy(original.name);
+		clone.serviceId = copy(original.serviceId);
+		clone.servletContextId = copy(original.servletContextId);
+		clone.servletInfo = copy(original.servletInfo);
+
+		return clone;
+	}
+
 	public static FailedFilterDTO clone(FailedFilterDTO original) {
 		FailedFilterDTO clone = new FailedFilterDTO();
 
@@ -177,7 +193,7 @@
 
 	private static long[] copy(long[] array) {
 		if (array == null) {
-			return null;
+			return new long[0];
 		}
 		if (array.length == 0) {
 			return array;
@@ -188,7 +204,7 @@
 
 	private static String[] copy(String[] array) {
 		if (array == null) {
-			return null;
+			return new String[0];
 		}
 		if (array.length == 0) {
 			return array;
@@ -223,14 +239,14 @@
 	}
 
 	private static Map<String, String> copyStringMap(Map<String, String> initParams) {
+		if (initParams == null) {
+			return Collections.emptyMap();
+		}
 		return new HashMap<String, String>(initParams);
 	}
 
 	public static <V> Map<String, Object> copyGenericMap(Map<String, V> value) {
-		if (value == null) {
-			return null;
-		}
-		if (value.isEmpty()) {
+		if ((value == null) || value.isEmpty()) {
 			return Collections.emptyMap();
 		}
 		HashMap<String, Object> result = new HashMap<String, Object>();
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/HttpStatus.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/HttpStatus.java
new file mode 100644
index 0000000..5db5d3e
--- /dev/null
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/HttpStatus.java
@@ -0,0 +1,173 @@
+/*******************************************************************************
+ * Copyright (c) Jan. 26, 2019 Liferay, Inc.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ *    Liferay, Inc. - initial API and implementation and/or initial
+ *                    documentation
+ ******************************************************************************/
+
+package org.eclipse.equinox.http.servlet.internal.util;
+
+/**
+ * @author Raymond Augé
+ */
+public enum HttpStatus {
+
+	UNKNOWN_STATUS(-1, "Unknown Status", ""), //$NON-NLS-1$ //$NON-NLS-2$
+
+    CONTINUE(100, "Continue", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    SWITCHING_PROTOCOLS(101, "Switching Protocols", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PROCESSING(102, "Processing", "RFC2518"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    EARLY_HINTS(103, "Early Hints", "RFC8297"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    OK(200, "OK", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    CREATED(201, "Created", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    ACCEPTED(202, "Accepted", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NON_AUTHORITATIVE_INFORMATION(203, "Non Authoritative Information", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NO_CONTENT(204, "No Content", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    RESET_CONTENT(205, "Reset Content", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PARTIAL_CONTENT(206, "Partial Content", "RFC7233"), //$NON-NLS-1$ //$NON-NLS-2$
+    /** )*/
+    MULTI_STATUS(207, "Partial Update OK", "RFC4918"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    ALREADY_REPORTED(208, "Already Reported", "RFC5842"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    IM_USED(226, "IM Used", "RFC3229"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    MULTIPLE_CHOICES(300, "Multiple Choices", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    MOVED_PERMANENTLY(301, "Moved Permanently", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    MOVED_TEMPORARILY(302, "Found", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    SEE_OTHER(303, "See Other", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NOT_MODIFIED(304, "Not Modified", "RFC7232"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    USE_PROXY(305, "Use Proxy", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    TEMPORARY_REDIRECT(307, "Temporary Redirect", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PERMANENT_REDIRECT(308, "Permanent Redirect", "RFC7538"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    BAD_REQUEST(400, "Bad Request", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    UNAUTHORIZED(401, "Unauthorized", "RFC7235"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PAYMENT_REQUIRED(402, "Payment Required", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    FORBIDDEN(403, "Forbidden", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NOT_FOUND(404, "Not Found", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    METHOD_NOT_ALLOWED(405, "Method Not Allowed", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NOT_ACCEPTABLE(406, "Not Acceptable", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required", "RFC7235"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    REQUEST_TIMEOUT(408, "Request Timeout", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    CONFLICT(409, "Conflict", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    GONE(410, "Gone", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    LENGTH_REQUIRED(411, "Length Required", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PRECONDITION_FAILED(412, "Precondition Failed", "RFC7232"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PAYLOAD_TOO_LARGE(413, "Payload Too Large", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    URI_TOO_LONG(414, "URI Too Long", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    RANGE_NOT_SATISFIABLE(416, "Range Not Satisfiable", "RFC7233"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    EXPECTATION_FAILED(417, "Expectation Failed", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    MISDIRECTED_REQUEST(421, "Misdirected Request", "RFC7540"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    UNPROCESSABLE_ENTITY(422, "Unprocessable Entity", "RFC4918"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    LOCKED(423, "Locked", "RFC4918"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    FAILED_DEPENDENCY(424, "Failed Dependency", "RFC4918"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    PRECONDITION_REQUIRED(428, "Precondition Required", "RFC6585"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    TOO_MANY_REQUESTS(429, "Too Many Requests", "RFC6585"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    REQUEST_HEADER_FIELDS_TOO_LARGE(431, "Request Header Fields Too Large", "RFC6585"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    UNAVAILABLE_FOR_LEGAL_REASONS(451, "Unavailable For Legal Reasons", "RFC7725"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    INTERNAL_SERVER_ERROR(500, "Internal Server Error", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NOT_IMPLEMENTED(501, "Not Implemented", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    BAD_GATEWAY(502, "Bad Gateway", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    SERVICE_UNAVAILABLE(503, "Service Unavailable", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    GATEWAY_TIMEOUT(504, "Gateway Timeout", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    HTTP_VERSION_NOT_SUPPORTED(505, "HTTP Version Not Supported", "RFC7231"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    VARIANT_ALSO_NEGOTIATES(506, "Variant Also Negotiates", "RFC2295"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    INSUFFICIENT_STORAGE(507, "Insufficient Storage", "RFC4918"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    LOOP_DETECTED(508, "Loop Detected", "RFC5842"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NOT_EXTENDED(510, "Not Extended", "RFC2774"), //$NON-NLS-1$ //$NON-NLS-2$
+
+    NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required", "RFC6585"); //$NON-NLS-1$ //$NON-NLS-2$
+
+	private HttpStatus(int value, String description, String reference) {
+    	this.value = value;
+    	this.description = description;
+    	this.reference = reference;
+	}
+
+	public String description() {
+		return description;
+	}
+
+    public String reference() {
+    	return reference;
+    }
+
+    public int value() {
+    	return value;
+    }
+
+    private final int value;
+    private final String description;
+    private final String reference;
+
+    public static HttpStatus of(int value) {
+    	for (HttpStatus v : HttpStatus.values()) {
+    		if (v.value == value) {
+    			return v;
+    		}
+    	}
+    	return UNKNOWN_STATUS;
+    }
+}
diff --git a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/ServiceProperties.java b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/ServiceProperties.java
index bca5f10..7ef15ed 100644
--- a/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/ServiceProperties.java
+++ b/bundles/org.eclipse.equinox.http.servlet/src/org/eclipse/equinox/http/servlet/internal/util/ServiceProperties.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2014, 2015 Liferay, Inc.
+ * Copyright (c) 2014, 2019 Liferay, Inc.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -50,9 +50,11 @@
 		}
 		for (String key : serviceReference.getPropertyKeys()) {
 			if (key.startsWith(prefix)) {
+				Object value = serviceReference.getProperty(key);
+				if (value instanceof String) {
 				initParams.put(
-					key.substring(prefix.length()),
-					String.valueOf(serviceReference.getProperty(key)));
+					key.substring(prefix.length()), (String)value);
+				}
 			}
 		}