FELIX-4060 : Implement HTTP Service Update (RFC-189) - correct context and path handling

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1659798 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/context/ExtServletContextWrapper.java b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ExtServletContextWrapper.java
new file mode 100644
index 0000000..6a04c24
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/context/ExtServletContextWrapper.java
@@ -0,0 +1,412 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.context;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.EventListener;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterRegistration;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRegistration.Dynamic;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionListener;
+
+/**
+ * Wrapper of an {code ExtServletContex}.
+ * This implementation simply forwards to the delegate.
+ */
+public abstract class ExtServletContextWrapper implements ExtServletContext
+{
+    private final ExtServletContext delegate;
+
+	public ExtServletContextWrapper(final ExtServletContext delegate)
+    {
+    	this.delegate = delegate;
+    }
+
+	@Override
+    public boolean handleSecurity(final HttpServletRequest req,
+	        final HttpServletResponse res) throws IOException
+	{
+		return delegate.handleSecurity(req, res);
+	}
+
+	@Override
+    public HttpSessionAttributeListener getHttpSessionAttributeListener()
+	{
+		return delegate.getHttpSessionAttributeListener();
+	}
+
+	@Override
+    public HttpSessionListener getHttpSessionListener()
+	{
+		return delegate.getHttpSessionListener();
+	}
+
+	@Override
+    public ServletRequestListener getServletRequestListener()
+	{
+		return delegate.getServletRequestListener();
+	}
+
+	@Override
+    public ServletRequestAttributeListener getServletRequestAttributeListener()
+	{
+		return delegate.getServletRequestAttributeListener();
+	}
+
+	@Override
+    public String getContextPath()
+	{
+		return delegate.getContextPath();
+	}
+
+	@Override
+    public ServletContext getContext(final String uripath)
+	{
+		return delegate.getContext(uripath);
+	}
+
+	@Override
+    public int getMajorVersion()
+	{
+		return delegate.getMajorVersion();
+	}
+
+	@Override
+    public int getMinorVersion()
+	{
+		return delegate.getMinorVersion();
+	}
+
+	@Override
+    public int getEffectiveMajorVersion()
+	{
+		return delegate.getEffectiveMajorVersion();
+	}
+
+	@Override
+    public int getEffectiveMinorVersion()
+	{
+		return delegate.getEffectiveMinorVersion();
+	}
+
+	@Override
+    public String getMimeType(final String file)
+	{
+		return delegate.getMimeType(file);
+	}
+
+	@Override
+    public Set<String> getResourcePaths(final String path)
+	{
+		return delegate.getResourcePaths(path);
+	}
+
+	@Override
+    public URL getResource(final String path) throws MalformedURLException
+	{
+		return delegate.getResource(path);
+	}
+
+	@Override
+    public InputStream getResourceAsStream(final String path)
+	{
+		return delegate.getResourceAsStream(path);
+	}
+
+	@Override
+    public RequestDispatcher getRequestDispatcher(final String path)
+	{
+		return delegate.getRequestDispatcher(path);
+	}
+
+	@Override
+    public RequestDispatcher getNamedDispatcher(final String name)
+	{
+		return delegate.getNamedDispatcher(name);
+	}
+
+	@Override
+    @SuppressWarnings("deprecation")
+    public Servlet getServlet(final String name) throws ServletException
+	{
+		return delegate.getServlet(name);
+	}
+
+	@Override
+    @SuppressWarnings("deprecation")
+    public Enumeration<Servlet> getServlets()
+    {
+		return delegate.getServlets();
+	}
+
+	@Override
+    @SuppressWarnings("deprecation")
+    public Enumeration<String> getServletNames()
+    {
+		return delegate.getServletNames();
+	}
+
+	@Override
+    public void log(final String msg)
+	{
+		delegate.log(msg);
+	}
+
+	@Override
+    @SuppressWarnings("deprecation")
+    public void log(final Exception exception, final String msg)
+	{
+		delegate.log(exception, msg);
+	}
+
+	@Override
+    public void log(final String message, final Throwable throwable)
+	{
+		delegate.log(message, throwable);
+	}
+
+	@Override
+    public String getRealPath(final String path)
+	{
+		return delegate.getRealPath(path);
+	}
+
+	@Override
+    public String getServerInfo()
+	{
+		return delegate.getServerInfo();
+	}
+
+	@Override
+    public String getInitParameter(final String name)
+	{
+		return delegate.getInitParameter(name);
+	}
+
+	@Override
+    public Enumeration<String> getInitParameterNames()
+	{
+		return delegate.getInitParameterNames();
+	}
+
+	@Override
+    public boolean setInitParameter(final String name, final String value)
+	{
+		return delegate.setInitParameter(name, value);
+	}
+
+	@Override
+    public Object getAttribute(final String name)
+	{
+		return delegate.getAttribute(name);
+	}
+
+	@Override
+    public Enumeration<String> getAttributeNames()
+	{
+		return delegate.getAttributeNames();
+	}
+
+	@Override
+    public void setAttribute(final String name, final Object object)
+	{
+		delegate.setAttribute(name, object);
+	}
+
+	@Override
+    public void removeAttribute(final String name)
+	{
+		delegate.removeAttribute(name);
+	}
+
+	@Override
+    public String getServletContextName() {
+		return delegate.getServletContextName();
+	}
+
+	@Override
+    public Dynamic addServlet(final String servletName, final String className)
+	{
+		return delegate.addServlet(servletName, className);
+	}
+
+	@Override
+    public Dynamic addServlet(final String servletName, final Servlet servlet)
+	{
+		return delegate.addServlet(servletName, servlet);
+	}
+
+	@Override
+    public Dynamic addServlet(final String servletName,
+	        final Class<? extends Servlet> servletClass)
+	{
+		return delegate.addServlet(servletName, servletClass);
+	}
+
+	@Override
+    public <T extends Servlet> T createServlet(final Class<T> clazz)
+			throws ServletException
+	{
+		return delegate.createServlet(clazz);
+	}
+
+	@Override
+    public ServletRegistration getServletRegistration(final String servletName)
+	{
+		return delegate.getServletRegistration(servletName);
+	}
+
+	@Override
+    public Map<String, ? extends ServletRegistration> getServletRegistrations()
+	{
+		return delegate.getServletRegistrations();
+	}
+
+	@Override
+    public javax.servlet.FilterRegistration.Dynamic addFilter(
+	        final String filterName, final String className)
+	{
+		return delegate.addFilter(filterName, className);
+	}
+
+	@Override
+    public javax.servlet.FilterRegistration.Dynamic addFilter(
+	        final String filterName, final Filter filter)
+	{
+		return delegate.addFilter(filterName, filter);
+	}
+
+	@Override
+    public javax.servlet.FilterRegistration.Dynamic addFilter(
+	        final String filterName, final Class<? extends Filter> filterClass)
+	{
+		return delegate.addFilter(filterName, filterClass);
+	}
+
+	@Override
+    public <T extends Filter> T createFilter(final Class<T> clazz)
+			throws ServletException
+	{
+		return delegate.createFilter(clazz);
+	}
+
+	@Override
+    public FilterRegistration getFilterRegistration(final String filterName)
+	{
+		return delegate.getFilterRegistration(filterName);
+	}
+
+	@Override
+    public Map<String, ? extends FilterRegistration> getFilterRegistrations()
+	{
+		return delegate.getFilterRegistrations();
+	}
+
+	@Override
+    public SessionCookieConfig getSessionCookieConfig()
+	{
+		return delegate.getSessionCookieConfig();
+	}
+
+	@Override
+    public void setSessionTrackingModes(
+	        final Set<SessionTrackingMode> sessionTrackingModes)
+	{
+		delegate.setSessionTrackingModes(sessionTrackingModes);
+	}
+
+	@Override
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+	{
+		return delegate.getDefaultSessionTrackingModes();
+	}
+
+	@Override
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+	{
+		return delegate.getEffectiveSessionTrackingModes();
+	}
+
+	@Override
+    public void addListener(final String className)
+	{
+		delegate.addListener(className);
+	}
+
+	@Override
+    public <T extends EventListener> void addListener(final T t)
+	{
+		delegate.addListener(t);
+	}
+
+	@Override
+    public void addListener(final Class<? extends EventListener> listenerClass)
+	{
+		delegate.addListener(listenerClass);
+	}
+
+	@Override
+    public <T extends EventListener> T createListener(final Class<T> clazz)
+			throws ServletException
+	{
+		return delegate.createListener(clazz);
+	}
+
+	@Override
+    public JspConfigDescriptor getJspConfigDescriptor()
+	{
+		return delegate.getJspConfigDescriptor();
+	}
+
+	@Override
+    public ClassLoader getClassLoader()
+	{
+		return delegate.getClassLoader();
+	}
+
+	@Override
+    public void declareRoles(final String... roleNames)
+	{
+		delegate.declareRoles(roleNames);
+	}
+
+	@Override
+    public String getVirtualServerName()
+	{
+		return delegate.getVirtualServerName();
+	}
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
index b322bdc..07b68d3 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/Dispatcher.java
@@ -327,29 +327,7 @@
         @Override
         public String getContextPath()
         {
-            /*
-             * FELIX-2030 Calculate the context path for the Http Service
-             * registered servlets from the container context and servlet paths
-             */
-            //            if (contextPath == null)
-            //            {
-            //                final String context = super.getContextPath();
-            //                final String servlet = super.getServletPath();
-            //                if (context == null || context.length() == 0)
-            //                {
-            //                    contextPath = servlet;
-            //                }
-            //                else if (servlet == null || servlet.length() == 0)
-            //                {
-            //                    contextPath = context;
-            //                }
-            //                else
-            //                {
-            //                    contextPath = context + servlet;
-            //                }
-            //            }
-
-            return super.getContextPath();
+            return this.getServletContext().getContextPath();
         }
 
         @Override
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java
index 2bf1eff..2c1439f 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/dispatch/ServletContextWrapper.java
@@ -22,12 +22,13 @@
 import javax.servlet.RequestDispatcher;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.context.ExtServletContextWrapper;
 import org.apache.felix.http.base.internal.service.ServletContextImpl;
 
 /**
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
-class ServletContextWrapper extends ServletContextImpl
+class ServletContextWrapper extends ExtServletContextWrapper
 {
     private final RequestDispatcherProvider provider;
 
@@ -36,7 +37,7 @@
     /**
      * Creates a new {@link ServletContextWrapper} instance.
      */
-    public ServletContextWrapper(Long contextId, ExtServletContext delegate, RequestDispatcherProvider provider)
+    public ServletContextWrapper(final Long contextId, final ExtServletContext delegate, final RequestDispatcherProvider provider)
     {
         super(delegate);
 
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
index a85b31a..72dfd90 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
@@ -47,6 +47,7 @@
 
     void addErrorServlet(String errorPage, ServletHandler handler) throws ServletException
     {
+        // TODO Handle special values 4xx and 5xx
         if (ERROR_CODE_PATTERN.matcher(errorPage).matches())
         {
             Integer errorCode = Integer.valueOf(errorPage);
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
index 0a00329..7bc6984 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRegistry.java
@@ -148,7 +148,7 @@
             {
                 return r.getErrorsMapping();
             }
-            else if ( serviceId == null && requestURI.startsWith(r.getPrefixPath()) )
+            else if ( serviceId == null && r.isMatching(requestURI) != null )
             {
                 return r.getErrorsMapping();
             }
@@ -205,9 +205,10 @@
         final List<PerContextHandlerRegistry> regs = this.registrations;
         for(final PerContextHandlerRegistry r : regs)
         {
-            if ( requestURI.startsWith(r.getPrefixPath()))
-            {
-                final ServletHandler handler = r.getServletHander(requestURI.substring(r.getPrefixPath().length() - 1));
+        	final String pathInContext = r.isMatching(requestURI);
+        	if ( pathInContext != null )
+        	{
+                final ServletHandler handler = r.getServletHander(pathInContext);
                 if ( handler != null )
                 {
                     return handler;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
index 4f34b90..c6b1f4d 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/PerContextHandlerRegistry.java
@@ -45,19 +45,30 @@
 
     private final int ranking;
 
-    private final String prefixPath;
+    private final String path;
+    
+    private final String prefix;
 
     public PerContextHandlerRegistry() {
         this.serviceId = 0;
         this.ranking = Integer.MAX_VALUE;
-        this.prefixPath = "/";
+        this.path = "/";
+        this.prefix = null;
     }
 
     public PerContextHandlerRegistry(final ServletContextHelperInfo info)
     {
         this.serviceId = info.getServiceId();
         this.ranking = info.getRanking();
-        this.prefixPath = info.getPath();
+        this.path = info.getPath();
+        if ( this.path.equals("/") ) 
+        {
+        	prefix = null;
+        }
+        else
+        {
+        	prefix = this.path + "/";
+        }
     }
 
     public synchronized void addFilter(FilterHandler handler) throws ServletException
@@ -76,7 +87,7 @@
     @Override
     public int compareTo(final PerContextHandlerRegistry other)
     {
-        final int result = other.prefixPath.compareTo(this.prefixPath);
+        final int result = other.path.compareTo(this.path);
         if ( result == 0 ) {
             if (other.ranking == this.ranking)
             {
@@ -332,9 +343,21 @@
         this.servletMapping = new HandlerMapping<ServletHandler>(this.servletMap.values());
     }
 
-    public String getPrefixPath()
+    public String isMatching(final String requestURI)
     {
-        return this.prefixPath;
+        if ( requestURI.equals(this.path) )
+        {
+        	return "";
+        }
+        if ( this.prefix == null )
+        {
+        	return requestURI;
+        }
+        if ( requestURI.startsWith(this.prefix) )
+        {
+        	return requestURI.substring(this.prefix.length() - 1);
+        }
+        return null;
     }
 
     public long getContextServiceId()
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java
index 06694b7..182e31c 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextHelperInfo.java
@@ -39,8 +39,6 @@
 
     private final String path;
 
-    private final String prefix;
-
     /**
      * The filter initialization parameters as provided during registration of the filter.
      */
@@ -51,15 +49,6 @@
         super(ref);
         this.name = this.getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME);
         this.path = this.getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH);
-        String prefix = null;
-        if ( !isEmpty(this.path) )
-        {
-            if ( !this.path.equals("/") && this.path.length() > 1 )
-            {
-                prefix = this.path.substring(0, this.path.length() - 1);
-            }
-        }
-        this.prefix = prefix;
         this.initParams = getInitParams(ref, CONTEXT_INIT_PREFIX);
     }
 
@@ -67,8 +56,12 @@
     {
         if ( !this.isEmpty(path) )
         {
+        	if ( path.equals("/") ) 
+        	{
+        		return true;
+        	}
             // TODO we need more validation
-            if ( path.startsWith("/") && path.endsWith("/") )
+            if ( path.startsWith("/") && !path.endsWith("/") )
             {
                 return true;
             }
@@ -93,11 +86,6 @@
         return this.path;
     }
 
-    public String getPrefix()
-    {
-        return this.prefix;
-    }
-
     public Map<String, String> getInitParameters()
     {
         return initParams;
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/ServletContextImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/ServletContextImpl.java
index 3dd59e8..5b53585 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/ServletContextImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/ServletContextImpl.java
@@ -70,16 +70,6 @@
         this.attributes = sharedAttributes ? null : new ConcurrentHashMap<String, Object>();
     }
 
-    protected ServletContextImpl(ExtServletContext delegate)
-    {
-        ServletContextImpl impl = (ServletContextImpl) delegate;
-        this.bundle = impl.bundle;
-        this.context = impl.context;
-        this.httpContext = impl.httpContext;
-        this.attributeListener = impl.attributeListener;
-        this.attributes = impl.attributes;
-    }
-
     @Override
     public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> type)
     {
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
index f89186f..b3703b1 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
@@ -18,7 +18,9 @@
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.ConcurrentSkipListSet;
 
 import javax.annotation.Nonnull;
 import javax.servlet.ServletContext;
@@ -43,6 +45,7 @@
 import org.apache.felix.http.base.internal.runtime.ServletContextListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletRequestAttributeListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletRequestListenerInfo;
+import org.apache.felix.http.base.internal.runtime.WhiteboardServiceInfo;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.ServiceObjects;
 import org.osgi.framework.ServiceReference;
@@ -53,12 +56,15 @@
     /** The info object for the context. */
     private final ServletContextHelperInfo info;
 
-    /** A map of all created servlet contexts. */
-    private final Map<Long, ContextHolder> contextMap = new HashMap<Long, ContextHolder>();
-
     /** The shared part of the servlet context. */
     private final ServletContext sharedContext;
 
+    /** The http bundle. */
+    private final Bundle bundle;
+
+    /** A map of all created servlet contexts. Each bundle gets it's own instance. */
+    private final Map<Long, ContextHolder> perBundleContextMap = new HashMap<Long, ContextHolder>();
+
     /** Servlet context listeners. */
     private final Map<Long, ServletContextListener> listeners = new HashMap<Long, ServletContextListener>();
 
@@ -82,8 +88,8 @@
     private final Map<ServiceReference<ServletRequestAttributeListener>, ServletRequestAttributeListener> requestAttributeListeners =
             new ConcurrentSkipListMap<ServiceReference<ServletRequestAttributeListener>, ServletRequestAttributeListener>();
 
-    /** The http bundle. */
-    private final Bundle bundle;
+    /** All whiteboard services - servlets, filters, resources. */
+    private final Set<WhiteboardServiceInfo<?>> whiteboardServices = new ConcurrentSkipListSet<WhiteboardServiceInfo<?>>();
 
     /**
      * Create new handler.
@@ -98,7 +104,7 @@
         this.bundle = bundle;
         this.sharedContext = new SharedServletContextImpl(webContext,
                 info.getName(),
-                info.getPrefix(),
+                info.getPath(),
                 info.getInitParameters(),
                 getContextAttributeListener());
     }
@@ -125,6 +131,7 @@
     public void deactivate()
     {
         this.ungetServletContext(bundle);
+        this.whiteboardServices.clear();
     }
 
     public void initialized(@Nonnull final ServletContextListenerInfo listenerInfo)
@@ -167,9 +174,9 @@
     public ExtServletContext getServletContext(@Nonnull final Bundle bundle)
     {
         final Long key = bundle.getBundleId();
-        synchronized ( this.contextMap )
+        synchronized ( this.perBundleContextMap )
         {
-            ContextHolder holder = this.contextMap.get(key);
+            ContextHolder holder = this.perBundleContextMap.get(key);
             if ( holder == null )
             {
                 final ServiceObjects<ServletContextHelper> so = bundle.getBundleContext().getServiceObjects(this.info.getServiceReference());
@@ -185,7 +192,7 @@
                             this.getSessionAttributeListener(),
                             this.getServletRequestListener(),
                             this.getServletRequestAttributeListener());
-                    this.contextMap.put(key, holder);
+                    this.perBundleContextMap.put(key, holder);
                 }
             }
             holder.counter++;
@@ -197,15 +204,15 @@
     public void ungetServletContext(@Nonnull final Bundle bundle)
     {
         final Long key = bundle.getBundleId();
-        synchronized ( this.contextMap )
+        synchronized ( this.perBundleContextMap )
         {
-            ContextHolder holder = this.contextMap.get(key);
+            ContextHolder holder = this.perBundleContextMap.get(key);
             if ( holder != null )
             {
                 holder.counter--;
                 if ( holder.counter <= 0 )
                 {
-                    this.contextMap.remove(key);
+                    this.perBundleContextMap.remove(key);
                     final ServiceObjects<ServletContextHelper> so = bundle.getBundleContext().getServiceObjects(this.info.getServiceReference());
                     if ( so != null )
                     {
@@ -521,4 +528,19 @@
             }
         };
     }
+
+    public void addWhiteboardService(final WhiteboardServiceInfo<?> info)
+    {
+        this.whiteboardServices.add(info);
+    }
+
+    public void removeWhiteboardService(final WhiteboardServiceInfo<?> info)
+    {
+        this.whiteboardServices.remove(info);
+    }
+
+    public Set<WhiteboardServiceInfo<?>> getWhiteboardServices()
+    {
+        return this.whiteboardServices;
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java
index 8b934fd..ccab6aa 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java
@@ -60,18 +60,18 @@
 
     public SharedServletContextImpl(final ServletContext webContext,
             final String name,
-            final String prefix,
+            final String path,
             final Map<String, String> initParameters,
             final ServletContextAttributeListener servletContextAttributeListener)
     {
         this.context = webContext;
-        if ( prefix == null )
+        if ( path.equals("/") )
         {
             this.contextPath = webContext.getContextPath();
         }
         else
         {
-            this.contextPath = webContext.getContextPath() + prefix;
+            this.contextPath = webContext.getContextPath() + path;
         }
         this.name = name;
         if ( initParameters != null )
@@ -372,7 +372,6 @@
     @Override
     public SessionCookieConfig getSessionCookieConfig()
     {
-        // TODO
         return this.context.getSessionCookieConfig();
     }
 
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
index e197517..889f0cc 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
@@ -25,6 +25,7 @@
 import java.util.Set;
 
 import javax.annotation.Nonnull;
+import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
 import javax.servlet.ServletContext;
@@ -39,6 +40,7 @@
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ResourceInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.runtime.WhiteboardServiceInfo;
 import org.apache.felix.http.base.internal.service.HttpServiceFactory;
 import org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker;
 import org.apache.felix.http.base.internal.whiteboard.tracker.HttpSessionAttributeListenerTracker;
@@ -184,6 +186,7 @@
                         servlet);
                 try {
                     this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).addServlet(handler);
+                    contextHandler.addWhiteboardService(servletInfo);
                 } catch (final ServletException e) {
                     so.ungetService(servlet);
                     // TODO create failure DTO
@@ -204,6 +207,7 @@
         {
             this.bundleContext.getServiceObjects(servletInfo.getServiceReference()).ungetService(instance);
             contextHandler.ungetServletContext(servletInfo.getServiceReference().getBundle());
+            contextHandler.removeWhiteboardService(servletInfo);
         }
     }
 
@@ -225,6 +229,7 @@
                     filterInfo);
             try {
                 this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).addFilter(handler);
+                contextHandler.addWhiteboardService(filterInfo);
             } catch (final ServletException e) {
                 // TODO create failure DTO
             }
@@ -243,6 +248,7 @@
         {
             this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).ungetService(instance);
             contextHandler.ungetServletContext(filterInfo.getServiceReference().getBundle());
+            contextHandler.removeWhiteboardService(filterInfo);
         }
     }
 
@@ -263,6 +269,7 @@
                 servlet);
         try {
             this.handlerRegistry.getRegistry(contextHandler.getContextInfo()).addServlet(handler);
+            contextHandler.addWhiteboardService(resourceInfo);
         } catch (ServletException e) {
             // TODO create failure DTO
         }
@@ -277,6 +284,7 @@
     {
         final ServletInfo servletInfo = new ServletInfo(resourceInfo);
         this.unregisterServlet(contextHandler, servletInfo);
+        contextHandler.removeWhiteboardService(resourceInfo);
     }
 
     public void registerContext(@Nonnull final ContextHandler contextHandler)
@@ -348,11 +356,77 @@
                     }
                 }
 
-                dto.errorPageDTOs = new ErrorPageDTO[0]; // TODO
-                dto.filterDTOs = new FilterDTO[0]; // TODO
+                final List<ErrorPageDTO> errorPages = new ArrayList<ErrorPageDTO>();
+                final List<FilterDTO> filters = new ArrayList<FilterDTO>();
+                final List<ServletDTO> servlets = new ArrayList<ServletDTO>();
+                final List<ResourceDTO> resources = new ArrayList<ResourceDTO>();
+                for(final WhiteboardServiceInfo<?> info : handler.getWhiteboardServices())
+                {
+                    if ( info instanceof ServletInfo )
+                    {
+                        final ServletInfo si = (ServletInfo)info;
+                        if ( si.getErrorPage() != null )
+                        {
+                            final ErrorPageDTO page = new ErrorPageDTO();
+                            errorPages.add(page);
+                            page.asyncSupported = si.isAsyncSupported();
+                            page.errorCodes = new long[0]; // TODO
+                            page.exceptions = toStringArray(si.getErrorPage()); // TODO
+                            page.initParams = new HashMap<String, String>(si.getInitParameters());
+                            page.name = si.getName();
+                            page.serviceId = si.getServiceId();
+                            page.servletContextId = handler.getContextInfo().getServiceId();
+                            page.servletInfo = null; // TODO
+                        }
+                        if ( si.getPatterns() != null )
+                        {
+                            final ServletDTO servlet = new ServletDTO();
+                            servlets.add(servlet);
+                            servlet.asyncSupported = si.isAsyncSupported();
+                            servlet.initParams = new HashMap<String, String>(si.getInitParameters());
+                            servlet.name = si.getName();
+                            servlet.patterns = toStringArray(si.getPatterns());
+                            servlet.serviceId = si.getServiceId();
+                            servlet.servletContextId = handler.getContextInfo().getServiceId();
+                            servlet.servletInfo = null; // TODO
+                        }
+                    }
+                    else if ( info instanceof ResourceInfo )
+                    {
+                        final ResourceDTO rsrc = new ResourceDTO();
+                        resources.add(rsrc);
+                        rsrc.patterns = ((ResourceInfo)info).getPatterns();
+                        rsrc.prefix = ((ResourceInfo)info).getPrefix();
+                        rsrc.serviceId = info.getServiceId();
+                        rsrc.servletContextId = handler.getContextInfo().getServiceId();
+                    }
+                    else if ( info instanceof FilterInfo )
+                    {
+                        final FilterDTO filter = new FilterDTO();
+                        filters.add(filter);
+                        filter.asyncSupported = ((FilterInfo)info).isAsyncSupported();
+                        final DispatcherType[] dTypes = ((FilterInfo)info).getDispatcher();
+                        filter.dispatcher = new String[dTypes.length];
+                        int index = 0;
+                        for(final DispatcherType dt : dTypes)
+                        {
+                            filter.dispatcher[index++] = dt.name();
+                        }
+                        filter.initParams = new HashMap<String, String>(((FilterInfo)info).getInitParameters());
+                        filter.name = ((FilterInfo)info).getName();
+                        filter.patterns = toStringArray(((FilterInfo)info).getPatterns());
+                        filter.regexs = toStringArray(((FilterInfo)info).getRegexs());
+                        filter.serviceId = info.getServiceId();
+                        filter.servletContextId = handler.getContextInfo().getServiceId();
+                        filter.servletNames = toStringArray(((FilterInfo)info).getServletNames());
+                    }
+                }
+                dto.errorPageDTOs = errorPages.toArray(new ErrorPageDTO[errorPages.size()]);
+                dto.filterDTOs = filters.toArray(new FilterDTO[filters.size()]);
+                dto.resourceDTOs = resources.toArray(new ResourceDTO[resources.size()]);
+                dto.servletDTOs = servlets.toArray(new ServletDTO[servlets.size()]);
+
                 dto.listenerDTOs = new ListenerDTO[0]; // TODO
-                dto.resourceDTOs = new ResourceDTO[0]; // TODO
-                dto.servletDTOs = new ServletDTO[0]; // TODO
             }
             finally
             {
@@ -378,4 +452,13 @@
         return null;
     }
 
+    private static final String[] EMPTY_ARRAY = new String[0];
+    private String[] toStringArray(final String[] array)
+    {
+        if ( array == null )
+        {
+            return EMPTY_ARRAY;
+        }
+        return array;
+    }
 }