FELIX-4545 : Implement Servlet Context Helper, more refactoring, add support for ServletContextListener services

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1656283 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
index 1cf76da..af79bb4 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/HttpServiceController.java
@@ -31,8 +31,7 @@
 import org.apache.felix.http.base.internal.listener.ServletRequestListenerManager;
 import org.apache.felix.http.base.internal.service.HttpServiceFactory;
 import org.apache.felix.http.base.internal.service.HttpServiceRuntimeImpl;
-import org.apache.felix.http.base.internal.service.InternalHttpService;
-import org.apache.felix.http.base.internal.whiteboard.ExtenderManager;
+import org.apache.felix.http.base.internal.whiteboard.WhiteboardHttpService;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.http.HttpService;
@@ -71,7 +70,7 @@
     private final HttpSessionAttributeListenerManager sessionAttributeListener;
     private final boolean sharedContextAttributes;
     private final HttpServicePlugin plugin;
-    private volatile ExtenderManager manager;
+    private volatile WhiteboardHttpService whiteboardHttpService;
     private volatile ServiceRegistration serviceReg;
     private volatile ServiceRegistration<HttpServiceRuntime> runtimeReg;
 
@@ -144,17 +143,17 @@
         HttpServiceFactory factory = new HttpServiceFactory(servletContext, this.registry, this.contextAttributeListener, this.sharedContextAttributes);
 
         this.serviceReg = this.bundleContext.registerService(ifaces, factory, this.serviceProps);
-        this.manager = new ExtenderManager(new InternalHttpService(this.bundleContext, servletContext, this.registry), this.bundleContext);
+        this.whiteboardHttpService = new WhiteboardHttpService(this.bundleContext, servletContext, this.registry);
 
         this.runtimeReg = this.bundleContext.registerService(HttpServiceRuntime.class, new HttpServiceRuntimeImpl(), null);
     }
 
     public void unregister()
     {
-        if ( this.manager != null )
+        if ( this.whiteboardHttpService != null )
         {
-            this.manager.close();
-            this.manager = null;
+            this.whiteboardHttpService.close();
+            this.whiteboardHttpService = null;
         }
 
         if ( this.runtimeReg != null )
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
index 450b11f..62d0ee5 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ContextInfo.java
@@ -48,11 +48,24 @@
         this.prefix = prefix;
     }
 
+    private boolean isValidPath()
+    {
+        if ( !this.isEmpty(path) )
+        {
+            // TODO we need more validation
+            if ( path.startsWith("/") && path.endsWith("/") )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public boolean isValid()
     {
-        // TODO - check if name and path are valid values
-        return super.isValid() && !this.isEmpty(this.name) && !this.isEmpty(this.path);
+        // TODO - check if name has valid value
+        return super.isValid() && !this.isEmpty(this.name) && isValidPath();
     }
 
     public String getName()
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextListenerInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextListenerInfo.java
new file mode 100644
index 0000000..fe73d8e
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletContextListenerInfo.java
@@ -0,0 +1,36 @@
+/*
+ * 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.runtime;
+
+import javax.servlet.ServletContextListener;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Info object for registered servlet context listeners
+ *
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public final class ServletContextListenerInfo extends WhiteboardServiceInfo<ServletContextListener>
+{
+    public ServletContextListenerInfo(final ServiceReference<ServletContextListener> ref)
+    {
+        super(ref);
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/InternalHttpService.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/InternalHttpService.java
deleted file mode 100644
index 6b9d3b7..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/InternalHttpService.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * 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.service;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-import javax.servlet.Filter;
-import javax.servlet.Servlet;
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
-
-import org.apache.felix.http.base.internal.context.ExtServletContext;
-import org.apache.felix.http.base.internal.handler.FilterHandler;
-import org.apache.felix.http.base.internal.handler.HandlerRegistry;
-import org.apache.felix.http.base.internal.handler.ServletHandler;
-import org.apache.felix.http.base.internal.runtime.AbstractInfo;
-import org.apache.felix.http.base.internal.runtime.ContextInfo;
-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.osgi.framework.BundleContext;
-import org.osgi.service.http.NamespaceException;
-import org.osgi.service.http.context.ServletContextHelper;
-
-public final class InternalHttpService
-{
-
-    private final HandlerRegistry handlerRegistry;
-
-    private final BundleContext bundleContext;
-
-    private final ServletContext webContext;
-
-    private static final class ContextHolder
-    {
-        public long counter;
-        public ExtServletContext servletContext;
-        public ServletContextHelper servletContextHelper;
-    }
-
-    private final Map<Long, ContextHolder> contextMap = new HashMap<Long, ContextHolder>();
-
-    public InternalHttpService(final BundleContext bundleContext,
-            final ServletContext context,
-            final HandlerRegistry handlerRegistry)
-    {
-        this.handlerRegistry = handlerRegistry;
-        this.bundleContext = bundleContext;
-        this.webContext = context;
-    }
-
-    private ExtServletContext getServletContext(@Nonnull final ContextInfo contextInfo,
-            @Nonnull final AbstractInfo<?> serviceInfo)
-    {
-        final Long key = contextInfo.getServiceId();
-        synchronized ( this.contextMap )
-        {
-            ContextHolder holder = this.contextMap.get(key);
-            if ( holder == null )
-            {
-                holder = new ContextHolder();
-                // TODO check for null
-                holder.servletContextHelper = serviceInfo.getServiceReference().getBundle().getBundleContext()
-                        .getServiceObjects(contextInfo.getServiceReference()).getService();
-                holder.servletContext = new ServletContextImpl(serviceInfo.getServiceReference().getBundle(),
-                        this.webContext,
-                        holder.servletContextHelper);
-            }
-            holder.counter++;
-
-            return holder.servletContext;
-        }
-    }
-
-    private void ungetServletContext(@Nonnull final ContextInfo contextInfo)
-    {
-        final Long key = contextInfo.getServiceId();
-        synchronized ( this.contextMap )
-        {
-            ContextHolder holder = this.contextMap.get(key);
-            if ( holder != null )
-            {
-                holder.counter--;
-                if ( holder.counter <= 0 )
-                {
-                    this.contextMap.remove(key);
-                }
-            }
-        }
-    }
-
-    /**
-     * Register a servlet.
-     * @param contextInfo The servlet context helper info
-     * @param servletInfo The servlet info
-     */
-    public void registerServlet(@Nonnull final ContextInfo contextInfo,
-            @Nonnull final ServletInfo servletInfo)
-    {
-        final Servlet servlet = this.bundleContext.getServiceObjects(servletInfo.getServiceReference()).getService();
-        // TODO create failure DTO if null
-        if ( servlet != null )
-        {
-            final ServletHandler handler = new ServletHandler(contextInfo,
-                    getServletContext(contextInfo, servletInfo),
-                    servletInfo,
-                    servlet);
-            try {
-                this.handlerRegistry.addServlet(contextInfo, handler);
-            } catch (ServletException e) {
-                // TODO create failure DTO
-            } catch (NamespaceException e) {
-                // TODO create failure DTO
-            }
-        }
-    }
-
-    /**
-     * Unregister a servlet
-     * @param contextInfo The servlet context helper info
-     * @param servletInfo The servlet info
-     */
-    public void unregisterServlet(@Nonnull final ContextInfo contextInfo, @Nonnull final ServletInfo servletInfo)
-    {
-        final Servlet instance = this.handlerRegistry.removeServlet(contextInfo, servletInfo, true);
-        if ( instance != null )
-        {
-            this.bundleContext.getServiceObjects(servletInfo.getServiceReference()).ungetService(instance);
-            this.ungetServletContext(contextInfo);
-        }
-    }
-
-    /**
-     * Register a filter
-     * @param contextInfo The servlet context helper info
-     * @param filterInfo The filter info
-     */
-    public void registerFilter(@Nonnull  final ContextInfo contextInfo,
-            @Nonnull final FilterInfo filterInfo)
-    {
-        final Filter filter = this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).getService();
-        // TODO create failure DTO if null
-        if ( filter != null )
-        {
-            final FilterHandler handler = new FilterHandler(contextInfo,
-                    getServletContext(contextInfo, filterInfo),
-                    filter,
-                    filterInfo);
-            try {
-                this.handlerRegistry.addFilter(handler);
-            } catch (final ServletException e) {
-                // TODO create failure DTO
-            }
-        }
-    }
-
-    /**
-     * Unregister a filter
-     * @param contextInfo The servlet context helper info
-     * @param filterInfo The filter info
-     */
-    public void unregisterFilter(@Nonnull final ContextInfo contextInfo, @Nonnull final FilterInfo filterInfo)
-    {
-        final Filter instance = this.handlerRegistry.removeFilter(filterInfo, true);
-        if ( instance != null )
-        {
-            this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).ungetService(instance);
-            this.ungetServletContext(contextInfo);
-        }
-    }
-
-    /**
-     * Register a resource.
-     * @param contextInfo The servlet context helper info
-     * @param resourceInfo The resource info
-     */
-    public void registerResource(@Nonnull final ContextInfo contextInfo,
-            @Nonnull final ResourceInfo resourceInfo)
-    {
-        final ServletInfo servletInfo = new ServletInfo(resourceInfo, new ResourceServlet(resourceInfo.getPrefix()));
-
-        this.registerServlet(contextInfo, servletInfo);
-    }
-
-    /**
-     * Unregister a resource.
-     * @param contextInfo The servlet context helper info
-     * @param resourceInfo The resource info
-     */
-    public void unregisterResource(@Nonnull final ContextInfo contextInfo, @Nonnull final ResourceInfo resourceInfo)
-    {
-        final ServletInfo servletInfo = new ServletInfo(resourceInfo, null);
-        this.unregisterServlet(contextInfo, servletInfo);
-    }
-}
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
new file mode 100644
index 0000000..d9afe6e
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ContextHandler.java
@@ -0,0 +1,154 @@
+/*
+ * 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.whiteboard;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.runtime.ContextInfo;
+import org.apache.felix.http.base.internal.runtime.ServletContextListenerInfo;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.context.ServletContextHelper;
+
+public final class ContextHandler implements Comparable<ContextHandler>
+{
+    /** The info object for the context. */
+    private final ContextInfo 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;
+
+    private final Map<Long, ServletContextListener> listeners = new HashMap<Long, ServletContextListener>();
+
+    /**
+     * Create new handler.
+     * @param info
+     * @param webContext
+     */
+    public ContextHandler(final ContextInfo info, final ServletContext webContext)
+    {
+        this.info = info;
+        this.sharedContext = new SharedServletContextImpl(webContext);
+    }
+
+    /**
+     * Get the context info
+     */
+    public ContextInfo getContextInfo()
+    {
+        return this.info;
+    }
+
+    @Override
+    public int compareTo(final ContextHandler o)
+    {
+        // TODO Auto-generated method stub
+        return this.info.compareTo(o.info);
+    }
+
+    public void activate(final Bundle bundle)
+    {
+        getServletContext(bundle);
+    }
+
+    public void deactivate(final Bundle bundle)
+    {
+        this.ungetServletContext(bundle);
+    }
+
+    public void initialized(final Bundle bundle, final ServletContextListenerInfo listenerInfo)
+    {
+        final ServletContextListener listener = bundle.getBundleContext().getServiceObjects(listenerInfo.getServiceReference()).getService();
+        if ( listener != null)
+        {
+            // no need to sync map - initialized is called in sync
+            this.listeners.put(listenerInfo.getServiceId(), listener);
+
+            final ServletContext context = this.getServletContext(listenerInfo.getServiceReference().getBundle());
+
+            listener.contextInitialized(new ServletContextEvent(context));
+        }
+    }
+
+    public void destroyed(final ServletContextListenerInfo listenerInfo)
+    {
+        // no need to sync map - destroyed is called in sync
+        final ServletContextListener listener = this.listeners.remove(listenerInfo.getServiceId());
+        if ( listener != null )
+        {
+            final ServletContext context = this.getServletContext(listenerInfo.getServiceReference().getBundle());
+            listener.contextDestroyed(new ServletContextEvent(context));
+            this.ungetServletContext(listenerInfo.getServiceReference().getBundle());
+        }
+    }
+
+    public ExtServletContext getServletContext(@Nonnull final Bundle bundle)
+    {
+        final Long key = bundle.getBundleId();
+        synchronized ( this.contextMap )
+        {
+            ContextHolder holder = this.contextMap.get(key);
+            if ( holder == null )
+            {
+                holder = new ContextHolder();
+                // TODO check for null
+                holder.servletContextHelper = bundle.getBundleContext().getServiceObjects(this.info.getServiceReference()).getService();
+                holder.servletContext = new PerBundleServletContextImpl(bundle,
+                        this.sharedContext,
+                        holder.servletContextHelper);
+            }
+            holder.counter++;
+
+            return holder.servletContext;
+        }
+    }
+
+    public void ungetServletContext(@Nonnull final Bundle bundle)
+    {
+        final Long key = bundle.getBundleId();
+        synchronized ( this.contextMap )
+        {
+            ContextHolder holder = this.contextMap.get(key);
+            if ( holder != null )
+            {
+                holder.counter--;
+                if ( holder.counter <= 0 )
+                {
+                    this.contextMap.remove(key);
+                    bundle.getBundleContext().getServiceObjects(this.info.getServiceReference()).ungetService(holder.servletContextHelper);
+                }
+            }
+        }
+    }
+
+    private static final class ContextHolder
+    {
+        public long counter;
+        public ExtServletContext servletContext;
+        public ServletContextHelper servletContextHelper;
+    }
+
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
deleted file mode 100644
index f16d141..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.whiteboard;
-
-import java.util.ArrayList;
-
-import org.apache.felix.http.base.internal.service.InternalHttpService;
-import org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker;
-import org.apache.felix.http.base.internal.whiteboard.tracker.ResourceTracker;
-import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextHelperTracker;
-import org.apache.felix.http.base.internal.whiteboard.tracker.ServletTracker;
-import org.osgi.framework.BundleContext;
-import org.osgi.util.tracker.ServiceTracker;
-
-/**
- * TODO - move code to ServletContextHelperManager
- */
-public final class ExtenderManager
-{
-    private final ServletContextHelperManager contextManager;
-
-    private final ArrayList<ServiceTracker<?, ?>> trackers = new ArrayList<ServiceTracker<?, ?>>();
-
-    public ExtenderManager(final InternalHttpService httpService, final BundleContext bundleContext)
-    {
-        this.contextManager = new ServletContextHelperManager(bundleContext, httpService);
-        addTracker(new FilterTracker(bundleContext, contextManager));
-        addTracker(new ServletTracker(bundleContext, this.contextManager));
-        addTracker(new ResourceTracker(bundleContext, this.contextManager));
-        addTracker(new ServletContextHelperTracker(bundleContext, this.contextManager));
-    }
-
-    public void close()
-    {
-        for(final ServiceTracker<?, ?> t : this.trackers)
-        {
-            t.close();
-        }
-        this.trackers.clear();
-        this.contextManager.close();
-    }
-
-    private void addTracker(ServiceTracker<?, ?> tracker)
-    {
-        this.trackers.add(tracker);
-        tracker.open();
-    }
- }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextBridge.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextBridge.java
deleted file mode 100644
index f4bb94c..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/HttpContextBridge.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.whiteboard;
-
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.context.ServletContextHelper;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Set;
-
-public class HttpContextBridge extends ServletContextHelper implements HttpContext {
-
-    private final ServletContextHelper delegatee;
-
-    public HttpContextBridge(final ServletContextHelper delegatee)
-    {
-        this.delegatee = delegatee;
-    }
-
-    @Override
-    public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException
-    {
-        return delegatee.handleSecurity(request, response);
-    }
-
-    @Override
-    public URL getResource(String name)
-    {
-        return delegatee.getResource(name);
-    }
-
-    @Override
-    public String getMimeType(String name)
-    {
-        return delegatee.getMimeType(name);
-    }
-
-    @Override
-    public Set<String> getResourcePaths(String path)
-    {
-        return delegatee.getResourcePaths(path);
-    }
-
-    @Override
-    public String getRealPath(String path)
-    {
-        return delegatee.getRealPath(path);
-    }
-
-    public ServletContextHelper getDelegatee()
-    {
-        return delegatee;
-    }
-}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerBundleServletContextImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerBundleServletContextImpl.java
new file mode 100644
index 0000000..3fae532
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerBundleServletContextImpl.java
@@ -0,0 +1,399 @@
+/*
+ * 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.whiteboard;
+
+import java.io.IOException;
+import java.io.InputStream;
+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.SessionCookieConfig;
+import javax.servlet.SessionTrackingMode;
+import javax.servlet.descriptor.JspConfigDescriptor;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.context.ServletContextHelper;
+
+/**
+ * This servlet context implementation represents the per
+ * bundle specific part of a servlet context backed by a
+ * servlet context helper.
+ *
+ */
+public class PerBundleServletContextImpl implements ExtServletContext {
+
+    private final Bundle bundle;
+    private final ServletContext delegatee;
+    private final ServletContextHelper contextHelper;
+
+    public PerBundleServletContextImpl(final Bundle bundle,
+            final ServletContext sharedContext,
+            final ServletContextHelper delegatee)
+    {
+        this.bundle = bundle;
+        this.delegatee = sharedContext;
+        this.contextHelper = delegatee;
+    }
+
+    @Override
+    public boolean handleSecurity(final HttpServletRequest req,
+            final HttpServletResponse res)
+    throws IOException
+    {
+        return this.contextHelper.handleSecurity(req, res);
+    }
+
+    @Override
+    public ClassLoader getClassLoader()
+    {
+        return this.bundle.getClass().getClassLoader();
+    }
+
+    /**
+     * @see javax.servlet.ServletContext#getResource(java.lang.String)
+     */
+    @Override
+    public URL getResource(final String path)
+    {
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return this.contextHelper.getResource(path);
+    }
+
+    @Override
+    public String getMimeType(final String name)
+    {
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return this.contextHelper.getMimeType(name);
+    }
+
+    @Override
+    public String getRealPath(final String path)
+    {
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return this.contextHelper.getRealPath(path);
+    }
+
+    @Override
+    public Set<String> getResourcePaths(final String path)
+    {
+        return this.contextHelper.getResourcePaths(path);
+    }
+
+    @Override
+    public String getContextPath()
+    {
+        return delegatee.getContextPath();
+    }
+
+    @Override
+    public ServletContext getContext(String uripath)
+    {
+        return delegatee.getContext(uripath);
+    }
+
+    @Override
+    public int getMajorVersion()
+    {
+        return delegatee.getMajorVersion();
+    }
+
+    @Override
+    public int getMinorVersion()
+    {
+        return delegatee.getMinorVersion();
+    }
+
+    @Override
+    public int getEffectiveMajorVersion()
+    {
+        return delegatee.getEffectiveMajorVersion();
+    }
+
+    @Override
+    public int getEffectiveMinorVersion()
+    {
+        return delegatee.getEffectiveMinorVersion();
+    }
+
+    @Override
+    public InputStream getResourceAsStream(String path)
+    {
+        return delegatee.getResourceAsStream(path);
+    }
+
+    @Override
+    public RequestDispatcher getRequestDispatcher(String path)
+    {
+        return delegatee.getRequestDispatcher(path);
+    }
+
+    @Override
+    public RequestDispatcher getNamedDispatcher(String name)
+    {
+        return delegatee.getNamedDispatcher(name);
+    }
+
+    @Override
+    public Servlet getServlet(String name) throws ServletException
+    {
+        return delegatee.getServlet(name);
+    }
+
+    @Override
+    public Enumeration<Servlet> getServlets()
+    {
+        return delegatee.getServlets();
+    }
+
+    @Override
+    public Enumeration<String> getServletNames()
+    {
+        return delegatee.getServletNames();
+    }
+
+    @Override
+    public void log(String msg)
+    {
+        delegatee.log(msg);
+    }
+
+    @Override
+    public void log(Exception exception, String msg)
+    {
+        delegatee.log(exception, msg);
+    }
+
+    @Override
+    public void log(String message, Throwable throwable)
+    {
+        delegatee.log(message, throwable);
+    }
+
+    @Override
+    public String getServerInfo()
+    {
+        return delegatee.getServerInfo();
+    }
+
+    @Override
+    public String getInitParameter(String name)
+    {
+        return delegatee.getInitParameter(name);
+    }
+
+    @Override
+    public Enumeration<String> getInitParameterNames()
+    {
+        return delegatee.getInitParameterNames();
+    }
+
+    @Override
+    public boolean setInitParameter(String name, String value)
+    {
+        return delegatee.setInitParameter(name, value);
+    }
+
+    @Override
+    public Object getAttribute(String name)
+    {
+        return delegatee.getAttribute(name);
+    }
+
+    @Override
+    public Enumeration<String> getAttributeNames()
+    {
+        return delegatee.getAttributeNames();
+    }
+
+    @Override
+    public void setAttribute(String name, Object object)
+    {
+        delegatee.setAttribute(name, object);
+    }
+
+    @Override
+    public void removeAttribute(String name)
+    {
+        delegatee.removeAttribute(name);
+    }
+
+    @Override
+    public String getServletContextName()
+    {
+        return delegatee.getServletContextName();
+    }
+
+    @Override
+    public Dynamic addServlet(String servletName, String className)
+    {
+        return delegatee.addServlet(servletName, className);
+    }
+
+    @Override
+    public Dynamic addServlet(String servletName, Servlet servlet)
+    {
+        return delegatee.addServlet(servletName, servlet);
+    }
+
+    @Override
+    public Dynamic addServlet(String servletName,
+            Class<? extends Servlet> servletClass)
+    {
+        return delegatee.addServlet(servletName, servletClass);
+    }
+
+    @Override
+    public <T extends Servlet> T createServlet(Class<T> clazz)
+    throws ServletException
+    {
+        return delegatee.createServlet(clazz);
+    }
+
+    @Override
+    public ServletRegistration getServletRegistration(String servletName)
+    {
+        return delegatee.getServletRegistration(servletName);
+    }
+
+    @Override
+    public Map<String, ? extends ServletRegistration> getServletRegistrations()
+    {
+        return delegatee.getServletRegistrations();
+    }
+
+    @Override
+    public javax.servlet.FilterRegistration.Dynamic addFilter(
+            String filterName, String className)
+    {
+        return delegatee.addFilter(filterName, className);
+    }
+
+    @Override
+    public javax.servlet.FilterRegistration.Dynamic addFilter(
+            String filterName, Filter filter)
+    {
+        return delegatee.addFilter(filterName, filter);
+    }
+
+    @Override
+    public javax.servlet.FilterRegistration.Dynamic addFilter(
+            String filterName, Class<? extends Filter> filterClass)
+    {
+        return delegatee.addFilter(filterName, filterClass);
+    }
+
+    @Override
+    public <T extends Filter> T createFilter(Class<T> clazz)
+            throws ServletException
+    {
+        return delegatee.createFilter(clazz);
+    }
+
+    @Override
+    public FilterRegistration getFilterRegistration(String filterName)
+    {
+        return delegatee.getFilterRegistration(filterName);
+    }
+
+    @Override
+    public Map<String, ? extends FilterRegistration> getFilterRegistrations()
+    {
+        return delegatee.getFilterRegistrations();
+    }
+
+    @Override
+    public SessionCookieConfig getSessionCookieConfig()
+    {
+        return delegatee.getSessionCookieConfig();
+    }
+
+    @Override
+    public void setSessionTrackingModes(
+            Set<SessionTrackingMode> sessionTrackingModes)
+    {
+        delegatee.setSessionTrackingModes(sessionTrackingModes);
+    }
+
+    @Override
+    public Set<SessionTrackingMode> getDefaultSessionTrackingModes()
+    {
+        return delegatee.getDefaultSessionTrackingModes();
+    }
+
+    @Override
+    public Set<SessionTrackingMode> getEffectiveSessionTrackingModes()
+    {
+        return delegatee.getEffectiveSessionTrackingModes();
+    }
+
+    @Override
+    public void addListener(String className)
+    {
+        delegatee.addListener(className);
+    }
+
+    @Override
+    public <T extends EventListener> void addListener(T t)
+    {
+        delegatee.addListener(t);
+    }
+
+    @Override
+    public void addListener(Class<? extends EventListener> listenerClass)
+    {
+        delegatee.addListener(listenerClass);
+    }
+
+    @Override
+    public <T extends EventListener> T createListener(Class<T> clazz)
+    throws ServletException
+    {
+        return delegatee.createListener(clazz);
+    }
+
+    @Override
+    public JspConfigDescriptor getJspConfigDescriptor()
+    {
+        return delegatee.getJspConfigDescriptor();
+    }
+
+    @Override
+    public void declareRoles(String... roleNames)
+    {
+        delegatee.declareRoles(roleNames);
+    }
+
+    @Override
+    public String getVirtualServerName()
+    {
+        return delegatee.getVirtualServerName();
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
index ec4a8e8..41c2b0a 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletContextHelperManager.java
@@ -25,12 +25,14 @@
 import java.util.List;
 import java.util.Map;
 
+import javax.servlet.ServletContext;
+
 import org.apache.felix.http.base.internal.runtime.ContextInfo;
 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.ServletContextListenerInfo;
 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.InternalHttpService;
 import org.apache.felix.http.base.internal.util.MimeTypes;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -43,22 +45,28 @@
 public final class ServletContextHelperManager
 {
     /** A map containing all servlet context registrations. Mapped by context name */
-    private final Map<String, List<ContextInfo>> contextMap = new HashMap<String, List<ContextInfo>>();
+    private final Map<String, List<ContextHandler>> contextMap = new HashMap<String, List<ContextHandler>>();
 
     /** A map with all servlet/filter registrations, mapped by abstract info. */
-    private final Map<WhiteboardServiceInfo<?>, List<ContextInfo>> servicesMap = new HashMap<WhiteboardServiceInfo<?>, List<ContextInfo>>();
+    private final Map<WhiteboardServiceInfo<?>, List<ContextHandler>> servicesMap = new HashMap<WhiteboardServiceInfo<?>, List<ContextHandler>>();
 
-    private final InternalHttpService httpService;
+    private final WhiteboardHttpService httpService;
 
     private final ServiceRegistration<ServletContextHelper> defaultContextRegistration;
 
+    private final ServletContext webContext;
+
+    private final Bundle bundle;
+
     /**
      * Create a new servlet context helper manager
      * and the default context
      */
-    public ServletContextHelperManager(final BundleContext bundleContext, final InternalHttpService httpService)
+    public ServletContextHelperManager(final BundleContext bundleContext, final ServletContext webContext, final WhiteboardHttpService httpService)
     {
         this.httpService = httpService;
+        this.webContext = webContext;
+        this.bundle = bundleContext.getBundle();
 
         final Dictionary<String, Object> props = new Hashtable<String, Object>();
         props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME);
@@ -92,6 +100,9 @@
                 props);
     }
 
+    /**
+     * Clean up the instance
+     */
     public void close()
     {
         // TODO cleanup
@@ -99,32 +110,69 @@
         this.defaultContextRegistration.unregister();
     }
 
-    private void activate(final ContextInfo contextInfo)
+    /**
+     * Activate a servlet context helper.
+     * @param contextInfo A context info
+     */
+    private void activate(final ContextHandler handler)
     {
-        for(final Map.Entry<WhiteboardServiceInfo<?>, List<ContextInfo>> entry : this.servicesMap.entrySet())
+        handler.activate(this.bundle);
+        // context listeners first
+        final List<WhiteboardServiceInfo<?>> services = new ArrayList<WhiteboardServiceInfo<?>>();
+        for(final Map.Entry<WhiteboardServiceInfo<?>, List<ContextHandler>> entry : this.servicesMap.entrySet())
         {
-            if ( entry.getKey().getContextSelectionFilter().match(contextInfo.getServiceReference()) )
+            if ( entry.getKey().getContextSelectionFilter().match(handler.getContextInfo().getServiceReference()) )
             {
-                entry.getValue().add(contextInfo);
-                this.registerWhiteboardService(contextInfo, entry.getKey());
+                entry.getValue().add(handler);
+                if ( entry.getKey() instanceof ServletContextListenerInfo )
+                {
+                    handler.initialized(this.bundle, (ServletContextListenerInfo)entry.getKey());
+                }
+                else
+                {
+                    services.add(entry.getKey());
+                }
             }
         }
+        // now register services
+        for(final WhiteboardServiceInfo<?> info : services)
+        {
+            this.registerWhiteboardService(handler, info);
+        }
     }
 
-    private void deactivate(final ContextInfo contextInfo)
+    /**
+     * Deactivate a servlet context helper.
+     * @param contextInfo A context info
+     */
+    private void deactivate(final ContextHandler handler)
     {
-        final Iterator<Map.Entry<WhiteboardServiceInfo<?>, List<ContextInfo>>> i = this.servicesMap.entrySet().iterator();
+        // context listeners last
+        final List<ServletContextListenerInfo> listeners = new ArrayList<ServletContextListenerInfo>();
+        final Iterator<Map.Entry<WhiteboardServiceInfo<?>, List<ContextHandler>>> i = this.servicesMap.entrySet().iterator();
         while ( i.hasNext() )
         {
-            final Map.Entry<WhiteboardServiceInfo<?>, List<ContextInfo>> entry = i.next();
-            if ( entry.getValue().remove(contextInfo) )
+            final Map.Entry<WhiteboardServiceInfo<?>, List<ContextHandler>> entry = i.next();
+            if ( entry.getValue().remove(handler) )
             {
-                this.unregisterWhiteboardService(contextInfo, entry.getKey());
+                if ( entry.getKey() instanceof ServletContextListenerInfo )
+                {
+                    listeners.add((ServletContextListenerInfo)entry.getKey());
+                }
+                else
+                {
+                    this.unregisterWhiteboardService(handler, entry.getKey());
+                }
                 if ( entry.getValue().isEmpty() ) {
                     i.remove();
                 }
             }
         }
+        for(final ServletContextListenerInfo info : listeners)
+        {
+            handler.destroyed(info);
+        }
+        handler.deactivate(this.bundle);
     }
 
     /**
@@ -132,25 +180,26 @@
      */
     public void addContextHelper(final ContextInfo info)
     {
+        final ContextHandler handler = new ContextHandler(info, this.webContext);
         synchronized ( this.contextMap )
         {
-            List<ContextInfo> holderList = this.contextMap.get(info.getName());
-            if ( holderList == null )
+            List<ContextHandler> handlerList = this.contextMap.get(info.getName());
+            if ( handlerList == null )
             {
-                holderList = new ArrayList<ContextInfo>();
-                this.contextMap.put(info.getName(), holderList);
+                handlerList = new ArrayList<ContextHandler>();
+                this.contextMap.put(info.getName(), handlerList);
             }
-            holderList.add(info);
-            Collections.sort(holderList);
+            handlerList.add(handler);
+            Collections.sort(handlerList);
             // check for activate/deactivate
-            if ( holderList.get(0) == info )
+            if ( handlerList.get(0) == handler )
             {
                 // check for deactivate
-                if ( holderList.size() > 1 )
+                if ( handlerList.size() > 1 )
                 {
-                    this.deactivate(holderList.get(1));
+                    this.deactivate(handlerList.get(1));
                 }
-                this.activate(info);
+                this.activate(handler);
             }
         }
     }
@@ -162,46 +211,49 @@
     {
         synchronized ( this.contextMap )
         {
-            final List<ContextInfo> holderList = this.contextMap.get(info.getName());
-            if ( holderList != null )
+            final List<ContextHandler> handlerList = this.contextMap.get(info.getName());
+            if ( handlerList != null )
             {
-                final Iterator<ContextInfo> i = holderList.iterator();
+                final Iterator<ContextHandler> i = handlerList.iterator();
                 boolean first = true;
                 boolean activateNext = false;
                 while ( i.hasNext() )
                 {
-                    final ContextInfo holder = i.next();
-                    if ( holder.compareTo(info) == 0 )
+                    final ContextHandler handler = i.next();
+                    if ( handler.getContextInfo().compareTo(info) == 0 )
                     {
                         i.remove();
                         // check for deactivate
                         if ( first )
                         {
-                            this.deactivate(holder);
+                            this.deactivate(handler);
                             activateNext = true;
                         }
                         break;
                     }
                     first = false;
                 }
-                if ( holderList.isEmpty() )
+                if ( handlerList.isEmpty() )
                 {
                     this.contextMap.remove(info.getName());
                 }
                 else if ( activateNext )
                 {
-                    this.activate(holderList.get(0));
+                    this.activate(handlerList.get(0));
                 }
             }
         }
     }
 
-    private List<ContextInfo> getMatchingContexts(final WhiteboardServiceInfo<?> info)
+    /**
+     * Find the list of matching contexts for the whiteboard service
+     */
+    private List<ContextHandler> getMatchingContexts(final WhiteboardServiceInfo<?> info)
     {
-        final List<ContextInfo> result = new ArrayList<ContextInfo>();
-        for(final List<ContextInfo> holders : this.contextMap.values()) {
-            final ContextInfo h = holders.get(0);
-            if ( info.getContextSelectionFilter().match(h.getServiceReference()) )
+        final List<ContextHandler> result = new ArrayList<ContextHandler>();
+        for(final List<ContextHandler> handlerList : this.contextMap.values()) {
+            final ContextHandler h = handlerList.get(0);
+            if ( info.getContextSelectionFilter().match(h.getContextInfo().getServiceReference()) )
             {
                 result.add(h);
             }
@@ -217,9 +269,9 @@
     {
         synchronized ( this.contextMap )
         {
-            final List<ContextInfo> holderList = this.getMatchingContexts(info);
-            this.servicesMap.put(info, holderList);
-            for(final ContextInfo h : holderList)
+            final List<ContextHandler> handlerList = this.getMatchingContexts(info);
+            this.servicesMap.put(info, handlerList);
+            for(final ContextHandler h : handlerList)
             {
                 this.registerWhiteboardService(h, info);
             }
@@ -234,10 +286,10 @@
     {
         synchronized ( this.contextMap )
         {
-            final List<ContextInfo> holderList = this.servicesMap.remove(info);
-            if ( holderList != null )
+            final List<ContextHandler> handlerList = this.servicesMap.remove(info);
+            if ( handlerList != null )
             {
-                for(final ContextInfo h : holderList)
+                for(final ContextHandler h : handlerList)
                 {
                     this.unregisterWhiteboardService(h, info);
                 }
@@ -250,19 +302,19 @@
      * @param contextInfo Context info
      * @param info Whiteboard service info
      */
-    private void registerWhiteboardService(final ContextInfo contextInfo, final WhiteboardServiceInfo<?> info)
+    private void registerWhiteboardService(final ContextHandler handler, final WhiteboardServiceInfo<?> info)
     {
         if ( info instanceof ServletInfo )
         {
-            this.httpService.registerServlet(contextInfo, (ServletInfo)info);
+            this.httpService.registerServlet(handler, (ServletInfo)info);
         }
         else if ( info instanceof FilterInfo )
         {
-            this.httpService.registerFilter(contextInfo, (FilterInfo)info);
+            this.httpService.registerFilter(handler, (FilterInfo)info);
         }
         else if ( info instanceof ResourceInfo )
         {
-            this.httpService.registerResource(contextInfo, (ResourceInfo)info);
+            this.httpService.registerResource(handler, (ResourceInfo)info);
         }
     }
 
@@ -271,19 +323,19 @@
      * @param contextInfo Context info
      * @param info Whiteboard service info
      */
-    private void unregisterWhiteboardService(final ContextInfo contextInfo, final WhiteboardServiceInfo<?> info)
+    private void unregisterWhiteboardService(final ContextHandler handler, final WhiteboardServiceInfo<?> info)
     {
         if ( info instanceof ServletInfo )
         {
-            this.httpService.unregisterServlet(contextInfo, (ServletInfo)info);
+            this.httpService.unregisterServlet(handler, (ServletInfo)info);
         }
         else if ( info instanceof FilterInfo )
         {
-            this.httpService.unregisterFilter(contextInfo, (FilterInfo)info);
+            this.httpService.unregisterFilter(handler, (FilterInfo)info);
         }
         else if ( info instanceof ResourceInfo )
         {
-            this.httpService.unregisterResource(contextInfo, (ResourceInfo)info);
+            this.httpService.unregisterResource(handler, (ResourceInfo)info);
         }
     }
 }
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/whiteboard/SharedServletContextImpl.java
similarity index 86%
rename from http/base/src/main/java/org/apache/felix/http/base/internal/service/ServletContextImpl.java
rename to http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/SharedServletContextImpl.java
index d177dbb..aa34c86 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/whiteboard/SharedServletContextImpl.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.felix.http.base.internal.service;
+package org.apache.felix.http.base.internal.whiteboard;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -36,39 +36,31 @@
 import javax.servlet.SessionCookieConfig;
 import javax.servlet.SessionTrackingMode;
 import javax.servlet.descriptor.JspConfigDescriptor;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
 
-import org.apache.felix.http.base.internal.context.ExtServletContext;
 import org.apache.felix.http.base.internal.logger.SystemLogger;
-import org.osgi.framework.Bundle;
-import org.osgi.service.http.context.ServletContextHelper;
 
-public class ServletContextImpl implements ExtServletContext {
+/**
+ * This servlet context implementation represents the shared
+ * part for a servlet context backed by a servlet context helper.
+ *
+ * For each using bundle, a {@link PerBundleServletContextImpl} is created.
+ */
+public class SharedServletContextImpl implements ServletContext
+{
 
-    private final ServletContextHelper delegatee;
-
-    private final Bundle bundle;
     private final ServletContext context;
-    private final Map<String, Object> attributes;
+    private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
 
-    public ServletContextImpl(final Bundle bundle,
-            final ServletContext context,
-            final ServletContextHelper delegatee)
+    public SharedServletContextImpl(final ServletContext webContext)
     {
-        this.bundle = bundle;
-        this.context = context;
-        this.delegatee = delegatee;
-        this.attributes = new ConcurrentHashMap<String, Object>();
+        this.context = webContext;
     }
 
-    /**
-     * @see org.apache.felix.http.base.internal.context.ExtServletContext#handleSecurity(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
-     */
     @Override
-    public boolean handleSecurity(HttpServletRequest request, HttpServletResponse response) throws IOException
+    public ClassLoader getClassLoader()
     {
-        return delegatee.handleSecurity(request, response);
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return null;
     }
 
     /**
@@ -77,13 +69,29 @@
     @Override
     public URL getResource(String path)
     {
-        return delegatee.getResource(path);
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return null;
     }
 
     @Override
     public String getMimeType(String file)
     {
-        return delegatee.getMimeType(file);
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return null;
+    }
+
+    @Override
+    public String getRealPath(String path)
+    {
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return null;
+    }
+
+    @Override
+    public Set<String> getResourcePaths(final String path)
+    {
+        // is implemented by {@link PerBundleServletContextImpl}.
+        return null;
     }
 
     @Override
@@ -182,12 +190,6 @@
     }
 
     @Override
-    public ClassLoader getClassLoader()
-    {
-        return bundle.getClass().getClassLoader();
-    }
-
-    @Override
     public ServletContext getContext(String uri)
     {
         // TODO
@@ -274,12 +276,6 @@
     }
 
     @Override
-    public String getRealPath(String path)
-    {
-        return this.delegatee.getRealPath(path);
-    }
-
-    @Override
     public RequestDispatcher getRequestDispatcher(String uri)
     {
         return this.context.getRequestDispatcher(uri);
@@ -304,12 +300,6 @@
     }
 
     @Override
-    public Set<String> getResourcePaths(final String path)
-    {
-        return this.delegatee.getResourcePaths(path);
-    }
-
-    @Override
     public String getServerInfo()
     {
         return this.context.getServerInfo();
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
new file mode 100644
index 0000000..db46053
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardHttpService.java
@@ -0,0 +1,193 @@
+/*
+ * 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.whiteboard;
+
+import java.util.ArrayList;
+
+import javax.annotation.Nonnull;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+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.service.ResourceServlet;
+import org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ResourceTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextHelperTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextListenerTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.http.NamespaceException;
+import org.osgi.util.tracker.ServiceTracker;
+
+public final class WhiteboardHttpService
+{
+
+    private final HandlerRegistry handlerRegistry;
+
+    private final BundleContext bundleContext;
+
+    private final ServletContextHelperManager contextManager;
+
+    private final ArrayList<ServiceTracker<?, ?>> trackers = new ArrayList<ServiceTracker<?, ?>>();
+
+    /**
+     * Create a new whiteboard http service
+     * @param bundleContext
+     * @param context
+     * @param handlerRegistry
+     */
+    public WhiteboardHttpService(final BundleContext bundleContext,
+            final ServletContext context,
+            final HandlerRegistry handlerRegistry)
+    {
+        this.handlerRegistry = handlerRegistry;
+        this.bundleContext = bundleContext;
+        this.contextManager = new ServletContextHelperManager(bundleContext, context, this);
+        addTracker(new FilterTracker(bundleContext, contextManager));
+        addTracker(new ServletTracker(bundleContext, this.contextManager));
+        addTracker(new ResourceTracker(bundleContext, this.contextManager));
+        addTracker(new ServletContextHelperTracker(bundleContext, this.contextManager));
+        addTracker(new ServletContextListenerTracker(bundleContext, this.contextManager));
+    }
+
+    public void close()
+    {
+        for(final ServiceTracker<?, ?> t : this.trackers)
+        {
+            t.close();
+        }
+        this.trackers.clear();
+        this.contextManager.close();
+    }
+
+    private void addTracker(ServiceTracker<?, ?> tracker)
+    {
+        this.trackers.add(tracker);
+        tracker.open();
+    }
+
+    /**
+     * Register a servlet.
+     * @param contextInfo The servlet context helper info
+     * @param servletInfo The servlet info
+     */
+    public void registerServlet(@Nonnull final ContextHandler contextHandler,
+            @Nonnull final ServletInfo servletInfo)
+    {
+        final Servlet servlet = this.bundleContext.getServiceObjects(servletInfo.getServiceReference()).getService();
+        // TODO create failure DTO if null
+        if ( servlet != null )
+        {
+            final ServletHandler handler = new ServletHandler(contextHandler.getContextInfo(),
+                    contextHandler.getServletContext(servletInfo.getServiceReference().getBundle()),
+                    servletInfo,
+                    servlet);
+            try {
+                this.handlerRegistry.addServlet(contextHandler.getContextInfo(), handler);
+            } catch (ServletException e) {
+                // TODO create failure DTO
+            } catch (NamespaceException e) {
+                // TODO create failure DTO
+            }
+        }
+    }
+
+    /**
+     * Unregister a servlet
+     * @param contextInfo The servlet context helper info
+     * @param servletInfo The servlet info
+     */
+    public void unregisterServlet(@Nonnull final ContextHandler contextHandler, @Nonnull final ServletInfo servletInfo)
+    {
+        final Servlet instance = this.handlerRegistry.removeServlet(contextHandler.getContextInfo(), servletInfo, true);
+        if ( instance != null )
+        {
+            this.bundleContext.getServiceObjects(servletInfo.getServiceReference()).ungetService(instance);
+            contextHandler.ungetServletContext(servletInfo.getServiceReference().getBundle());
+        }
+    }
+
+    /**
+     * Register a filter
+     * @param contextInfo The servlet context helper info
+     * @param filterInfo The filter info
+     */
+    public void registerFilter(@Nonnull  final ContextHandler contextHandler,
+            @Nonnull final FilterInfo filterInfo)
+    {
+        final Filter filter = this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).getService();
+        // TODO create failure DTO if null
+        if ( filter != null )
+        {
+            final FilterHandler handler = new FilterHandler(contextHandler.getContextInfo(),
+                    contextHandler.getServletContext(filterInfo.getServiceReference().getBundle()),
+                    filter,
+                    filterInfo);
+            try {
+                this.handlerRegistry.addFilter(handler);
+            } catch (final ServletException e) {
+                // TODO create failure DTO
+            }
+        }
+    }
+
+    /**
+     * Unregister a filter
+     * @param contextInfo The servlet context helper info
+     * @param filterInfo The filter info
+     */
+    public void unregisterFilter(@Nonnull final ContextHandler contextHandler, @Nonnull final FilterInfo filterInfo)
+    {
+        final Filter instance = this.handlerRegistry.removeFilter(filterInfo, true);
+        if ( instance != null )
+        {
+            this.bundleContext.getServiceObjects(filterInfo.getServiceReference()).ungetService(instance);
+            contextHandler.ungetServletContext(filterInfo.getServiceReference().getBundle());
+        }
+    }
+
+    /**
+     * Register a resource.
+     * @param contextInfo The servlet context helper info
+     * @param resourceInfo The resource info
+     */
+    public void registerResource(@Nonnull final ContextHandler contextHandler,
+            @Nonnull final ResourceInfo resourceInfo)
+    {
+        final ServletInfo servletInfo = new ServletInfo(resourceInfo, new ResourceServlet(resourceInfo.getPrefix()));
+
+        this.registerServlet(contextHandler, servletInfo);
+    }
+
+    /**
+     * Unregister a resource.
+     * @param contextInfo The servlet context helper info
+     * @param resourceInfo The resource info
+     */
+    public void unregisterResource(@Nonnull final ContextHandler contextHandler, @Nonnull final ResourceInfo resourceInfo)
+    {
+        final ServletInfo servletInfo = new ServletInfo(resourceInfo, null);
+        this.unregisterServlet(contextHandler, servletInfo);
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextListenerTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextListenerTracker.java
new file mode 100644
index 0000000..e7632b3
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletContextListenerTracker.java
@@ -0,0 +1,81 @@
+/*
+ * 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.whiteboard.tracker;
+
+import javax.servlet.ServletContextListener;
+
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.ServletContextListenerInfo;
+import org.apache.felix.http.base.internal.whiteboard.ServletContextHelperManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+public final class ServletContextListenerTracker extends AbstractReferenceTracker<ServletContextListener>
+{
+    private final ServletContextHelperManager contextManager;
+
+    /** TODO Currently under discussion. */
+    private static final String OPT_IN_PROP = "osgi.http.whiteboard.listener";
+
+    private static org.osgi.framework.Filter createFilter(final BundleContext btx)
+    {
+        try
+        {
+            return btx.createFilter(String.format("(&(objectClass=%s)(%s=*))",
+                    ServletContextListener.class.getName(),
+                    OPT_IN_PROP));
+        }
+        catch ( final InvalidSyntaxException ise)
+        {
+            // we can safely ignore it as the above filter is a constant
+        }
+        return null; // we never get here - and if we get an NPE which is fine
+    }
+
+    public ServletContextListenerTracker(final BundleContext context, final ServletContextHelperManager manager)
+    {
+        super(context, createFilter(context));
+        this.contextManager = manager;
+    }
+
+    @Override
+    protected void added(final ServiceReference<ServletContextListener> ref)
+    {
+        final ServletContextListenerInfo info = new ServletContextListenerInfo(ref);
+
+        if ( info.isValid() )
+        {
+            this.contextManager.addWhiteboardService(info);
+        }
+        else
+        {
+            SystemLogger.debug("Ignoring Filter service " + ref);
+        }
+    }
+
+    @Override
+    protected void removed(final ServiceReference<ServletContextListener> ref)
+    {
+        final ServletContextListenerInfo info = new ServletContextListenerInfo(ref);
+
+        if ( info.isValid() )
+        {
+            this.contextManager.removeWhiteboardService(info);
+        }
+    }
+}