FELIX-4546 - Implement HttpServiceRuntime service:

- initial patch from Thomas applied (thanks!);
- fixed a couple of failing itests that were trivial to fix.



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1661116 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 e08d0c7..c3af022 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
@@ -29,7 +29,7 @@
 import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
 import org.apache.felix.http.base.internal.service.HttpServiceFactory;
 import org.apache.felix.http.base.internal.service.listener.ServletContextAttributeListenerManager;
-import org.apache.felix.http.base.internal.whiteboard.WhiteboardHttpService;
+import org.apache.felix.http.base.internal.whiteboard.WhiteboardManager;
 import org.osgi.framework.BundleContext;
 
 public final class HttpServiceController
@@ -39,7 +39,7 @@
     private final Dispatcher dispatcher;
     private final HttpServicePlugin plugin;
     private final HttpServiceFactory httpServiceFactory;
-    private final WhiteboardHttpService whiteboardHttpService;
+    private final WhiteboardManager whiteboardManager;
 
     public HttpServiceController(final BundleContext bundleContext)
     {
@@ -48,7 +48,7 @@
         this.dispatcher = new Dispatcher(this.registry);
         this.plugin = new HttpServicePlugin(bundleContext, registry);
         this.httpServiceFactory = new HttpServiceFactory(this.bundleContext, this.registry);
-        this.whiteboardHttpService = new WhiteboardHttpService(this.bundleContext, this.registry, this.httpServiceFactory);
+        this.whiteboardManager = new WhiteboardManager(bundleContext, this.httpServiceFactory, this.registry);
     }
 
     Dispatcher getDispatcher()
@@ -68,7 +68,7 @@
             @Override
             public void sessionDestroyed(final HttpSessionEvent se) {
                 httpServiceFactory.getSessionListener().sessionDestroyed(se);
-                whiteboardHttpService.sessionDestroyed(se.getSession(), HttpSessionWrapper.getSessionContextIds(se.getSession()));
+                whiteboardManager.sessionDestroyed(se.getSession(), HttpSessionWrapper.getSessionContextIds(se.getSession()));
             }
 
             @Override
@@ -86,7 +86,7 @@
     public void setProperties(final Hashtable<String, Object> props)
     {
         this.httpServiceFactory.setProperties(props);
-        this.whiteboardHttpService.setProperties(props);
+        this.whiteboardManager.setProperties(props);
     }
 
     public void register(final ServletContext servletContext)
@@ -94,20 +94,20 @@
         this.plugin.register();
 
         this.httpServiceFactory.start(servletContext);
-        this.whiteboardHttpService.start(servletContext);
+        this.whiteboardManager.start(servletContext);
 
-        this.dispatcher.setWhiteboardHttpService(this.whiteboardHttpService);
+        this.dispatcher.setWhiteboardManager(this.whiteboardManager);
     }
 
     public void unregister()
     {
         this.plugin.unregister();
 
-        this.dispatcher.setWhiteboardHttpService(null);
+        this.dispatcher.setWhiteboardManager(null);
 
-        if ( this.whiteboardHttpService != null )
+        if ( this.whiteboardManager != null )
         {
-            this.whiteboardHttpService.stop();
+            this.whiteboardManager.stop();
         }
 
         if ( this.httpServiceFactory != null )
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 8d73570..416069e 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
@@ -56,7 +56,7 @@
 import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
 import org.apache.felix.http.base.internal.handler.ServletHandler;
 import org.apache.felix.http.base.internal.util.UriUtils;
-import org.apache.felix.http.base.internal.whiteboard.WhiteboardHttpService;
+import org.apache.felix.http.base.internal.whiteboard.WhiteboardManager;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.useradmin.Authorization;
 
@@ -538,16 +538,16 @@
 
     private final HandlerRegistry handlerRegistry;
 
-    private WhiteboardHttpService whiteboardService;
+    private WhiteboardManager whiteboardManager;
 
     public Dispatcher(final HandlerRegistry handlerRegistry)
     {
         this.handlerRegistry = handlerRegistry;
     }
 
-    public void setWhiteboardHttpService(final WhiteboardHttpService service)
+    public void setWhiteboardManager(final WhiteboardManager service)
     {
-        this.whiteboardService = service;
+        this.whiteboardManager = service;
     }
 
     /**
@@ -565,7 +565,7 @@
         if ( session != null )
         {
             final Set<Long> ids = HttpSessionWrapper.getExpiredSessionContextIds(session);
-            this.whiteboardService.sessionDestroyed(session, ids);
+            this.whiteboardManager.sessionDestroyed(session, ids);
         }
         String requestURI = getRequestURI(req);
         if ( requestURI == null )
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 72dfd90..39bfc7a 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
@@ -16,15 +16,22 @@
  */
 package org.apache.felix.http.base.internal.handler;
 
+import static org.apache.felix.http.base.internal.util.CollectionUtils.union;
+
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 
 import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
+
 public final class ErrorsMapping
 {
     private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d{3}");
@@ -116,4 +123,27 @@
         this.exceptionsMap.clear();
     }
 
+    @SuppressWarnings("unchecked")
+    public Collection<ServletHandler> getMappedHandlers()
+    {
+        return union(errorCodesMap.values(), exceptionsMap.values());
+    }
+
+    public HandlerRuntime.ErrorPage getErrorPage(ServletHandler servletHandler)
+    {
+        Collection<Integer> errorCodes = getCopy(servletHandler, invertedErrorCodesMap);
+        Collection<String> exceptions = getCopy(servletHandler, invertedExceptionsMap);
+        return new HandlerRuntime.ErrorPage(servletHandler, errorCodes, exceptions);
+    }
+
+    private static <T> List<T> getCopy(ServletHandler key, Map<Servlet, Collection<T>> map)
+    {
+        Collection<T> result = map.get(key.getServlet());
+        if (result != null)
+        {
+            return new ArrayList<T>(result);
+        }
+
+        return Collections.emptyList();
+    }
 }
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 7bc6984..3f65777 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
@@ -24,6 +24,7 @@
 import javax.annotation.Nonnull;
 import javax.servlet.DispatcherType;
 
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
 import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
 
 /**
@@ -217,4 +218,14 @@
         }
         return null;
     }
+
+    public synchronized List<HandlerRuntime> getRuntime()
+    {
+        List<HandlerRuntime> handlerRuntimes = new ArrayList<HandlerRuntime>();
+        for (PerContextHandlerRegistry contextRegistry : this.registrations)
+        {
+            handlerRuntimes.add(contextRegistry.getRuntime());
+        }
+        return handlerRuntimes;
+    }
 }
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 6221f47..c0ade02 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
@@ -18,6 +18,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -29,8 +30,11 @@
 import javax.servlet.ServletException;
 
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime.ErrorPage;
 import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.util.InternalIdFactory;
 
 public final class PerContextHandlerRegistry implements Comparable<PerContextHandlerRegistry>
 {
@@ -39,7 +43,7 @@
     private final Map<String, Servlet> servletPatternMap = new HashMap<String, Servlet>();
     private volatile HandlerMapping<ServletHandler> servletMapping = new HandlerMapping<ServletHandler>();
     private volatile HandlerMapping<FilterHandler> filterMapping = new HandlerMapping<FilterHandler>();
-    private volatile ErrorsMapping errorsMapping = new ErrorsMapping();
+    private final ErrorsMapping errorsMapping = new ErrorsMapping();
 
     private final long serviceId;
 
@@ -364,4 +368,19 @@
     {
         return this.serviceId;
     }
+
+    public synchronized HandlerRuntime getRuntime() {
+        List<ServletHandler> servletHandlers = new ArrayList<ServletHandler>(servletMap.values());
+        List<FilterHandler> filterHandlers = new ArrayList<FilterHandler>(filterMap.values());
+
+        Collection<ErrorPage> errorPages = new ArrayList<HandlerRuntime.ErrorPage>();
+        Collection<ServletHandler> errorHandlers = errorsMapping.getMappedHandlers();
+        for (ServletHandler servletHandler : errorHandlers)
+        {
+            errorPages.add(errorsMapping.getErrorPage(servletHandler));
+        }
+        servletHandlers.removeAll(errorHandlers);
+
+        return new HandlerRuntime(servletHandlers, filterHandlers, errorPages, serviceId);
+    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
index 8f11086..4ecc318 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/FilterInfo.java
@@ -137,6 +137,26 @@
         this.dispatcher = new DispatcherType[] {DispatcherType.REQUEST};
     }
 
+    FilterInfo(int serviceRanking,
+            long serviceId,
+            String name,
+            String[] patterns,
+            String[] servletNames,
+            String[] regexs,
+            boolean asyncSupported,
+            DispatcherType[] dispatcher,
+            Map<String, String> initParams)
+    {
+        super(serviceRanking, serviceId);
+        this.name = name;
+        this.patterns = patterns;
+        this.servletNames = servletNames;
+        this.regexs = regexs;
+        this.asyncSupported = asyncSupported;
+        this.dispatcher = dispatcher;
+        this.initParams = initParams;
+    }
+
     @Override
     public boolean isValid()
     {
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/HandlerRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/HandlerRuntime.java
new file mode 100644
index 0000000..ee86d25
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/HandlerRuntime.java
@@ -0,0 +1,102 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Collections;
+
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+
+public final class HandlerRuntime
+{
+    private final Collection<ServletHandler> servletHandlers;
+    private final Collection<FilterHandler> filterHandlers;
+    private final Collection<ErrorPage> errorPages;
+    private final long serviceId;
+
+    public HandlerRuntime(Collection<ServletHandler> servletHandlers,
+            Collection<FilterHandler> filterHandlers,
+            Collection<ErrorPage> errorPages,
+            long serviceId)
+    {
+        this.servletHandlers = servletHandlers;
+        this.filterHandlers = filterHandlers;
+        this.errorPages = errorPages;
+        this.serviceId = serviceId;
+    }
+
+    public static HandlerRuntime empty(long serviceId)
+    {
+        return new HandlerRuntime(Collections.<ServletHandler>emptyList(),
+                Collections.<FilterHandler>emptyList(),
+                Collections.<ErrorPage> emptyList(),
+                serviceId);
+    }
+
+    public Collection<ServletHandler> getServletHandlers()
+    {
+        return servletHandlers;
+    }
+
+    public Collection<FilterHandler> getFilterHandlers()
+    {
+        return filterHandlers;
+    }
+
+    public Collection<ErrorPage> getErrorPages()
+    {
+        return errorPages;
+    }
+
+    public long getServiceId()
+    {
+        return serviceId;
+    }
+
+    public static class ErrorPage {
+        private final ServletHandler servletHandler;
+        private final Collection<Integer> errorCodes;
+        private final Collection<String> exceptions;
+
+        public ErrorPage(ServletHandler servletHandler,
+                Collection<Integer> errorCodes,
+                Collection<String> exceptions)
+        {
+            this.servletHandler = servletHandler;
+            this.errorCodes = errorCodes;
+            this.exceptions = exceptions;
+        }
+
+        public ServletHandler getServletHandler()
+        {
+            return servletHandler;
+        }
+
+        public Collection<Integer> getErrorCodes()
+        {
+            return errorCodes;
+        }
+
+        public Collection<String> getExceptions()
+        {
+            return exceptions;
+        }
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/RegistryRuntime.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/RegistryRuntime.java
new file mode 100644
index 0000000..8f83d67
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/RegistryRuntime.java
@@ -0,0 +1,78 @@
+/*
+ * 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 java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.felix.http.base.internal.whiteboard.ContextHandler;
+import org.osgi.framework.ServiceReference;
+
+public final class RegistryRuntime
+{
+    private final Collection<ContextHandler> contexts;
+    private final Map<Long, Collection<ServiceReference<?>>> listenerRuntimes;
+    private final Map<Long, HandlerRuntime> handlerRuntimes;
+
+    public RegistryRuntime(Collection<ContextHandler> contexts,
+            Collection<HandlerRuntime> contextRuntimes,
+            Map<Long, Collection<ServiceReference<?>>> listenerRuntimes)
+    {
+        this.contexts = contexts;
+        this.handlerRuntimes = createServiceIdMap(contextRuntimes);
+        this.listenerRuntimes = listenerRuntimes;
+    }
+
+    private static Map<Long, HandlerRuntime> createServiceIdMap(Collection<HandlerRuntime> contextRuntimes)
+    {
+        Map<Long, HandlerRuntime> runtimesMap = new HashMap<Long, HandlerRuntime>();
+        for (HandlerRuntime contextRuntime : contextRuntimes)
+        {
+            runtimesMap.put(contextRuntime.getServiceId(), contextRuntime);
+        }
+        return runtimesMap;
+    }
+
+    public HandlerRuntime getHandlerRuntime(ContextHandler contextHandler)
+    {
+        long serviceId = contextHandler.getContextInfo().getServiceId();
+
+        if (handlerRuntimes.containsKey(serviceId))
+        {
+            return handlerRuntimes.get(serviceId);
+        }
+        return HandlerRuntime.empty(serviceId);
+    }
+
+    public Collection<ServiceReference<?>> getListenerRuntime(ContextHandler contextHandler)
+    {
+        if (listenerRuntimes.containsKey(contextHandler.getContextInfo().getServiceId()))
+        {
+            return listenerRuntimes.get(contextHandler.getContextInfo().getServiceId());
+        }
+        return Collections.emptyList();
+    }
+
+    public Collection<ContextHandler> getContexts()
+    {
+        return contexts;
+    }
+}
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 182e31c..9eaf2af 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
@@ -52,6 +52,18 @@
         this.initParams = getInitParams(ref, CONTEXT_INIT_PREFIX);
     }
 
+    ServletContextHelperInfo(int serviceRanking,
+            long serviceId,
+            String name,
+            String path,
+            Map<String, String> initParams)
+    {
+        super(serviceRanking, serviceId);
+        this.name = name;
+        this.path = path;
+        this.initParams = initParams;
+    }
+
     private boolean isValidPath()
     {
         if ( !this.isEmpty(path) )
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
index feed6a9..c24871a 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/ServletInfo.java
@@ -93,7 +93,7 @@
         this.initParams = Collections.emptyMap();
     }
 
-    private static ServiceReference getRef(final ServiceReference ref)
+    private static ServiceReference getRef(ServiceReference ref)
     {
         return ref;
     }
@@ -114,6 +114,22 @@
         this.errorPage = null;
     }
 
+    ServletInfo(int serviceRanking,
+            long serviceId,
+            String name,
+            String[] patterns,
+            String[] errorPage,
+            boolean asyncSupported,
+            Map<String, String> initParams)
+    {
+        super(serviceRanking, serviceId);
+        this.name = name;
+        this.patterns = patterns;
+        this.errorPage = errorPage;
+        this.asyncSupported = asyncSupported;
+        this.initParams = initParams;
+    }
+
     @Override
     public boolean isValid()
     {
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceInfo.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceInfo.java
index 08293e1..a523446 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceInfo.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceInfo.java
@@ -18,8 +18,7 @@
  */
 package org.apache.felix.http.base.internal.runtime;
 
-import java.util.concurrent.atomic.AtomicLong;
-
+import org.apache.felix.http.base.internal.util.InternalIdFactory;
 import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
@@ -31,9 +30,6 @@
  */
 public abstract class WhiteboardServiceInfo<T> extends AbstractInfo<T>
 {
-    /** Service id for services not provided through the service registry. */
-    private static final AtomicLong serviceIdCounter = new AtomicLong(-1);
-
     /** The context selection. */
     private final String contextSelection;
 
@@ -67,7 +63,7 @@
 
     public WhiteboardServiceInfo(final int ranking)
     {
-        this(ranking, serviceIdCounter.getAndDecrement());
+        this(ranking, InternalIdFactory.INSTANCE.next());
 
     }
 
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/BaseDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/BaseDTOBuilder.java
new file mode 100644
index 0000000..9546e6a
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/BaseDTOBuilder.java
@@ -0,0 +1,56 @@
+/*
+ * 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.dto;
+
+import static java.util.Arrays.copyOf;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.dto.DTO;
+
+
+abstract class BaseDTOBuilder<T, U extends DTO>
+{
+    public Collection<U> build(Collection<T> whiteboardServices, long servletContextId)
+    {
+        List<U> dtoList = new ArrayList<U>();
+        for (T whiteboardService : whiteboardServices)
+        {
+            dtoList.add(buildDTO(whiteboardService, servletContextId));
+        }
+        return dtoList;
+    }
+
+    abstract U buildDTO(T whiteboardService, long servletContextId);
+
+    protected <K, V> Map<K, V> copyWithDefault(Map<K, V> map)
+    {
+        return map == null ? Collections.<K, V>emptyMap() : new HashMap<K, V>(map);
+    }
+
+    protected <V> V[] copyWithDefault(V[] array, V[] defaultArray)
+    {
+        return array == null ? defaultArray : copyOf(array, array.length);
+    }
+}
\ No newline at end of file
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/BaseServletDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/BaseServletDTOBuilder.java
new file mode 100644
index 0000000..50d5d94
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/BaseServletDTOBuilder.java
@@ -0,0 +1,42 @@
+/*
+ * 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.dto;
+
+import javax.servlet.Servlet;
+
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.osgi.service.http.runtime.dto.BaseServletDTO;
+
+abstract class BaseServletDTOBuilder<T, U extends BaseServletDTO> extends BaseDTOBuilder<T, U>
+{
+    final U setBaseFields(U dto, ServletHandler servletHandler, long servletContextId)
+    {
+        ServletInfo info = servletHandler.getServletInfo();
+        Servlet servlet = servletHandler.getServlet();
+
+        dto.asyncSupported = info.isAsyncSupported();
+        dto.initParams = copyWithDefault(info.getInitParameters());
+        dto.name = info.getName();
+        dto.serviceId = servletHandler.getServletInfo().getServiceId();
+        dto.servletContextId = servletContextId;
+        dto.servletInfo = servlet.getServletInfo();
+        return dto;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ErrorPageDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ErrorPageDTOBuilder.java
new file mode 100644
index 0000000..5fef87e
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ErrorPageDTOBuilder.java
@@ -0,0 +1,51 @@
+/*
+ * 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.dto;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime.ErrorPage;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+
+final class ErrorPageDTOBuilder extends BaseServletDTOBuilder<ErrorPage, ErrorPageDTO>
+{
+    private static final String[] STRING_ARRAY = new String[0];
+
+    @Override
+    ErrorPageDTO buildDTO(ErrorPage errorPage, long servletConextId)
+    {
+        ErrorPageDTO errorPageDTO = new ErrorPageDTO();
+        setBaseFields(errorPageDTO, errorPage.getServletHandler(), servletConextId);
+        errorPageDTO.errorCodes = getErrorCodes(errorPage.getErrorCodes());
+        errorPageDTO.exceptions = errorPage.getExceptions().toArray(STRING_ARRAY);
+        return errorPageDTO;
+    }
+
+    private long[] getErrorCodes(Collection<Integer> errorCodes)
+    {
+        Iterator<Integer> itr = errorCodes.iterator();
+        long[] result = new long[errorCodes.size()];
+        for (int i = 0; i < result.length; i++)
+        {
+            result[i] = (long) itr.next();
+        }
+        return result;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FilterDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FilterDTOBuilder.java
new file mode 100644
index 0000000..6c1f9a4
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/FilterDTOBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * 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.dto;
+
+import javax.servlet.DispatcherType;
+
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+
+final class FilterDTOBuilder extends BaseDTOBuilder<FilterHandler, FilterDTO>
+{
+    private static final String[] STRING_ARRAY = new String[0];
+
+    @Override
+    FilterDTO buildDTO(FilterHandler filterHandler, long servletContextId)
+    {
+        FilterInfo info = filterHandler.getFilterInfo();
+
+        FilterDTO filterDTO = new FilterDTO();
+        filterDTO.asyncSupported = info.isAsyncSupported();
+        filterDTO.dispatcher = getNames(info.getDispatcher());
+        filterDTO.initParams = copyWithDefault(info.getInitParameters());
+        filterDTO.name = info.getName();
+        filterDTO.patterns = copyWithDefault(info.getPatterns(), STRING_ARRAY);
+        filterDTO.regexs = copyWithDefault(info.getRegexs(), STRING_ARRAY);
+        filterDTO.serviceId = filterHandler.getFilterInfo().getServiceId();
+        filterDTO.servletContextId = servletContextId;
+        filterDTO.servletNames = copyWithDefault(info.getServletNames(), STRING_ARRAY);
+
+        return filterDTO;
+    }
+
+    private String[] getNames(DispatcherType[] dispatcher)
+    {
+        if (dispatcher == null)
+        {
+            return STRING_ARRAY;
+        }
+
+        String[] names = new String[dispatcher.length];
+        for (int i = 0; i < dispatcher.length; i++)
+        {
+            names[i] = dispatcher[i].name();
+        }
+        return names;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ListenerDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ListenerDTOBuilder.java
new file mode 100644
index 0000000..34d2989
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ListenerDTOBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dto;
+
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.runtime.dto.ListenerDTO;
+
+final class ListenerDTOBuilder extends BaseDTOBuilder<ServiceReference<?>, ListenerDTO>
+{
+    @Override
+    ListenerDTO buildDTO(ServiceReference<?> listenerRef, long servletContextId)
+    {
+        ListenerDTO listenerDTO = new ListenerDTO();
+        listenerDTO.serviceId = (Long) listenerRef.getProperty(Constants.SERVICE_ID);
+        listenerDTO.servletContextId = servletContextId;
+        // TODO Is this the desired value?
+        listenerDTO.types = (String[]) listenerRef.getProperty(Constants.OBJECTCLASS);
+        return listenerDTO;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ResourceDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ResourceDTOBuilder.java
new file mode 100644
index 0000000..25a40da
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ResourceDTOBuilder.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dto;
+
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+
+//TODO
+final class ResourceDTOBuilder extends BaseDTOBuilder<ServletHandler, ResourceDTO>
+{
+    @Override
+    ResourceDTO buildDTO(ServletHandler handler, long servletContextId)
+    {
+        ResourceDTO resourceDTO = new ResourceDTO();
+        resourceDTO.patterns = null;
+        resourceDTO.prefix = null;
+        resourceDTO.serviceId = handler.getServletInfo().getServiceId();
+        resourceDTO.servletContextId = servletContextId;
+        return resourceDTO;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilder.java
new file mode 100644
index 0000000..80792e6
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilder.java
@@ -0,0 +1,125 @@
+/*
+ * 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.dto;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime.ErrorPage;
+import org.apache.felix.http.base.internal.runtime.RegistryRuntime;
+import org.apache.felix.http.base.internal.whiteboard.ContextHandler;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.ListenerDTO;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+import org.osgi.service.http.runtime.dto.RuntimeDTO;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+
+public final class RuntimeDTOBuilder
+{
+    private static final ServletContextDTO[] CONTEXT_DTO_ARRAY = new ServletContextDTO[0];
+
+    private static final ServletDTOBuilder SERVLET_DTO_BUILDER = new ServletDTOBuilder();
+    private static final ResourceDTOBuilder RESOURCE_DTO_BUILDER = new ResourceDTOBuilder();
+    private static final FilterDTOBuilder FILTER_DTO_BUILDER = new FilterDTOBuilder();
+    private static final ErrorPageDTOBuilder ERROR_PAGE_DTO_BUILDER = new ErrorPageDTOBuilder();
+    private static final ListenerDTOBuilder LISTENER_DTO_BUILDER = new ListenerDTOBuilder();
+
+    private final RegistryRuntime registry;
+    private final Map<String, Object> serviceProperties;
+
+    public RuntimeDTOBuilder(RegistryRuntime registry, Map<String, Object> serviceProperties)
+    {
+        this.registry = registry;
+        this.serviceProperties = serviceProperties;
+    }
+
+    public RuntimeDTO build()
+    {
+        RuntimeDTO runtimeDTO = new RuntimeDTO();
+        runtimeDTO.attributes = createAttributes();
+        //TODO <**
+        runtimeDTO.failedErrorPageDTOs = null;
+        runtimeDTO.failedFilterDTOs = null;
+        runtimeDTO.failedListenerDTOs = null;
+        runtimeDTO.failedResourceDTOs = null;
+        runtimeDTO.failedServletContextDTOs = null;
+        runtimeDTO.failedServletDTOs = null;
+        //**>
+        runtimeDTO.servletContextDTOs = createContextDTOs();
+        return runtimeDTO;
+    }
+
+    private Map<String, String> createAttributes()
+    {
+        Map<String, String> attributes = new HashMap<String, String>();
+        for (Map.Entry<String, Object> entry : this.serviceProperties.entrySet())
+        {
+            attributes.put(entry.getKey(), entry.getValue().toString());
+        }
+        return attributes;
+    }
+
+    private ServletContextDTO[] createContextDTOs()
+    {
+        List<ServletContextDTO> contextDTOs = new ArrayList<ServletContextDTO>();
+        for (ContextHandler context : registry.getContexts())
+        {
+            contextDTOs.add(createContextDTO(context,
+                    registry.getHandlerRuntime(context),
+                    registry.getListenerRuntime(context)));
+        }
+        return contextDTOs.toArray(CONTEXT_DTO_ARRAY);
+    }
+
+    private ServletContextDTO createContextDTO(ContextHandler context,
+            HandlerRuntime handlerRuntime,
+            Collection<ServiceReference<?>> listenerRefs)
+    {
+        Collection<ServletHandler> servletHandlers = handlerRuntime.getServletHandlers();
+        //TODO missing functionality
+        Collection<ServletHandler> resourceHandlers = Collections.emptyList();
+        Collection<FilterHandler> filterHandlers = handlerRuntime.getFilterHandlers();
+        Collection<ErrorPage> errorPages = handlerRuntime.getErrorPages();
+        long servletContextId = handlerRuntime.getServiceId();
+
+        Collection<ServletDTO> servletDTOs = SERVLET_DTO_BUILDER.build(servletHandlers, servletContextId);
+        Collection<ResourceDTO> resourcesDTOs = RESOURCE_DTO_BUILDER.build(resourceHandlers, servletContextId);
+        Collection<FilterDTO> filtersDTOs = FILTER_DTO_BUILDER.build(filterHandlers, servletContextId);
+        Collection<ErrorPageDTO> errorsDTOs = ERROR_PAGE_DTO_BUILDER.build(errorPages, servletContextId);
+        Collection<ListenerDTO> listenersDTOs = LISTENER_DTO_BUILDER.build(listenerRefs, servletContextId);
+
+        return new ServletContextDTOBuilder(context,
+                    servletDTOs,
+                    resourcesDTOs,
+                    filtersDTOs,
+                    errorsDTOs,
+                    listenersDTOs)
+                .build();
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletContextDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletContextDTOBuilder.java
new file mode 100644
index 0000000..5ee0fa1
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletContextDTOBuilder.java
@@ -0,0 +1,138 @@
+/*
+ * 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.dto;
+
+import static java.util.Collections.list;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+
+import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
+import org.apache.felix.http.base.internal.whiteboard.ContextHandler;
+import org.osgi.dto.DTO;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.ListenerDTO;
+import org.osgi.service.http.runtime.dto.ResourceDTO;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+
+final class ServletContextDTOBuilder
+{
+    private static final ServletDTO[] SERVLET_DTO_ARRAY = new ServletDTO[0];
+    private static final ResourceDTO[] RESOURCE_DTO_ARRAY = new ResourceDTO[0];
+    private static final FilterDTO[] FILTER_DTO_ARRAY = new FilterDTO[0];
+    private static final ErrorPageDTO[] ERROR_PAGE_DTO_ARRAY = new ErrorPageDTO[0];
+    private static final ListenerDTO[] LISTENER_DTO_ARRAY = new ListenerDTO[0];
+
+    private final ContextHandler contextHandler;
+    private final ServletDTO[] servletDTOs;
+    private final ResourceDTO[] resourceDTOs;
+    private final FilterDTO[] filterDTOs;
+    private final ErrorPageDTO[] errorPageDTOs;
+    private final ListenerDTO[] listenerDTOs;
+
+    public ServletContextDTOBuilder(ContextHandler contextHandler,
+            Collection<ServletDTO> servletDTOs,
+            Collection<ResourceDTO> resourceDTOs,
+            Collection<FilterDTO> filterDTOs,
+            Collection<ErrorPageDTO> errorPageDTOs,
+            Collection<ListenerDTO> listenerDTOs)
+    {
+        this.contextHandler = contextHandler;
+        this.servletDTOs = servletDTOs != null ?
+                servletDTOs.toArray(SERVLET_DTO_ARRAY) : SERVLET_DTO_ARRAY;
+        this.resourceDTOs = resourceDTOs != null ?
+                resourceDTOs.toArray(RESOURCE_DTO_ARRAY) : RESOURCE_DTO_ARRAY;
+        this.filterDTOs = filterDTOs != null ?
+                filterDTOs.toArray(FILTER_DTO_ARRAY) : FILTER_DTO_ARRAY;
+        this.errorPageDTOs = errorPageDTOs != null ?
+                errorPageDTOs.toArray(ERROR_PAGE_DTO_ARRAY) : ERROR_PAGE_DTO_ARRAY;
+        this.listenerDTOs = listenerDTOs != null ?
+                listenerDTOs.toArray(LISTENER_DTO_ARRAY) : LISTENER_DTO_ARRAY;
+    }
+
+    ServletContextDTO build()
+    {
+        ServletContext context  = contextHandler.getSharedContext();
+        ServletContextHelperInfo contextInfo = contextHandler.getContextInfo();
+        long contextId = contextInfo.getServiceId();
+
+        ServletContextDTO contextDTO = new ServletContextDTO();
+        contextDTO.attributes = getAttributes(context);
+        contextDTO.contextName = context.getServletContextName();
+        contextDTO.contextPath = context.getContextPath();
+        contextDTO.errorPageDTOs = errorPageDTOs;
+        contextDTO.filterDTOs = filterDTOs;
+        contextDTO.initParams = getInitParameters(context);
+        contextDTO.listenerDTOs = listenerDTOs;
+        contextDTO.name = contextId >= 0 ? contextInfo.getName() : null;
+        contextDTO.resourceDTOs = resourceDTOs;
+        contextDTO.servletDTOs = servletDTOs;
+        contextDTO.serviceId = contextId;
+        return contextDTO;
+    }
+
+    private Map<String, Object> getAttributes(ServletContext context)
+    {
+        Map<String, Object> attributes = new HashMap<String, Object>();
+        for (String name : list(context.getAttributeNames()))
+        {
+            Object attribute = context.getAttribute(name);
+            if (isSupportedType(attribute))
+            {
+                attributes.put(name, attribute);
+            }
+        }
+        return attributes;
+    }
+
+    private boolean isSupportedType(Object attribute)
+    {
+        Class<?> attributeClass = attribute.getClass();
+        Class<?> type = !attributeClass.isArray() ?
+                attributeClass : attributeClass.getComponentType();
+
+        return Number.class.isAssignableFrom(type) ||
+                Boolean.class.isAssignableFrom(type) ||
+                String.class.isAssignableFrom(type) ||
+                DTO.class.isAssignableFrom(type) ||
+                boolean.class.isAssignableFrom(type) ||
+                int.class.isAssignableFrom(type) ||
+                double.class.isAssignableFrom(type) ||
+                float.class.isAssignableFrom(type) ||
+                long.class.isAssignableFrom(type) ||
+                short.class.isAssignableFrom(type) ||
+                byte.class.isAssignableFrom(type) ||
+                char.class.isAssignableFrom(type);
+    }
+
+    private Map<String, String> getInitParameters(ServletContext context)
+    {
+        Map<String, String> initParameters = new HashMap<String, String>();
+        for (String name : list(context.getInitParameterNames()))
+        {
+            initParameters.put(name, context.getInitParameter(name));
+        }
+        return initParameters;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletDTOBuilder.java
new file mode 100644
index 0000000..0444837
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/ServletDTOBuilder.java
@@ -0,0 +1,46 @@
+/*
+ * 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.dto;
+
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+
+final class ServletDTOBuilder extends BaseServletDTOBuilder<ServletHandler, ServletDTO>
+{
+    @Override
+    ServletDTO buildDTO(ServletHandler servletHandler, long servletContextId)
+    {
+        ServletInfo info = servletHandler.getServletInfo();
+
+        ServletDTO servletDTO = new ServletDTO();
+        setBaseFields(servletDTO, servletHandler, servletContextId);
+        servletDTO.patterns = copyWithDefault(checkNotEmpty(info.getPatterns()), null);
+        return servletDTO;
+    }
+
+    private String[] checkNotEmpty(String[] patterns)
+    {
+        if (patterns == null || patterns.length == 0)
+        {
+            throw new IllegalArgumentException("No patterns specified in servlet info");
+        }
+        return patterns;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
index 5021732..bb5a128 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceImpl.java
@@ -51,22 +51,26 @@
     private final Set<Servlet> localServlets = new HashSet<Servlet>();
     private final Set<Filter> localFilters = new HashSet<Filter>();
     private final ServletContextManager contextManager;
-
     private final Map<String, ServletHandler> aliasMap = new HashMap<String, ServletHandler>();
 
-    public HttpServiceImpl(final Bundle bundle,
-            final ServletContext context,
-            final PerContextHandlerRegistry handlerRegistry,
-            final ServletContextAttributeListener servletAttributeListener,
-            final boolean sharedContextAttributes,
-            final ServletRequestListener reqListener,
-            final ServletRequestAttributeListener reqAttrListener)
+    public HttpServiceImpl(Bundle bundle, ServletContext context, PerContextHandlerRegistry handlerRegistry, ServletContextAttributeListener servletAttributeListener, boolean sharedContextAttributes, ServletRequestListener reqListener, ServletRequestAttributeListener reqAttrListener)
     {
+        if (bundle == null)
+        {
+            throw new IllegalArgumentException("Bundle cannot be null!");
+        }
+        if (context == null)
+        {
+            throw new IllegalArgumentException("Context cannot be null!");
+        }
+        if (handlerRegistry == null)
+        {
+            throw new IllegalArgumentException("HandlerRegistry cannot be null!");
+        }
+
         this.bundle = bundle;
         this.handlerRegistry = handlerRegistry;
-        this.contextManager = new ServletContextManager(this.bundle, context,
-                servletAttributeListener, sharedContextAttributes,
-                reqListener, reqAttrListener);
+        this.contextManager = new ServletContextManager(this.bundle, context, servletAttributeListener, sharedContextAttributes, reqListener, reqAttrListener);
     }
 
     @Override
@@ -87,7 +91,7 @@
         }
 
         final Map<String, String> paramMap = new HashMap<String, String>();
-        if ( initParams != null && initParams.size() > 0 )
+        if (initParams != null && initParams.size() > 0)
         {
             Enumeration e = initParams.keys();
             while (e.hasMoreElements())
@@ -102,9 +106,8 @@
             }
         }
 
-        final FilterInfo filterInfo = new FilterInfo(String.format("%s_%d", filter.getClass(), this.hashCode()),
-                pattern, ranking, paramMap);
-        if ( !filterInfo.isValid() )
+        final FilterInfo filterInfo = new FilterInfo(String.format("%s_%d", filter.getClass(), this.hashCode()), pattern, ranking, paramMap);
+        if (!filterInfo.isValid())
         {
             throw new ServletException("Invalid registration information for filter.");
         }
@@ -112,9 +115,12 @@
         final ExtServletContext httpContext = getServletContext(context);
 
         FilterHandler handler = new FilterHandler(null, httpContext, filter, filterInfo);
-        try {
+        try
+        {
             this.handlerRegistry.addFilter(handler);
-        } catch (ServletException e) {
+        }
+        catch (ServletException e)
+        {
             // TODO create failure DTO
         }
         this.localFilters.add(filter);
@@ -148,8 +154,7 @@
      * @see org.osgi.service.http.HttpService#registerServlet(java.lang.String, javax.servlet.Servlet, java.util.Dictionary, org.osgi.service.http.HttpContext)
      */
     @Override
-    public void registerServlet(String alias, Servlet servlet, Dictionary initParams, HttpContext context)
-    throws ServletException, NamespaceException
+    public void registerServlet(String alias, Servlet servlet, Dictionary initParams, HttpContext context) throws ServletException, NamespaceException
     {
         if (servlet == null)
         {
@@ -161,7 +166,7 @@
         }
 
         final Map<String, String> paramMap = new HashMap<String, String>();
-        if ( initParams != null && initParams.size() > 0 )
+        if (initParams != null && initParams.size() > 0)
         {
             Enumeration e = initParams.keys();
             while (e.hasMoreElements())
@@ -176,25 +181,21 @@
             }
         }
 
-        final ServletInfo servletInfo = new ServletInfo(String.format("%s_%d",servlet.getClass(), this.hashCode()),
-                alias, 0, paramMap);
+        final ServletInfo servletInfo = new ServletInfo(String.format("%s_%d", servlet.getClass(), this.hashCode()), alias, 0, paramMap);
         final ExtServletContext httpContext = getServletContext(context);
 
-        final ServletHandler handler = new ServletHandler(null,
-                httpContext,
-                servletInfo,
-                servlet);
+        final ServletHandler handler = new ServletHandler(null, httpContext, servletInfo, servlet);
 
-        synchronized ( this.aliasMap )
+        synchronized (this.aliasMap)
         {
-        	if ( this.aliasMap.containsKey(alias) )
-        	{
-        	    throw new NamespaceException("Alias " + alias + " is already in use.");
-        	}
-        	if ( this.localServlets.contains(servlet) )
-        	{
+            if (this.aliasMap.containsKey(alias))
+            {
+                throw new NamespaceException("Alias " + alias + " is already in use.");
+            }
+            if (this.localServlets.contains(servlet))
+            {
                 throw new ServletException("Servlet instance " + handler.getName() + " already registered");
-        	}
+            }
 
             this.handlerRegistry.addServlet(handler);
 
@@ -209,19 +210,19 @@
     @Override
     public void unregister(final String alias)
     {
-        synchronized ( this.aliasMap )
+        synchronized (this.aliasMap)
         {
-		    final ServletHandler handler = this.aliasMap.remove(alias);
-	        if ( handler == null )
-	        {
-	        	throw new IllegalArgumentException("Nothing registered at " + alias);
-	        }
-	        final Servlet servlet = this.handlerRegistry.removeServlet(handler.getServletInfo(), true);
-	        if (servlet != null)
-	        {
-	        	this.localServlets.remove(servlet);
-	        }
-		}
+            final ServletHandler handler = this.aliasMap.remove(alias);
+            if (handler == null)
+            {
+                throw new IllegalArgumentException("Nothing registered at " + alias);
+            }
+            final Servlet servlet = this.handlerRegistry.removeServlet(handler.getServletInfo(), true);
+            if (servlet != null)
+            {
+                this.localServlets.remove(servlet);
+            }
+        }
     }
 
     public void unregisterAll()
@@ -261,23 +262,23 @@
 
     private void unregisterServlet(final Servlet servlet, final boolean destroy)
     {
-        if ( servlet != null )
+        if (servlet != null)
         {
             this.handlerRegistry.removeServlet(servlet, destroy);
-            synchronized ( this.aliasMap )
+            synchronized (this.aliasMap)
             {
-            	final Iterator<Map.Entry<String, ServletHandler>> i = this.aliasMap.entrySet().iterator();
-            	while ( i.hasNext() )
-            	{
-            		final Map.Entry<String, ServletHandler> entry = i.next();
-            		if ( entry.getValue().getServlet() == servlet )
-            		{
-            			i.remove();
-            			break;
-            		}
+                final Iterator<Map.Entry<String, ServletHandler>> i = this.aliasMap.entrySet().iterator();
+                while (i.hasNext())
+                {
+                    final Map.Entry<String, ServletHandler> entry = i.next();
+                    if (entry.getValue().getServlet() == servlet)
+                    {
+                        i.remove();
+                        break;
+                    }
 
-            	}
-            	this.localServlets.remove(servlet);
+                }
+                this.localServlets.remove(servlet);
             }
         }
     }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceRuntimeImpl.java b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceRuntimeImpl.java
new file mode 100644
index 0000000..096ae72
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/service/HttpServiceRuntimeImpl.java
@@ -0,0 +1,79 @@
+/*
+ * 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 static java.util.Collections.list;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.runtime.RegistryRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.RuntimeDTOBuilder;
+import org.apache.felix.http.base.internal.whiteboard.ServletContextHelperManager;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+import org.osgi.service.http.runtime.dto.RuntimeDTO;
+
+public final class HttpServiceRuntimeImpl implements HttpServiceRuntime
+{
+    private final Hashtable<String, Object> attributes = new Hashtable<String, Object>();
+
+    private final HandlerRegistry registry;
+    private final ServletContextHelperManager contextManager;
+
+
+    public HttpServiceRuntimeImpl(HandlerRegistry registry,
+            ServletContextHelperManager contextManager)
+    {
+        this.registry = registry;
+        this.contextManager = contextManager;
+    }
+
+    public synchronized RuntimeDTO getRuntimeDTO()
+    {
+        RegistryRuntime runtime = contextManager.getRuntime(registry);
+        RuntimeDTOBuilder runtimeDTOBuilder = new RuntimeDTOBuilder(runtime, attributes);
+        return runtimeDTOBuilder.build();
+    }
+
+    @Override
+    public RequestInfoDTO calculateRequestInfoDTO(String path)
+    {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public synchronized void setAttribute(String name, Object value)
+    {
+        attributes.put(name, value);
+    }
+
+    public synchronized void setAllAttributes(Dictionary<String, Object> attributes)
+    {
+        this.attributes.clear();
+        for (String key :list(attributes.keys()))
+        {
+            this.attributes.put(key, attributes.get(key));
+        }
+    }
+
+    public synchronized Dictionary<String, Object> getAttributes()
+    {
+        return attributes;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/CollectionUtils.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/CollectionUtils.java
new file mode 100644
index 0000000..2f3b45f
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/CollectionUtils.java
@@ -0,0 +1,33 @@
+/*
+ * 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.util;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class CollectionUtils {
+	public static <T extends Comparable<?>> Set<T> union(Collection<? extends T>... collections)
+    {
+        Set<T> union = new TreeSet<T>();
+        for (Collection<? extends T> collection : collections)
+        {
+            union.addAll(collection);
+        }
+        return union;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/util/InternalIdFactory.java b/http/base/src/main/java/org/apache/felix/http/base/internal/util/InternalIdFactory.java
new file mode 100644
index 0000000..4d432d5
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/util/InternalIdFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.util;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Provides service ids for services not provided through the service registry.
+ * <p>
+ * All provided ids are unique negative {@code long}s.
+ */
+public enum InternalIdFactory
+{
+    INSTANCE;
+
+    private final AtomicLong idCounter = new AtomicLong();
+
+    public long next()
+    {
+        return idCounter.decrementAndGet();
+    }
+}
\ No newline at end of file
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 15c344a..344b4b7 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,37 +18,19 @@
 
 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;
-import javax.servlet.ServletContextAttributeEvent;
 import javax.servlet.ServletContextAttributeListener;
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-import javax.servlet.ServletRequestAttributeEvent;
 import javax.servlet.ServletRequestAttributeListener;
-import javax.servlet.ServletRequestEvent;
 import javax.servlet.ServletRequestListener;
 import javax.servlet.http.HttpSessionAttributeListener;
-import javax.servlet.http.HttpSessionBindingEvent;
-import javax.servlet.http.HttpSessionEvent;
 import javax.servlet.http.HttpSessionListener;
 
 import org.apache.felix.http.base.internal.context.ExtServletContext;
-import org.apache.felix.http.base.internal.runtime.HttpSessionAttributeListenerInfo;
-import org.apache.felix.http.base.internal.runtime.HttpSessionListenerInfo;
-import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
-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;
 import org.osgi.service.http.context.ServletContextHelper;
 
 public final class ContextHandler implements Comparable<ContextHandler>
@@ -65,53 +47,41 @@
     /** 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>();
+    private final HttpSessionListener sessionListener;
+    private final HttpSessionAttributeListener sessionAttributeListener;
+    private final ServletRequestListener requestListener;
+    private final ServletRequestAttributeListener requestAttributeListener;
 
-    /** Servlet context attribute listeners. */
-    private final Map<ServiceReference<ServletContextAttributeListener>, ServletContextAttributeListener> contextAttributeListeners =
-                      new ConcurrentSkipListMap<ServiceReference<ServletContextAttributeListener>, ServletContextAttributeListener>();
+    public ContextHandler(ServletContextHelperInfo info,
+            ServletContext webContext,
+            PerContextEventListener eventListener,
+            Bundle bundle)
+    {
+        this(info, webContext, eventListener, eventListener, eventListener, eventListener, eventListener, bundle);
+    }
 
-    /** Session attribute listeners. */
-    private final Map<ServiceReference<HttpSessionAttributeListener>, HttpSessionAttributeListener> sessionAttributeListeners =
-            new ConcurrentSkipListMap<ServiceReference<HttpSessionAttributeListener>, HttpSessionAttributeListener>();
-
-    /** Session listeners. */
-    private final Map<ServiceReference<HttpSessionListener>, HttpSessionListener> sessionListeners =
-            new ConcurrentSkipListMap<ServiceReference<HttpSessionListener>, HttpSessionListener>();
-
-    /** Request listeners. */
-    private final Map<ServiceReference<ServletRequestListener>, ServletRequestListener> requestListeners =
-            new ConcurrentSkipListMap<ServiceReference<ServletRequestListener>, ServletRequestListener>();
-
-    /** Request attribute listeners. */
-    private final Map<ServiceReference<ServletRequestAttributeListener>, ServletRequestAttributeListener> requestAttributeListeners =
-            new ConcurrentSkipListMap<ServiceReference<ServletRequestAttributeListener>, ServletRequestAttributeListener>();
-
-    /** All whiteboard services - servlets, filters, resources. */
-    private final Set<WhiteboardServiceInfo<?>> whiteboardServices = new ConcurrentSkipListSet<WhiteboardServiceInfo<?>>();
-
-    /**
-     * Create new handler.
-     * @param info
-     * @param webContext
-     */
-    public ContextHandler(final ServletContextHelperInfo info,
-            final ServletContext webContext,
-            final Bundle bundle)
+    public ContextHandler(ServletContextHelperInfo info,
+            ServletContext webContext,
+            ServletContextAttributeListener servletContextAttributeListener,
+            HttpSessionListener sessionListener,
+            HttpSessionAttributeListener sessionAttributeListener,
+            ServletRequestListener requestListener,
+            ServletRequestAttributeListener requestAttributeListener,
+            Bundle bundle)
     {
         this.info = info;
+        this.sessionListener = sessionListener;
+        this.sessionAttributeListener = sessionAttributeListener;
+        this.requestListener = requestListener;
+        this.requestAttributeListener = requestAttributeListener;
         this.bundle = bundle;
         this.sharedContext = new SharedServletContextImpl(webContext,
                 info.getName(),
                 info.getPath(),
                 info.getInitParameters(),
-                getContextAttributeListener());
+                servletContextAttributeListener);
     }
 
-    /**
-     * Get the context info
-     */
     public ServletContextHelperInfo getContextInfo()
     {
         return this.info;
@@ -131,36 +101,11 @@
     public void deactivate()
     {
         this.ungetServletContext(bundle);
-        this.whiteboardServices.clear();
     }
 
-    public void initialized(@Nonnull final ServletContextListenerInfo listenerInfo)
+    public ServletContext getSharedContext()
     {
-        final ServletContextListener listener = listenerInfo.getService(bundle);
-        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(@Nonnull 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));
-            // call unget twice, once for the call in initialized and once for the call in this method(!)
-            this.ungetServletContext(listenerInfo.getServiceReference().getBundle());
-            this.ungetServletContext(listenerInfo.getServiceReference().getBundle());
-            listenerInfo.ungetService(bundle, listener);
-        }
+        return sharedContext;
     }
 
     public ExtServletContext getServletContext(@Nonnull final Bundle bundle)
@@ -180,10 +125,10 @@
                     holder.servletContext = new PerBundleServletContextImpl(bundle,
                             this.sharedContext,
                             holder.servletContextHelper,
-                            this.getSessionListener(),
-                            this.getSessionAttributeListener(),
-                            this.getServletRequestListener(),
-                            this.getServletRequestAttributeListener());
+                            this.sessionListener,
+                            this.sessionAttributeListener,
+                            this.requestListener,
+                            this.requestAttributeListener);
                     this.perBundleContextMap.put(key, holder);
                 }
                 // TODO - check null for so
@@ -226,289 +171,10 @@
         }
     }
 
-    /**
-     * Add servlet context attribute listener
-     * @param info
-     */
-    public void addListener(@Nonnull final ServletContextAttributeListenerInfo info)
-    {
-        final  ServletContextAttributeListener service = info.getService(bundle);
-        if ( service != null )
-        {
-            this.contextAttributeListeners.put(info.getServiceReference(), service);
-        }
-    }
-
-    /**
-     * Remove servlet context attribute listener
-     * @param info
-     */
-    public void removeListener(@Nonnull final ServletContextAttributeListenerInfo info)
-    {
-        final  ServletContextAttributeListener service = this.contextAttributeListeners.remove(info.getServiceReference());
-        if ( service != null )
-        {
-            info.ungetService(bundle, service);
-        }
-    }
-
-    /**
-     * Add session attribute listener
-     * @param info
-     */
-    public void addListener(@Nonnull final HttpSessionAttributeListenerInfo info)
-    {
-        final  HttpSessionAttributeListener service = info.getService(bundle);
-        if ( service != null )
-        {
-            this.sessionAttributeListeners.put(info.getServiceReference(), service);
-        }
-    }
-
-    /**
-     * Remove session attribute listener
-     * @param info
-     */
-    public void removeListener(@Nonnull final HttpSessionAttributeListenerInfo info)
-    {
-        final  HttpSessionAttributeListener service = this.sessionAttributeListeners.remove(info.getServiceReference());
-        if ( service != null )
-        {
-            info.ungetService(bundle, service);
-        }
-    }
-
-    /**
-     * Add session listener
-     * @param info
-     */
-    public void addListener(@Nonnull final HttpSessionListenerInfo info)
-    {
-        final  HttpSessionListener service = info.getService(bundle);
-        if ( service != null )
-        {
-            this.sessionListeners.put(info.getServiceReference(), service);
-        }
-    }
-
-    /**
-     * Remove session listener
-     * @param info
-     */
-    public void removeListener(@Nonnull final HttpSessionListenerInfo info)
-    {
-        final  HttpSessionListener service = this.sessionListeners.remove(info.getServiceReference());
-        if ( service != null )
-        {
-            info.ungetService(bundle, service);
-        }
-    }
-
-    /**
-     * Add request listener
-     * @param info
-     */
-    public void addListener(@Nonnull final ServletRequestListenerInfo info)
-    {
-        final  ServletRequestListener service = info.getService(bundle);
-        if ( service != null )
-        {
-            this.requestListeners.put(info.getServiceReference(), service);
-        }
-    }
-
-    /**
-     * Remove request listener
-     * @param info
-     */
-    public void removeListener(@Nonnull final ServletRequestListenerInfo info)
-    {
-        final ServletRequestListener service = this.requestListeners.remove(info.getServiceReference());
-        if ( service != null )
-        {
-            info.ungetService(bundle, service);
-        }
-    }
-
-    /**
-     * Add request attribute listener
-     * @param info
-     */
-    public void addListener(@Nonnull final ServletRequestAttributeListenerInfo info)
-    {
-        final  ServletRequestAttributeListener service = info.getService(bundle);
-        if ( service != null )
-        {
-            this.requestAttributeListeners.put(info.getServiceReference(), service);
-        }
-    }
-
-    /**
-     * Remove request attribute listener
-     * @param info
-     */
-    public void removeListener(@Nonnull final ServletRequestAttributeListenerInfo info)
-    {
-        final ServletRequestAttributeListener service = this.requestAttributeListeners.remove(info.getServiceReference());
-        if ( service != null )
-        {
-            info.ungetService(bundle, service);
-        }
-    }
-
     private static final class ContextHolder
     {
         public long counter;
         public ExtServletContext servletContext;
         public ServletContextHelper servletContextHelper;
     }
-
-    public HttpSessionAttributeListener getSessionAttributeListener()
-    {
-        return new HttpSessionAttributeListener() {
-
-            @Override
-            public void attributeReplaced(final HttpSessionBindingEvent event) {
-                for(final HttpSessionAttributeListener l : sessionAttributeListeners.values())
-                {
-                    l.attributeReplaced(event);
-                }
-            }
-
-            @Override
-            public void attributeRemoved(final HttpSessionBindingEvent event) {
-                for(final HttpSessionAttributeListener l : sessionAttributeListeners.values())
-                {
-                    l.attributeReplaced(event);
-                }
-            }
-
-            @Override
-            public void attributeAdded(final HttpSessionBindingEvent event) {
-                for(final HttpSessionAttributeListener l : sessionAttributeListeners.values())
-                {
-                    l.attributeReplaced(event);
-                }
-            }
-        };
-    }
-
-    private ServletContextAttributeListener getContextAttributeListener()
-    {
-        return new ServletContextAttributeListener() {
-
-            @Override
-            public void attributeReplaced(final ServletContextAttributeEvent event) {
-                for(final ServletContextAttributeListener l : contextAttributeListeners.values())
-                {
-                    l.attributeReplaced(event);
-                }
-            }
-
-            @Override
-            public void attributeRemoved(final ServletContextAttributeEvent event) {
-                for(final ServletContextAttributeListener l : contextAttributeListeners.values())
-                {
-                    l.attributeReplaced(event);
-                }
-            }
-
-            @Override
-            public void attributeAdded(final ServletContextAttributeEvent event) {
-                for(final ServletContextAttributeListener l : contextAttributeListeners.values())
-                {
-                    l.attributeReplaced(event);
-                }
-            }
-        };
-    }
-
-    public HttpSessionListener getSessionListener()
-    {
-        return new HttpSessionListener() {
-
-            @Override
-            public void sessionCreated(final HttpSessionEvent se) {
-                for(final HttpSessionListener l : sessionListeners.values())
-                {
-                    l.sessionCreated(se);
-                }
-            }
-
-            @Override
-            public void sessionDestroyed(final HttpSessionEvent se) {
-                for(final HttpSessionListener l : sessionListeners.values())
-                {
-                    l.sessionDestroyed(se);
-                }
-            }
-        };
-    }
-
-    private ServletRequestListener getServletRequestListener()
-    {
-        return new ServletRequestListener() {
-
-            @Override
-            public void requestDestroyed(final ServletRequestEvent sre) {
-                for(final ServletRequestListener l : requestListeners.values())
-                {
-                    l.requestDestroyed(sre);
-                }
-            }
-
-            @Override
-            public void requestInitialized(final ServletRequestEvent sre) {
-                for(final ServletRequestListener l : requestListeners.values())
-                {
-                    l.requestInitialized(sre);
-                }
-            }
-        };
-    }
-
-    private ServletRequestAttributeListener getServletRequestAttributeListener()
-    {
-        return new ServletRequestAttributeListener() {
-
-            @Override
-            public void attributeAdded(final ServletRequestAttributeEvent srae) {
-                for(final ServletRequestAttributeListener l : requestAttributeListeners.values())
-                {
-                    l.attributeAdded(srae);
-                }
-            }
-
-            @Override
-            public void attributeRemoved(final ServletRequestAttributeEvent srae) {
-                for(final ServletRequestAttributeListener l : requestAttributeListeners.values())
-                {
-                    l.attributeRemoved(srae);
-                }
-            }
-
-            @Override
-            public void attributeReplaced(final ServletRequestAttributeEvent srae) {
-                for(final ServletRequestAttributeListener l : requestAttributeListeners.values())
-                {
-                    l.attributeReplaced(srae);
-                }
-            }
-        };
-    }
-
-    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/ListenerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ListenerRegistry.java
new file mode 100644
index 0000000..43d26c2
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ListenerRegistry.java
@@ -0,0 +1,103 @@
+/*
+ * 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.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.annotation.Nonnull;
+
+import org.apache.felix.http.base.internal.runtime.ListenerInfo;
+import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
+import org.apache.felix.http.base.internal.runtime.ServletContextListenerInfo;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public final class ListenerRegistry
+{
+    private final Map<ServletContextHelperInfo, PerContextEventListener> registriesByContext = new TreeMap<ServletContextHelperInfo, PerContextEventListener>();
+
+    private final Bundle bundle;
+
+    public ListenerRegistry(final Bundle bundle)
+    {
+        this.bundle = bundle;
+    }
+
+    public void initialized(@Nonnull final ServletContextListenerInfo listenerInfo,
+            ContextHandler contextHandler)
+    {
+        registriesByContext.get(contextHandler.getContextInfo()).initialized(listenerInfo, contextHandler);
+    }
+
+    public void destroyed(@Nonnull final ServletContextListenerInfo listenerInfo,
+            ContextHandler contextHandler)
+    {
+        registriesByContext.get(contextHandler.getContextInfo()).destroyed(listenerInfo, contextHandler);
+    }
+
+    public <T extends ListenerInfo<?>> void addListener(@Nonnull final T info,
+            final ContextHandler contextHandler)
+    {
+        getRegistryForContext(contextHandler).addListener(info);
+    }
+
+    public <T extends ListenerInfo<?>> void removeListener(@Nonnull final T info,
+           final ContextHandler contextHandler)
+    {
+        getRegistryForContext(contextHandler).removeListener(info);
+    }
+
+    private PerContextEventListener getRegistryForContext(ContextHandler contextHandler)
+    {
+        PerContextEventListener contextRegistry = registriesByContext.get(contextHandler.getContextInfo());
+        if (contextRegistry == null)
+        {
+            throw new IllegalArgumentException("ContextHandler " + contextHandler.getContextInfo().getName() + " is not registered");
+        }
+        return contextRegistry;
+    }
+
+    public PerContextEventListener addContext(ServletContextHelperInfo info)
+    {
+        if (registriesByContext.containsKey(info))
+        {
+            throw new IllegalArgumentException("Context with id " + info.getServiceId() + "is already registered");
+        }
+
+        PerContextEventListener contextRegistry = new PerContextEventListener(bundle);
+        registriesByContext.put(info, contextRegistry);
+        return contextRegistry;
+    }
+
+    public void removeContext(ServletContextHelperInfo info)
+    {
+        registriesByContext.remove(info);
+    }
+
+    public Map<Long, Collection<ServiceReference<?>>> getContextRuntimes()
+    {
+        Map<Long, Collection<ServiceReference<?>>> listenersByContext = new HashMap<Long, Collection<ServiceReference<?>>>();
+        for (ServletContextHelperInfo contextInfo : registriesByContext.keySet())
+        {
+            listenersByContext.put(contextInfo.getServiceId(), registriesByContext.get(contextInfo).getRuntime());
+        }
+        return listenersByContext;
+    }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerContextEventListener.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerContextEventListener.java
new file mode 100644
index 0000000..fd9e851
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/PerContextEventListener.java
@@ -0,0 +1,416 @@
+/*
+ * 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.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import javax.annotation.Nonnull;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+
+import org.apache.felix.http.base.internal.runtime.HttpSessionAttributeListenerInfo;
+import org.apache.felix.http.base.internal.runtime.HttpSessionListenerInfo;
+import org.apache.felix.http.base.internal.runtime.ListenerInfo;
+import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
+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.util.CollectionUtils;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public final class PerContextEventListener implements
+        HttpSessionListener,
+        HttpSessionAttributeListener,
+        ServletContextAttributeListener,
+        ServletRequestListener,
+        ServletRequestAttributeListener
+{
+    /** Servlet context listeners. */
+    private final Map<ServiceReference<ServletContextListener>, ServletContextListener> listeners = new HashMap<ServiceReference<ServletContextListener>, ServletContextListener>();
+
+    /** Servlet context attribute listeners. */
+    private final Map<ServiceReference<ServletContextAttributeListener>, ServletContextAttributeListener> contextAttributeListeners = new ConcurrentSkipListMap<ServiceReference<ServletContextAttributeListener>, ServletContextAttributeListener>();
+
+    /** Session attribute listeners. */
+    private final Map<ServiceReference<HttpSessionAttributeListener>, HttpSessionAttributeListener> sessionAttributeListeners = new ConcurrentSkipListMap<ServiceReference<HttpSessionAttributeListener>, HttpSessionAttributeListener>();
+
+    /** Session listeners. */
+    private final Map<ServiceReference<HttpSessionListener>, HttpSessionListener> sessionListeners = new ConcurrentSkipListMap<ServiceReference<HttpSessionListener>, HttpSessionListener>();
+
+    /** Request listeners. */
+    private final Map<ServiceReference<ServletRequestListener>, ServletRequestListener> requestListeners = new ConcurrentSkipListMap<ServiceReference<ServletRequestListener>, ServletRequestListener>();
+
+    /** Request attribute listeners. */
+    private final Map<ServiceReference<ServletRequestAttributeListener>, ServletRequestAttributeListener> requestAttributeListeners = new ConcurrentSkipListMap<ServiceReference<ServletRequestAttributeListener>, ServletRequestAttributeListener>();
+
+    private final Bundle bundle;
+
+    PerContextEventListener(final Bundle bundle)
+    {
+        this.bundle = bundle;
+    }
+
+    void initialized(@Nonnull final ServletContextListenerInfo listenerInfo,
+            ContextHandler contextHandler)
+    {
+        final ServletContextListener listener = listenerInfo.getService(bundle);
+        if (listener != null)
+        {
+            // no need to sync map - initialized is called in sync
+            this.listeners.put(listenerInfo.getServiceReference(), listener);
+
+            final ServletContext context = contextHandler
+                    .getServletContext(listenerInfo.getServiceReference()
+                            .getBundle());
+
+            listener.contextInitialized(new ServletContextEvent(context));
+        }
+    }
+
+    void destroyed(@Nonnull final ServletContextListenerInfo listenerInfo,
+            ContextHandler contextHandler)
+    {
+        final ServiceReference<ServletContextListener> listenerRef = listenerInfo
+                .getServiceReference();
+        final ServletContextListener listener = this.listeners
+                .remove(listenerRef);
+        if (listener != null)
+        {
+            final ServletContext context = contextHandler
+                    .getServletContext(listenerRef.getBundle());
+            listener.contextDestroyed(new ServletContextEvent(context));
+            // call unget twice, once for the call in initialized and once for
+            // the call in this method(!)
+            contextHandler.ungetServletContext(listenerRef.getBundle());
+            contextHandler.ungetServletContext(listenerRef.getBundle());
+            listenerInfo.ungetService(bundle, listener);
+        }
+    }
+
+    /**
+     * Add servlet context attribute listener
+     * 
+     * @param info
+     */
+    void addListener(@Nonnull final ServletContextAttributeListenerInfo info)
+    {
+        final ServletContextAttributeListener service = info.getService(bundle);
+        if (service != null)
+        {
+            this.contextAttributeListeners.put(info.getServiceReference(),
+                    service);
+        }
+    }
+
+    /**
+     * Remove servlet context attribute listener
+     * 
+     * @param info
+     */
+    void removeListener(@Nonnull final ServletContextAttributeListenerInfo info)
+    {
+        final ServletContextAttributeListener service = this.contextAttributeListeners
+                .remove(info.getServiceReference());
+        if (service != null)
+        {
+            info.ungetService(bundle, service);
+        }
+    }
+
+    /**
+     * Add session attribute listener
+     * 
+     * @param info
+     */
+    void addListener(@Nonnull final HttpSessionAttributeListenerInfo info)
+    {
+        final HttpSessionAttributeListener service = info.getService(bundle);
+        if (service != null)
+        {
+            this.sessionAttributeListeners.put(info.getServiceReference(),
+                    service);
+        }
+    }
+
+    /**
+     * Remove session attribute listener
+     * 
+     * @param info
+     */
+    void removeListener(@Nonnull final HttpSessionAttributeListenerInfo info)
+    {
+        final HttpSessionAttributeListener service = this.sessionAttributeListeners
+                .remove(info.getServiceReference());
+        if (service != null)
+        {
+            info.ungetService(bundle, service);
+        }
+    }
+
+    /**
+     * Add session listener
+     * 
+     * @param info
+     */
+    void addListener(@Nonnull final HttpSessionListenerInfo info)
+    {
+        final HttpSessionListener service = info.getService(bundle);
+        if (service != null)
+        {
+            this.sessionListeners.put(info.getServiceReference(), service);
+        }
+    }
+
+    /**
+     * Remove session listener
+     * 
+     * @param info
+     */
+    void removeListener(@Nonnull final HttpSessionListenerInfo info)
+    {
+        final HttpSessionListener service = this.sessionListeners.remove(info
+                .getServiceReference());
+        if (service != null)
+        {
+            info.ungetService(bundle, service);
+        }
+    }
+
+    /**
+     * Add request listener
+     * 
+     * @param info
+     */
+    void addListener(@Nonnull final ServletRequestListenerInfo info)
+    {
+        final ServletRequestListener service = info.getService(bundle);
+        if (service != null)
+        {
+            this.requestListeners.put(info.getServiceReference(), service);
+        }
+    }
+
+    /**
+     * Remove request listener
+     * 
+     * @param info
+     */
+    void removeListener(@Nonnull final ServletRequestListenerInfo info)
+    {
+        final ServletRequestListener service = this.requestListeners
+                .remove(info.getServiceReference());
+        if (service != null)
+        {
+            info.ungetService(bundle, service);
+        }
+    }
+
+    /**
+     * Add request attribute listener
+     * 
+     * @param info
+     */
+    void addListener(@Nonnull final ServletRequestAttributeListenerInfo info)
+    {
+        final ServletRequestAttributeListener service = info.getService(bundle);
+        if (service != null)
+        {
+            this.requestAttributeListeners.put(info.getServiceReference(),
+                    service);
+        }
+    }
+
+    /**
+     * Remove request attribute listener
+     * 
+     * @param info
+     */
+    void removeListener(@Nonnull final ServletRequestAttributeListenerInfo info)
+    {
+        final ServletRequestAttributeListener service = this.requestAttributeListeners
+                .remove(info.getServiceReference());
+        if (service != null)
+        {
+            info.ungetService(bundle, service);
+        }
+    }
+
+    // Make calling from ListenerRegistry easier
+    <T extends ListenerInfo<?>> void addListener(@Nonnull T info)
+    {
+        throw new UnsupportedOperationException("Listeners of type "
+                + info.getClass() + "are not supported");
+    }
+
+    <T extends ListenerInfo<?>> void removeListener(@Nonnull T info)
+    {
+        throw new UnsupportedOperationException("Listeners of type "
+                + info.getClass() + "are not supported");
+    }
+
+    @Override
+    public void attributeReplaced(final HttpSessionBindingEvent event)
+    {
+        for (final HttpSessionAttributeListener l : sessionAttributeListeners
+                .values())
+        {
+            l.attributeReplaced(event);
+        }
+    }
+
+    @Override
+    public void attributeRemoved(final HttpSessionBindingEvent event)
+    {
+        for (final HttpSessionAttributeListener l : sessionAttributeListeners
+                .values())
+        {
+            l.attributeReplaced(event);
+        }
+    }
+
+    @Override
+    public void attributeAdded(final HttpSessionBindingEvent event)
+    {
+        for (final HttpSessionAttributeListener l : sessionAttributeListeners
+                .values())
+        {
+            l.attributeReplaced(event);
+        }
+    }
+
+    @Override
+    public void attributeReplaced(final ServletContextAttributeEvent event)
+    {
+        for (final ServletContextAttributeListener l : contextAttributeListeners
+                .values())
+        {
+            l.attributeReplaced(event);
+        }
+    }
+
+    @Override
+    public void attributeRemoved(final ServletContextAttributeEvent event)
+    {
+        for (final ServletContextAttributeListener l : contextAttributeListeners
+                .values())
+        {
+            l.attributeReplaced(event);
+        }
+    }
+
+    @Override
+    public void attributeAdded(final ServletContextAttributeEvent event)
+    {
+        for (final ServletContextAttributeListener l : contextAttributeListeners
+                .values())
+        {
+            l.attributeReplaced(event);
+        }
+    }
+
+    @Override
+    public void sessionCreated(final HttpSessionEvent se)
+    {
+        for (final HttpSessionListener l : sessionListeners.values())
+        {
+            l.sessionCreated(se);
+        }
+    }
+
+    @Override
+    public void sessionDestroyed(final HttpSessionEvent se)
+    {
+        for (final HttpSessionListener l : sessionListeners.values())
+        {
+            l.sessionDestroyed(se);
+        }
+    }
+
+    @Override
+    public void requestDestroyed(final ServletRequestEvent sre)
+    {
+        for (final ServletRequestListener l : requestListeners.values())
+        {
+            l.requestDestroyed(sre);
+        }
+    }
+
+    @Override
+    public void requestInitialized(final ServletRequestEvent sre)
+    {
+        for (final ServletRequestListener l : requestListeners.values())
+        {
+            l.requestInitialized(sre);
+        }
+    }
+
+    @Override
+    public void attributeAdded(final ServletRequestAttributeEvent srae)
+    {
+        for (final ServletRequestAttributeListener l : requestAttributeListeners
+                .values())
+        {
+            l.attributeAdded(srae);
+        }
+    }
+
+    @Override
+    public void attributeRemoved(final ServletRequestAttributeEvent srae)
+    {
+        for (final ServletRequestAttributeListener l : requestAttributeListeners
+                .values())
+        {
+            l.attributeRemoved(srae);
+        }
+    }
+
+    @Override
+    public void attributeReplaced(final ServletRequestAttributeEvent srae)
+    {
+        for (final ServletRequestAttributeListener l : requestAttributeListeners
+                .values())
+        {
+            l.attributeReplaced(srae);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    Collection<ServiceReference<?>> getRuntime()
+    {
+        return CollectionUtils.<ServiceReference<?>> union(
+                contextAttributeListeners.keySet(),
+                sessionAttributeListeners.keySet(),
+                sessionListeners.keySet(),
+                requestAttributeListeners.keySet(),
+                requestListeners.keySet());
+    }
+}
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 a600795..a31484d 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
@@ -27,24 +27,24 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.concurrent.ConcurrentSkipListSet;
 
 import javax.annotation.Nonnull;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextListener;
 
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
 import org.apache.felix.http.base.internal.logger.SystemLogger;
 import org.apache.felix.http.base.internal.runtime.AbstractInfo;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
-import org.apache.felix.http.base.internal.runtime.HttpSessionAttributeListenerInfo;
-import org.apache.felix.http.base.internal.runtime.HttpSessionListenerInfo;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
+import org.apache.felix.http.base.internal.runtime.ListenerInfo;
+import org.apache.felix.http.base.internal.runtime.RegistryRuntime;
 import org.apache.felix.http.base.internal.runtime.ResourceInfo;
-import org.apache.felix.http.base.internal.runtime.ServletContextAttributeListenerInfo;
 import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
 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.ServletRequestAttributeListenerInfo;
-import org.apache.felix.http.base.internal.runtime.ServletRequestListenerInfo;
 import org.apache.felix.http.base.internal.runtime.WhiteboardServiceInfo;
 import org.apache.felix.http.base.internal.util.MimeTypes;
 import org.osgi.framework.Bundle;
@@ -56,6 +56,7 @@
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.http.context.ServletContextHelper;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
 import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
 
 public final class ServletContextHelperManager
@@ -68,42 +69,57 @@
 
     private final WhiteboardHttpService httpService;
 
-    private final ServiceRegistration<ServletContextHelper> defaultContextRegistration;
+    private final ListenerRegistry listenerRegistry;
 
-    private final ServletContext webContext;
-
-    private final Bundle bundle;
+    private final BundleContext bundleContext;
 
     private final Set<AbstractInfo<?>> invalidRegistrations = new ConcurrentSkipListSet<AbstractInfo<?>>();
 
+    private volatile ServletContext webContext;
+
+    private volatile ServiceReference<HttpServiceRuntime> httpServiceRuntime;
+
+    private volatile ServiceRegistration<ServletContextHelper> defaultContextRegistration;
+
     /**
      * Create a new servlet context helper manager
      * and the default context
      */
     public ServletContextHelperManager(final BundleContext bundleContext,
-            final ServletContext webContext,
-            final WhiteboardHttpService httpService)
+            final WhiteboardHttpService httpService,
+            final ListenerRegistry listenerRegistry)
     {
+        this.bundleContext = bundleContext;
         this.httpService = httpService;
+        this.listenerRegistry = listenerRegistry;
+    }
+
+    public void start(ServletContext webContext, ServiceReference<HttpServiceRuntime> httpServiceRuntime)
+    {
         this.webContext = webContext;
-        this.bundle = bundleContext.getBundle();
+        this.httpServiceRuntime = httpServiceRuntime;
 
         final Dictionary<String, Object> props = new Hashtable<String, Object>();
         props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, HttpWhiteboardConstants.HTTP_WHITEBOARD_DEFAULT_CONTEXT_NAME);
         props.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/");
         props.put(Constants.SERVICE_RANKING, Integer.MIN_VALUE);
 
-        this.defaultContextRegistration = bundleContext.registerService(ServletContextHelper.class,
-                new ServiceFactory<ServletContextHelper>() {
+        this.defaultContextRegistration = bundleContext.registerService(
+                ServletContextHelper.class,
+                new ServiceFactory<ServletContextHelper>()
+                {
 
                     @Override
                     public ServletContextHelper getService(
                             final Bundle bundle,
-                            final ServiceRegistration<ServletContextHelper> registration) {
-                        return new ServletContextHelper(bundle) {
+                            final ServiceRegistration<ServletContextHelper> registration)
+                    {
+                        return new ServletContextHelper(bundle)
+                        {
 
                             @Override
-                            public String getMimeType(final String file) {
+                            public String getMimeType(final String file)
+                            {
                                 return MimeTypes.get().getByFile(file);
                             }
                         };
@@ -113,11 +129,11 @@
                     public void ungetService(
                             final Bundle bundle,
                             final ServiceRegistration<ServletContextHelper> registration,
-                            final ServletContextHelper service) {
+                            final ServletContextHelper service)
+                    {
                         // nothing to do
                     }
-                },
-                props);
+                }, props);
     }
 
     /**
@@ -126,8 +142,11 @@
     public void close()
     {
         // TODO cleanup
-
-        this.defaultContextRegistration.unregister();
+        if (this.defaultContextRegistration != null)
+        {
+            this.defaultContextRegistration.unregister();
+            this.defaultContextRegistration = null;
+        }
     }
 
     /**
@@ -162,7 +181,7 @@
         // context listeners first
         for(final ServletContextListenerInfo info : listeners.values())
         {
-            handler.initialized(info);
+            this.listenerRegistry.initialized(info, handler);
         }
         // now register services
         for(final WhiteboardServiceInfo<?> info : services)
@@ -198,7 +217,7 @@
         }
         for(final ServletContextListenerInfo info : listeners.values())
         {
-            handler.destroyed(info);
+            this.listenerRegistry.destroyed(info, handler);
         }
         handler.deactivate();
 
@@ -216,9 +235,14 @@
         {
             if ( info.isValid() )
             {
-                final ContextHandler handler = new ContextHandler(info, this.webContext, this.bundle);
                 synchronized ( this.contextMap )
                 {
+                    PerContextEventListener contextEventListener = listenerRegistry.addContext(info);
+                    ContextHandler handler = new ContextHandler(info,
+                            this.webContext,
+                            contextEventListener,
+                            this.bundleContext.getBundle());
+
                     List<ContextHandler> handlerList = this.contextMap.get(info.getName());
                     if ( handlerList == null )
                     {
@@ -290,6 +314,7 @@
                         {
                             this.activate(handlerList.get(0));
                         }
+                        listenerRegistry.removeContext(info);
                     }
                 }
             }
@@ -394,28 +419,9 @@
         {
             this.httpService.registerResource(handler, (ResourceInfo)info);
         }
-
-        else if ( info instanceof ServletContextAttributeListenerInfo )
+        else if ( info instanceof ListenerInfo )
         {
-            handler.addListener((ServletContextAttributeListenerInfo)info );
-        }
-
-        else if ( info instanceof HttpSessionListenerInfo )
-        {
-            handler.addListener((HttpSessionListenerInfo)info );
-        }
-        else if ( info instanceof HttpSessionAttributeListenerInfo )
-        {
-            handler.addListener((HttpSessionAttributeListenerInfo)info );
-        }
-
-        else if ( info instanceof ServletRequestListenerInfo )
-        {
-            handler.addListener((ServletRequestListenerInfo)info );
-        }
-        else if ( info instanceof ServletRequestAttributeListenerInfo )
-        {
-            handler.addListener((ServletRequestAttributeListenerInfo)info );
+            this.listenerRegistry.addListener((ListenerInfo<?>)info, handler);
         }
     }
 
@@ -438,28 +444,9 @@
         {
             this.httpService.unregisterResource(handler, (ResourceInfo)info);
         }
-
-        else if ( info instanceof ServletContextAttributeListenerInfo )
+        else if ( info instanceof ListenerInfo )
         {
-            handler.removeListener((ServletContextAttributeListenerInfo)info );
-        }
-
-        else if ( info instanceof HttpSessionListenerInfo )
-        {
-            handler.removeListener((HttpSessionListenerInfo)info );
-        }
-        else if ( info instanceof HttpSessionAttributeListenerInfo )
-        {
-            handler.removeListener((HttpSessionAttributeListenerInfo)info );
-        }
-
-        else if ( info instanceof ServletRequestListenerInfo )
-        {
-            handler.removeListener((ServletRequestListenerInfo)info );
-        }
-        else if ( info instanceof ServletRequestAttributeListenerInfo )
-        {
-            handler.removeListener((ServletRequestAttributeListenerInfo)info );
+            this.listenerRegistry.removeListener((ListenerInfo<?>)info, handler);
         }
     }
 
@@ -474,8 +461,8 @@
         {
             try
             {
-                final Filter f = this.bundle.getBundleContext().createFilter(target);
-                return f.match(this.httpService.getServiceReference());
+                final Filter f = this.bundleContext.createFilter(target);
+                return f.match(this.httpServiceRuntime);
             }
             catch ( final InvalidSyntaxException ise)
             {
@@ -516,4 +503,21 @@
          }
          return handlers;
     }
+
+    public RegistryRuntime getRuntime(HandlerRegistry registry)
+    {
+        List<HandlerRuntime> handlerRuntimes;
+        Map<Long, Collection<ServiceReference<?>>> listenerRuntimes;
+        Set<ContextHandler> contextHandlers = new TreeSet<ContextHandler>();
+        synchronized ( this.contextMap )
+        {
+            for (List<ContextHandler> contextHandlerList : this.contextMap.values())
+            {
+                contextHandlers.addAll(contextHandlerList);
+            }
+            handlerRuntimes = registry.getRuntime();
+            listenerRuntimes = listenerRegistry.getContextRuntimes();
+        }
+        return new RegistryRuntime(contextHandlers, handlerRuntimes, listenerRuntimes);
+    }
 }
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 918ea9e..b80ec80 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
@@ -16,154 +16,37 @@
  */
 package org.apache.felix.http.base.internal.whiteboard;
 
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
 import javax.annotation.Nonnull;
-import javax.servlet.DispatcherType;
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
-import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
-import javax.servlet.http.HttpSession;
 
-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.HttpSessionWrapper;
 import org.apache.felix.http.base.internal.handler.PerContextHandlerRegistry;
 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.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;
-import org.apache.felix.http.base.internal.whiteboard.tracker.HttpSessionListenerTracker;
-import org.apache.felix.http.base.internal.whiteboard.tracker.ResourceTracker;
-import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextAttributeListenerTracker;
-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.ServletRequestAttributeListenerTracker;
-import org.apache.felix.http.base.internal.whiteboard.tracker.ServletRequestListenerTracker;
-import org.apache.felix.http.base.internal.whiteboard.tracker.ServletTracker;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceObjects;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.http.runtime.HttpServiceRuntime;
-import org.osgi.service.http.runtime.HttpServiceRuntimeConstants;
-import org.osgi.service.http.runtime.dto.ErrorPageDTO;
-import org.osgi.service.http.runtime.dto.FilterDTO;
-import org.osgi.service.http.runtime.dto.ListenerDTO;
-import org.osgi.service.http.runtime.dto.RequestInfoDTO;
-import org.osgi.service.http.runtime.dto.ResourceDTO;
-import org.osgi.service.http.runtime.dto.RuntimeDTO;
-import org.osgi.service.http.runtime.dto.ServletContextDTO;
-import org.osgi.service.http.runtime.dto.ServletDTO;
-import org.osgi.util.tracker.ServiceTracker;
 
-public final class WhiteboardHttpService implements HttpServiceRuntime
+public final class WhiteboardHttpService
 {
-
     private final HandlerRegistry handlerRegistry;
 
     private final BundleContext bundleContext;
 
-    private volatile ServletContextHelperManager contextManager;
-
-    private final List<ServiceTracker<?, ?>> trackers = new ArrayList<ServiceTracker<?, ?>>();
-
-    private final Hashtable<String, Object> runtimeServiceProps = new Hashtable<String, Object>();;
-
-    private final HttpServiceFactory httpServiceFactory;
-
-    private volatile ServiceRegistration<HttpServiceRuntime> runtimeServiceReg;
-
     /**
      * Create a new whiteboard http service
      * @param bundleContext
-     * @param context
      * @param handlerRegistry
      */
     public WhiteboardHttpService(final BundleContext bundleContext,
-            final HandlerRegistry handlerRegistry,
-            final HttpServiceFactory httpServiceFactory)
+            final HandlerRegistry handlerRegistry)
     {
         this.handlerRegistry = handlerRegistry;
         this.bundleContext = bundleContext;
-        this.httpServiceFactory = httpServiceFactory;
-    }
-
-    public void start(final ServletContext context)
-    {
-        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 HttpSessionListenerTracker(bundleContext, this.contextManager));
-        addTracker(new HttpSessionAttributeListenerTracker(bundleContext, this.contextManager));
-
-        addTracker(new ServletContextHelperTracker(bundleContext, this.contextManager));
-        addTracker(new ServletContextListenerTracker(bundleContext, this.contextManager));
-        addTracker(new ServletContextAttributeListenerTracker(bundleContext, this.contextManager));
-
-        addTracker(new ServletRequestListenerTracker(bundleContext, this.contextManager));
-        addTracker(new ServletRequestAttributeListenerTracker(bundleContext, this.contextManager));
-
-        this.runtimeServiceProps.put(HttpServiceRuntimeConstants.HTTP_SERVICE_ID_ATTRIBUTE,
-                this.httpServiceFactory.getHttpServiceServiceId());
-        this.runtimeServiceReg = this.bundleContext.registerService(HttpServiceRuntime.class,
-                this,
-                this.runtimeServiceProps);
-    }
-
-    public void stop()
-    {
-        if ( this.runtimeServiceReg != null )
-        {
-            this.runtimeServiceReg.unregister();
-            this.runtimeServiceReg = null;
-        }
-
-        for(final ServiceTracker<?, ?> t : this.trackers)
-        {
-            t.close();
-        }
-        this.trackers.clear();
-        if ( this.contextManager != null )
-        {
-            this.contextManager.close();
-            this.contextManager = null;
-        }
-    }
-
-    private void addTracker(ServiceTracker<?, ?> tracker)
-    {
-        this.trackers.add(tracker);
-        tracker.open();
-    }
-
-    public void setProperties(final Hashtable<String, Object> props)
-    {
-        // runtime service gets the same props for now
-        this.runtimeServiceProps.clear();
-        this.runtimeServiceProps.putAll(props);
-
-        if (this.runtimeServiceReg != null)
-        {
-            this.runtimeServiceProps.put(HttpServiceRuntimeConstants.HTTP_SERVICE_ID_ATTRIBUTE,
-                    this.httpServiceFactory.getHttpServiceServiceId());
-            this.runtimeServiceReg.setProperties(this.runtimeServiceProps);
-        }
     }
 
     /**
@@ -190,7 +73,6 @@
                     if (registry != null )
                     {
                         registry.addServlet(handler);
-                        contextHandler.addWhiteboardService(servletInfo);
                     }
                 } catch (final ServletException e) {
                     so.ungetService(servlet);
@@ -217,7 +99,6 @@
             }
         }
         contextHandler.ungetServletContext(servletInfo.getServiceReference().getBundle());
-        contextHandler.removeWhiteboardService(servletInfo);
     }
 
     /**
@@ -241,7 +122,6 @@
                 if (registry != null )
                 {
                     registry.addFilter(handler);
-                    contextHandler.addWhiteboardService(filterInfo);
                 }
             } catch (final ServletException e) {
                 // TODO create failure DTO
@@ -266,7 +146,6 @@
             }
         }
         contextHandler.ungetServletContext(filterInfo.getServiceReference().getBundle());
-        contextHandler.removeWhiteboardService(filterInfo);
     }
 
     /**
@@ -289,7 +168,6 @@
             if (registry != null )
             {
                 registry.addServlet(handler);
-                contextHandler.addWhiteboardService(resourceInfo);
             }
         } catch (ServletException e) {
             // TODO create failure DTO
@@ -310,7 +188,6 @@
             registry.removeServlet(servletInfo, true);
         }
         contextHandler.ungetServletContext(servletInfo.getServiceReference().getBundle());
-        contextHandler.removeWhiteboardService(servletInfo);
     }
 
     public void registerContext(@Nonnull final ContextHandler contextHandler)
@@ -322,173 +199,4 @@
     {
         this.handlerRegistry.remove(contextHandler.getContextInfo());
     }
-
-    public void sessionDestroyed(@Nonnull final HttpSession session, final Set<Long> contextIds)
-    {
-        for(final Long contextId : contextIds)
-        {
-            // TODO - on shutdown context manager is already NULL which shouldn't be the case
-            if ( this.contextManager != null )
-            {
-                final ContextHandler handler = this.contextManager.getContextHandler(contextId);
-                if ( handler != null )
-                {
-                    final ExtServletContext context = handler.getServletContext(this.bundleContext.getBundle());
-                    new HttpSessionWrapper(contextId, session, context, true).invalidate();
-                    handler.ungetServletContext(this.bundleContext.getBundle());
-                }
-            }
-        }
-    }
-
-    public ServiceReference<HttpServiceRuntime> getServiceReference()
-    {
-        return this.runtimeServiceReg.getReference();
-    }
-
-    @Override
-    public RuntimeDTO getRuntimeDTO()
-    {
-        // create new DTO on every call
-        final RuntimeDTO runtime = new RuntimeDTO();
-
-        // attributes
-        runtime.attributes = new HashMap<String, String>();
-        for(final Map.Entry<String, Object> entry : this.runtimeServiceProps.entrySet())
-        {
-            runtime.attributes.put(entry.getKey(), entry.getValue().toString());
-        }
-
-        // servlet context DTOs
-        final List<ServletContextDTO> contextDTOs = new ArrayList<ServletContextDTO>();
-        for(final ContextHandler handler : this.contextManager.getContextHandlers())
-        {
-            final ServletContextDTO dto = new ServletContextDTO();
-
-            final ServletContext ctx = handler.getServletContext(this.bundleContext.getBundle());
-            try
-            {
-                dto.name = handler.getContextInfo().getName();
-                dto.contextPath = handler.getContextInfo().getPath();
-                dto.initParams = new HashMap<String, String>(handler.getContextInfo().getInitParameters());
-                dto.serviceId = handler.getContextInfo().getServiceId();
-
-                dto.contextName = ctx.getServletContextName();
-                dto.attributes = new HashMap<String, Object>();
-                final Enumeration<String> e = ctx.getAttributeNames();
-                while ( e.hasMoreElements() )
-                {
-                    final String name = e.nextElement();
-                    final Object value = ctx.getAttribute(name);
-                    if ( value != null )
-                    {
-                        // TODO - check for appropriate value types
-                    }
-                }
-
-                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
-            }
-            finally
-            {
-                handler.ungetServletContext(this.bundleContext.getBundle());
-            }
-            contextDTOs.add(dto);
-        }
-        runtime.servletContextDTOs = contextDTOs.toArray(new ServletContextDTO[contextDTOs.size()]);
-
-        runtime.failedErrorPageDTOs = null; // TODO
-        runtime.failedFilterDTOs = null; // TODO
-        runtime.failedListenerDTOs = null; // TODO
-        runtime.failedResourceDTOs = null; // TODO
-        runtime.failedServletContextDTOs = null; // TODO
-        runtime.failedServletDTOs = null; // TODO
-
-        return runtime;
-    }
-
-    @Override
-    public RequestInfoDTO calculateRequestInfoDTO(final String path) {
-        // TODO
-        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;
-    }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardManager.java
new file mode 100644
index 0000000..acf0a86
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/WhiteboardManager.java
@@ -0,0 +1,163 @@
+/*
+ * 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 java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+
+import org.apache.felix.http.base.internal.context.ExtServletContext;
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.handler.HttpSessionWrapper;
+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.whiteboard.tracker.FilterTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.HttpSessionAttributeListenerTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.HttpSessionListenerTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ResourceTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextAttributeListenerTracker;
+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.ServletRequestAttributeListenerTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletRequestListenerTracker;
+import org.apache.felix.http.base.internal.whiteboard.tracker.ServletTracker;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.http.runtime.HttpServiceRuntime;
+import org.osgi.service.http.runtime.HttpServiceRuntimeConstants;
+import org.osgi.util.tracker.ServiceTracker;
+
+public final class WhiteboardManager
+{
+    private final BundleContext bundleContext;
+
+    private final List<ServiceTracker<?, ?>> trackers = new ArrayList<ServiceTracker<?, ?>>();
+
+    private final HttpServiceFactory httpServiceFactory;
+
+    private final HttpServiceRuntimeImpl serviceRuntime;
+
+    private final ServletContextHelperManager contextManager;
+
+    private volatile ServiceRegistration<HttpServiceRuntime> runtimeServiceReg;
+
+    /**
+     * Create a new whiteboard http manager
+     * @param bundleContext
+     * @param httpServiceFactory
+     * @param registry 
+     */
+    public WhiteboardManager(final BundleContext bundleContext,
+            final HttpServiceFactory httpServiceFactory,
+            final HandlerRegistry registry)
+    {
+        this.bundleContext = bundleContext;
+        this.httpServiceFactory = httpServiceFactory;
+        WhiteboardHttpService whiteboardHttpService = new WhiteboardHttpService(this.bundleContext, registry);
+        ListenerRegistry listenerRegistry = new ListenerRegistry(bundleContext.getBundle());
+        this.contextManager = new ServletContextHelperManager(bundleContext, whiteboardHttpService, listenerRegistry);
+        this.serviceRuntime = new HttpServiceRuntimeImpl(registry, this.contextManager);
+    }
+
+    public void start(final ServletContext context)
+    {
+        // TODO set Endpoint
+        this.serviceRuntime.setAttribute(HttpServiceRuntimeConstants.HTTP_SERVICE_ID_ATTRIBUTE,
+                this.httpServiceFactory.getHttpServiceServiceId());
+        this.runtimeServiceReg = this.bundleContext.registerService(HttpServiceRuntime.class,
+                serviceRuntime,
+                this.serviceRuntime.getAttributes());
+
+        this.contextManager.start(context, this.runtimeServiceReg.getReference());
+
+        addTracker(new FilterTracker(this.bundleContext, contextManager));
+        addTracker(new ServletTracker(this.bundleContext, this.contextManager));
+        addTracker(new ResourceTracker(this.bundleContext, this.contextManager));
+
+        addTracker(new HttpSessionListenerTracker(this.bundleContext, this.contextManager));
+        addTracker(new HttpSessionAttributeListenerTracker(this.bundleContext, this.contextManager));
+
+        addTracker(new ServletContextHelperTracker(this.bundleContext, this.contextManager));
+        addTracker(new ServletContextListenerTracker(this.bundleContext, this.contextManager));
+        addTracker(new ServletContextAttributeListenerTracker(this.bundleContext, this.contextManager));
+
+        addTracker(new ServletRequestListenerTracker(this.bundleContext, this.contextManager));
+        addTracker(new ServletRequestAttributeListenerTracker(this.bundleContext, this.contextManager));
+    }
+
+    public void stop()
+    {
+        for(final ServiceTracker<?, ?> t : this.trackers)
+        {
+            t.close();
+        }
+        this.trackers.clear();
+
+        if ( this.contextManager != null )
+        {
+            this.contextManager.close();
+        }
+
+        if ( this.runtimeServiceReg != null )
+        {
+            this.runtimeServiceReg.unregister();
+            this.runtimeServiceReg = null;
+        }
+    }
+
+    private void addTracker(ServiceTracker<?, ?> tracker)
+    {
+        this.trackers.add(tracker);
+        tracker.open();
+    }
+
+    public void setProperties(final Hashtable<String, Object> props)
+    {
+        // runtime service gets the same props for now
+        this.serviceRuntime.setAllAttributes(props);
+
+        if (this.runtimeServiceReg != null)
+        {
+            this.serviceRuntime.setAttribute(HttpServiceRuntimeConstants.HTTP_SERVICE_ID_ATTRIBUTE,
+                    this.httpServiceFactory.getHttpServiceServiceId());
+            this.runtimeServiceReg.setProperties(this.serviceRuntime.getAttributes());
+        }
+    }
+
+    public void sessionDestroyed(@Nonnull final HttpSession session, final Set<Long> contextIds)
+    {
+        for(final Long contextId : contextIds)
+        {
+            // TODO - on shutdown context manager is already NULL which shouldn't be the case
+            if ( this.contextManager != null )
+            {
+                final ContextHandler handler = this.contextManager.getContextHandler(contextId);
+                if ( handler != null )
+                {
+                    final ExtServletContext context = handler.getServletContext(this.bundleContext.getBundle());
+                    new HttpSessionWrapper(contextId, session, context, true).invalidate();
+                    handler.ungetServletContext(this.bundleContext.getBundle());
+                }
+            }
+        }
+    }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceHelper.java b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceHelper.java
new file mode 100644
index 0000000..2108ee4
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/WhiteboardServiceHelper.java
@@ -0,0 +1,189 @@
+/*
+ * 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 static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+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.ServletHandler;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime.ErrorPage;
+
+public final class WhiteboardServiceHelper
+{
+    public static final AtomicLong ID_COUNTER = new AtomicLong();
+
+    public static FilterHandler createTestFilterWithServiceId(String identifier,
+            ExtServletContext context)
+    {
+        return createTestFilter(identifier, context, ID_COUNTER.incrementAndGet());
+    }
+
+    public static FilterHandler createTestFilter(String identifier,
+            ExtServletContext context)
+    {
+        return createTestFilter(identifier, context, -ID_COUNTER.incrementAndGet());
+    }
+
+    private static FilterHandler createTestFilter(String identifier,
+            ExtServletContext context,
+            Long serviceId)
+    {
+        FilterInfo filterInfo = createFilterInfo(identifier, serviceId);
+        return new FilterHandler(null, context, mock(Filter.class), filterInfo);
+    }
+
+    private static FilterInfo createFilterInfo(String identifier,
+            Long serviceId)
+    {
+        boolean asyncSupported = true;
+        String name = identifier;
+        Map<String, String> initParams = createInitParameterMap(identifier);
+        String[] patterns = new String[]{ "/" + identifier };
+        String[] regexs = new String[]{ "." + identifier };
+        DispatcherType[] dispatcher = new DispatcherType[] { DispatcherType.ASYNC, DispatcherType.REQUEST };
+        return createFilterInfo(0, serviceId, name, patterns, null, regexs, asyncSupported, dispatcher, initParams);
+    }
+
+    public static FilterInfo createFilterInfo(int serviceRanking,
+            Long serviceId,
+            String name,
+            String[] patterns,
+            String[] servletNames,
+            String[] regexs,
+            boolean asyncSupported,
+            DispatcherType[] dispatcher,
+            Map<String, String> initParams)
+    {
+        FilterInfo info = new FilterInfo(0,
+                serviceId,
+                name,
+                patterns,
+                servletNames,
+                regexs,
+                asyncSupported,
+                dispatcher,
+                initParams);
+        return info;
+    }
+
+    public static ServletHandler createTestServletWithServiceId(String identifier,
+            ExtServletContext context)
+    {
+        return createTestServlet(identifier, context, ID_COUNTER.incrementAndGet());
+    }
+
+    public static ServletHandler createTestServlet(String identifier, ExtServletContext context)
+    {
+        return createTestServlet(identifier, context, -ID_COUNTER.incrementAndGet());
+    }
+
+    private static ServletHandler createTestServlet(String identifier,
+            ExtServletContext context,
+            Long serviceId)
+    {
+        ServletInfo servletInfo = createServletInfo(identifier, serviceId);
+        Servlet servlet = mock(Servlet.class);
+        when(servlet.getServletInfo()).thenReturn("info_" + identifier);
+        return new ServletHandler(null, context, servletInfo, servlet);
+    }
+
+    private static ServletInfo createServletInfo(String identifier, Long serviceId)
+    {
+        boolean asyncSupported = true;
+        String name = identifier;
+        Map<String, String> initParams = createInitParameterMap(identifier);
+        String[] patterns = new String[]{ "/" + identifier };
+        return createServletInfo(0, serviceId, name, patterns, null, asyncSupported, initParams);
+    }
+
+    public static ServletInfo createServletInfo(int serviceRanking,
+            Long serviceId,
+            String name,
+            String[] patterns,
+            String[] errorPages,
+            boolean asyncSupported,
+            Map<String, String> initParams)
+    {
+        return new ServletInfo(0,
+                serviceId,
+                name,
+                patterns,
+                null,
+                asyncSupported,
+                initParams);
+    }
+
+    @SuppressWarnings("serial")
+    private static HashMap<String, String> createInitParameterMap(final String identifier)
+    {
+        return new HashMap<String, String>()
+                {
+                    {
+                        put("paramOne_" + identifier, "valOne_" + identifier);
+                        put("paramTwo_" + identifier, "valTwo_" + identifier);
+                    }
+                };
+    }
+
+    public static ErrorPage createErrorPageWithServiceId(String identifier, ExtServletContext context)
+    {
+        return createErrorPage(identifier, context, ID_COUNTER.incrementAndGet());
+    }
+
+    public static ErrorPage createErrorPage(String identifier, ExtServletContext context)
+    {
+        return createErrorPage(identifier, context, -ID_COUNTER.incrementAndGet());
+    }
+
+    private static ErrorPage createErrorPage(String identifier,
+            ExtServletContext context,
+            Long serviceId)
+    {
+        ServletHandler servletHandler = createTestServlet(identifier, context, serviceId);
+        Collection<Integer> errorCodes = Arrays.asList(400, 500);
+        Collection<String> exceptions = Arrays.asList("Bad request", "Error");
+
+        return new ErrorPage(servletHandler, errorCodes, exceptions);
+    }
+
+    public static ServletContextHelperInfo createContextInfo(int serviceRanking,
+            long serviceId,
+            String name,
+            String path,
+            Map<String, String> initParams)
+    {
+        return new ServletContextHelperInfo(serviceRanking,
+                serviceId,
+                name,
+                path,
+                initParams);
+    }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilderTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilderTest.java
new file mode 100644
index 0000000..31b3cab
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/runtime/dto/RuntimeDTOBuilderTest.java
@@ -0,0 +1,578 @@
+/*
+ * 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.dto;
+
+import static java.util.Arrays.asList;
+import static java.util.Arrays.copyOf;
+import static java.util.Arrays.sort;
+import static java.util.Collections.emptyMap;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.ID_COUNTER;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createContextInfo;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createErrorPage;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createErrorPageWithServiceId;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createFilterInfo;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createServletInfo;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createTestFilter;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createTestFilterWithServiceId;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createTestServlet;
+import static org.apache.felix.http.base.internal.runtime.WhiteboardServiceHelper.createTestServletWithServiceId;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+
+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.ServletHandler;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime;
+import org.apache.felix.http.base.internal.runtime.HandlerRuntime.ErrorPage;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.apache.felix.http.base.internal.runtime.RegistryRuntime;
+import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.whiteboard.ContextHandler;
+import org.apache.felix.http.base.internal.whiteboard.ListenerRegistry;
+import org.apache.felix.http.base.internal.whiteboard.PerContextEventListener;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.dto.DTO;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.runtime.dto.ErrorPageDTO;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.ListenerDTO;
+import org.osgi.service.http.runtime.dto.RuntimeDTO;
+import org.osgi.service.http.runtime.dto.ServletContextDTO;
+import org.osgi.service.http.runtime.dto.ServletDTO;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RuntimeDTOBuilderTest
+{
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
+
+    private static final Long ID_0 = -ID_COUNTER.incrementAndGet();
+    private static final Long ID_A = ID_COUNTER.incrementAndGet();
+    private static final Long ID_B = ID_COUNTER.incrementAndGet();
+
+    private static final Long ID_LISTENER_1 = ID_COUNTER.incrementAndGet();
+    private static final Long ID_LISTENER_2 = ID_COUNTER.incrementAndGet();
+
+    private static final List<String> CONTEXT_NAMES = Arrays.asList("0", "A", "B");
+
+    @SuppressWarnings("serial")
+    private static final Map<String, List<String>> CONTEXT_ENTITY_NAMES = new HashMap<String, List<String>>()
+            {
+                {
+                    put("0", Arrays.asList("1"));
+                    put("A", Arrays.asList("A_1"));
+                    put("B", Arrays.asList("B_1", "B_2"));
+                }
+            };
+
+    @Mock private Bundle bundle;
+
+    @Mock private DTO testDTO;
+
+    @Mock private ExtServletContext context_0;
+    @Mock private ExtServletContext context_A;
+    @Mock private ExtServletContext context_B;
+
+    @Mock private ServiceReference<?> listener_1;
+    @Mock private ServiceReference<?> listener_2;
+
+    private RegistryRuntime registry;
+    private Map<String, Object> runtimeAttributes;
+	private ListenerRegistry listenerRegistry;
+
+    @Before
+    public void setUp()
+    {
+        registry = null;
+        runtimeAttributes = Collections.emptyMap();
+        listenerRegistry = new ListenerRegistry(bundle);
+    }
+
+	public ContextHandler setupContext(ServletContext context, String name, long serviceId)
+    {
+        when(context.getServletContextName()).thenReturn(name);
+
+        String path = "/" + name;
+        when(context.getContextPath()).thenReturn(path);
+
+        List<String> initParameterNames = asList("param_1", "param_2");
+        when(context.getInitParameterNames()).thenReturn(Collections.enumeration(initParameterNames));
+        when(context.getInitParameter("param_1")).thenReturn("init_val_1");
+        when(context.getInitParameter("param_2")).thenReturn("init_val_2");
+
+        Map<String, String> initParemters = new HashMap<String, String>();
+        initParemters.put("param_1", "init_val_1");
+        initParemters.put("param_2", "init_val_2");
+        ServletContextHelperInfo contextInfo = createContextInfo(0, serviceId, name, path, initParemters);
+
+        PerContextEventListener eventListener = listenerRegistry.addContext(contextInfo);
+        ContextHandler contextHandler = new ContextHandler(contextInfo, context, eventListener, bundle);
+
+        ServletContext sharedContext = contextHandler.getSharedContext();
+        sharedContext.setAttribute("intAttr", 1);
+        sharedContext.setAttribute("dateAttr", new Date());
+        sharedContext.setAttribute("stringAttr", "one");
+        sharedContext.setAttribute("dtoAttr", testDTO);
+
+        return contextHandler;
+    }
+
+    @SuppressWarnings("unchecked")
+    public Map<Long, Collection<ServiceReference<?>>> setupListeners()
+    {
+        Map<Long, Collection<ServiceReference<?>>> listenerRuntimes = new HashMap<Long, Collection<ServiceReference<?>>>();
+        listenerRuntimes.put(ID_0, asList(listener_1, listener_2));
+        listenerRuntimes.put(ID_A, Arrays.<ServiceReference<?>> asList(listener_1));
+        listenerRuntimes.put(ID_B, asList(listener_1, listener_2));
+
+        when(listener_1.getProperty(Constants.SERVICE_ID)).thenReturn(ID_LISTENER_1);
+        when(listener_1.getProperty(Constants.OBJECTCLASS))
+                .thenReturn(new String[] { "org.test.interface_1" });
+
+        when(listener_2.getProperty(Constants.SERVICE_ID)).thenReturn(ID_LISTENER_2);
+        when(listener_2.getProperty(Constants.OBJECTCLASS))
+                .thenReturn(new String[] { "org.test.interface_1", "org.test.interface_2" });
+
+        return listenerRuntimes;
+    }
+
+    public void setupRegistry(List<ContextHandler> contexts,
+            List<HandlerRuntime> contextRuntimes,
+            Map<Long, Collection<ServiceReference<?>>> listenerRuntimes)
+    {
+        registry = new RegistryRuntime(contexts, contextRuntimes, listenerRuntimes);
+    }
+
+    @Test
+    public void buildRuntimeDTO()
+    {
+        ContextHandler contextHelper_0 = setupContext(context_0, "0", ID_0);
+        ContextHandler contextHelper_A = setupContext(context_A, "A", ID_A);
+        ContextHandler contextHelper_B = setupContext(context_B, "B", ID_B);
+
+        List<ServletHandler> servlets_0 = asList(createTestServlet("1", context_0));
+        List<FilterHandler> filters_0 = asList(createTestFilter("1", context_0));
+        List<ErrorPage> errorPages_0 = asList(createErrorPage("E_1", context_0));
+        HandlerRuntime contextRuntime_0 = new HandlerRuntime(servlets_0, filters_0, errorPages_0, ID_0);
+
+        List<ServletHandler> servlets_A = asList(createTestServlet("A_1", context_A));
+        List<FilterHandler> filters_A = asList(createTestFilter("A_1", context_A));
+        List<ErrorPage> errorPages_A = asList(createErrorPage("E_A_1", context_A));
+        HandlerRuntime contextRuntime_A = new HandlerRuntime(servlets_A, filters_A, errorPages_A, ID_A);
+
+        List<ServletHandler> servlets_B = asList(createTestServletWithServiceId("B_1", context_B),
+                createTestServletWithServiceId("B_2", context_B));
+        List<FilterHandler> filters_B = asList(createTestFilterWithServiceId("B_1", context_B),
+                createTestFilterWithServiceId("B_2", context_B));
+        List<ErrorPage> errorPages_B = asList(createErrorPageWithServiceId("E_B_1", context_B),
+                createErrorPageWithServiceId("E_B_2", context_B));
+        HandlerRuntime contextRuntime_B = new HandlerRuntime(servlets_B, filters_B, errorPages_B, ID_B);
+
+        Map<Long, Collection<ServiceReference<?>>> listenerRuntimes = setupListeners();
+
+        setupRegistry(asList(contextHelper_0, contextHelper_A, contextHelper_B),
+                asList(contextRuntime_0, contextRuntime_A, contextRuntime_B),
+                listenerRuntimes);
+
+        RuntimeDTO runtimeDTO = new RuntimeDTOBuilder(registry, runtimeAttributes).build();
+
+        assertServletContextDTOs(runtimeDTO);
+    }
+
+    private void assertServletContextDTOs(RuntimeDTO runtimeDTO)
+    {
+        SortedSet<Long> seenServiceIds = new TreeSet<Long>();
+
+        assertEquals(3, runtimeDTO.servletContextDTOs.length);
+
+        for (ServletContextDTO servletContextDTO : runtimeDTO.servletContextDTOs)
+        {
+            String contextName = servletContextDTO.contextName;
+            assertTrue(CONTEXT_NAMES.contains(contextName));
+            if (contextName.equals("0"))
+            {
+                assertNull(contextName,
+                        servletContextDTO.name);
+                assertTrue(contextName,
+                        servletContextDTO.serviceId < 0);
+                assertEquals(contextName,
+                        1, servletContextDTO.servletDTOs.length);
+                assertEquals(contextName,
+                        1, servletContextDTO.filterDTOs.length);
+                assertEquals(contextName,
+                        1, servletContextDTO.errorPageDTOs.length);
+                assertEquals(contextName,
+                        2, servletContextDTO.listenerDTOs.length);
+            }
+            else
+            {
+                assertEquals(contextName,
+                        contextName, servletContextDTO.name);
+
+                int expectedId = CONTEXT_NAMES.indexOf(contextName) + 1;
+                assertEquals(contextName,
+                        expectedId, servletContextDTO.serviceId);
+
+                int expectedChildren = CONTEXT_NAMES.indexOf(contextName);
+                assertEquals(contextName,
+                        expectedChildren, servletContextDTO.servletDTOs.length);
+                assertEquals(contextName,
+                        expectedChildren, servletContextDTO.filterDTOs.length);
+                assertEquals(contextName,
+                        expectedChildren, servletContextDTO.errorPageDTOs.length);
+                assertEquals(contextName,
+                        expectedChildren, servletContextDTO.listenerDTOs.length);
+            }
+            seenServiceIds.add(servletContextDTO.serviceId);
+
+            assertEquals(contextName,
+                    3, servletContextDTO.attributes.size());
+            assertEquals(contextName,
+                    1, servletContextDTO.attributes.get("intAttr"));
+            assertEquals(contextName,
+                    "one", servletContextDTO.attributes.get("stringAttr"));
+            assertEquals(contextName,
+                    testDTO, servletContextDTO.attributes.get("dtoAttr"));
+
+            assertEquals(contextName,
+                    2, servletContextDTO.initParams.size());
+            assertEquals(contextName,
+                    "init_val_1", servletContextDTO.initParams.get("param_1"));
+            assertEquals(contextName,
+                    "init_val_2", servletContextDTO.initParams.get("param_2"));
+
+            assertEquals(contextName,
+                    "/" + contextName + "/" + contextName, servletContextDTO.contextPath);
+
+            Collection<Long> serviceIds = assertServletDTOs(contextName,
+                    servletContextDTO.serviceId, servletContextDTO.servletDTOs);
+            seenServiceIds.addAll(serviceIds);
+
+            serviceIds = assertFilterDTOs(contextName,
+                    servletContextDTO.serviceId, servletContextDTO.filterDTOs);
+            seenServiceIds.addAll(serviceIds);
+
+            serviceIds = assertErrorPageDTOs(contextName,
+                    servletContextDTO.serviceId, servletContextDTO.errorPageDTOs);
+            seenServiceIds.addAll(serviceIds);
+
+            serviceIds = assertListenerDTOs(contextName,
+                    servletContextDTO.serviceId, servletContextDTO.listenerDTOs);
+            seenServiceIds.addAll(serviceIds);
+        }
+        assertEquals(10, seenServiceIds.tailSet(0L).size());
+        assertEquals(7, seenServiceIds.headSet(0L).size());
+    }
+
+    private Collection<Long> assertServletDTOs(String contextName, long contextId, ServletDTO[] dtos) {
+        List<Long> serviceIds = new ArrayList<Long>();
+        for (ServletDTO servletDTO : dtos)
+        {
+            String name = servletDTO.name;
+            assertTrue(CONTEXT_ENTITY_NAMES.get(contextName).contains(name));
+
+            if (contextId != ID_B)
+            {
+                assertTrue(name,
+                        servletDTO.serviceId < 0);
+            }
+            else
+            {
+                assertTrue(name,
+                        servletDTO.serviceId > 0);
+            }
+            serviceIds.add(servletDTO.serviceId);
+
+            assertEquals(name,
+                    contextId, servletDTO.servletContextId);
+
+            assertTrue(name,
+                    servletDTO.asyncSupported);
+
+            assertEquals(name,
+                    2, servletDTO.initParams.size());
+            assertEquals(name,
+                    "valOne_" + name, servletDTO.initParams.get("paramOne_" + name));
+            assertEquals(name,
+                    "valTwo_" + name, servletDTO.initParams.get("paramTwo_" + name));
+
+            assertEquals(name,
+                    1, servletDTO.patterns.length);
+            assertEquals(name,
+                    "/" + name, servletDTO.patterns[0]);
+
+            assertEquals(name,
+                    "info_" + name, servletDTO.servletInfo);
+        }
+
+        return serviceIds;
+    }
+
+    private Collection<Long> assertFilterDTOs(String contextName, long contextId, FilterDTO[] dtos) {
+        List<Long> serviceIds = new ArrayList<Long>();
+        for (FilterDTO filterDTO : dtos)
+        {
+            String name = filterDTO.name;
+            assertTrue(CONTEXT_ENTITY_NAMES.get(contextName).contains(name));
+
+            if (contextId != ID_B)
+            {
+                assertTrue(name,
+                        filterDTO.serviceId < 0);
+            }
+            else
+            {
+                assertTrue(name,
+                        filterDTO.serviceId > 0);
+            }
+            serviceIds.add(filterDTO.serviceId);
+
+            assertEquals(name,
+                    contextId, filterDTO.servletContextId);
+
+            assertTrue(name,
+                    filterDTO.asyncSupported);
+
+            assertEquals(name,
+                    2, filterDTO.initParams.size());
+            assertEquals(name,
+                    "valOne_" + name, filterDTO.initParams.get("paramOne_" + name));
+            assertEquals(name,
+                    "valTwo_" + name, filterDTO.initParams.get("paramTwo_" + name));
+
+            assertEquals(name,
+                    1, filterDTO.patterns.length);
+            assertEquals(name,
+                    "/" + name, filterDTO.patterns[0]);
+
+            assertEquals(name,
+                    1, filterDTO.regexs.length);
+            assertEquals(name,
+                    "." + name, filterDTO.regexs[0]);
+
+            assertEquals(name,
+                    2, filterDTO.dispatcher.length);
+            assertEquals(name,
+                    "ASYNC", filterDTO.dispatcher[0]);
+            assertEquals(name,
+                    "REQUEST", filterDTO.dispatcher[1]);
+        }
+
+        return serviceIds;
+    }
+
+    private Collection<Long> assertErrorPageDTOs(String contextName, long contextId, ErrorPageDTO[] dtos)
+    {
+        List<Long> serviceIds = new ArrayList<Long>();
+        for (ErrorPageDTO  errorPageDTO : dtos)
+        {
+            String name = errorPageDTO.name;
+            assertTrue(CONTEXT_ENTITY_NAMES.get(contextName).contains(name.substring(2)));
+
+            if (contextId != ID_B)
+            {
+                assertTrue(name,
+                        errorPageDTO.serviceId < 0);
+            }
+            else
+            {
+                assertTrue(name,
+                        errorPageDTO.serviceId > 0);
+            }
+            serviceIds.add(errorPageDTO.serviceId);
+
+            assertEquals(name,
+                    contextId, errorPageDTO.servletContextId);
+
+            assertTrue(name,
+                    errorPageDTO.asyncSupported);
+
+            assertEquals(name,
+                    2, errorPageDTO.initParams.size());
+            assertEquals(name,
+                    "valOne_" + name, errorPageDTO.initParams.get("paramOne_" + name));
+            assertEquals(name,
+                    "valTwo_" + name, errorPageDTO.initParams.get("paramTwo_" + name));
+
+            assertEquals(name,
+                    "info_" + name, errorPageDTO.servletInfo);
+
+            assertEquals(name,
+                    2, errorPageDTO.errorCodes.length);
+            long[] errorCodes = copyOf(errorPageDTO.errorCodes, 2);
+            sort(errorCodes);
+            assertEquals(name,
+                    400, errorCodes[0]);
+            assertEquals(name,
+                    500, errorCodes[1]);
+
+            assertEquals(name,
+                    2, errorPageDTO.exceptions.length);
+            String[] exceptions = copyOf(errorPageDTO.exceptions, 2);
+            sort(exceptions);
+            assertEquals(name,
+                    "Bad request", exceptions[0]);
+            assertEquals(name,
+                    "Error", exceptions[1]);
+        }
+
+        return serviceIds;
+    }
+
+    private Collection<Long> assertListenerDTOs(String contextName, long contextId, ListenerDTO[] dtos)
+    {
+        Set<Long> serviceIds = new HashSet<Long>();
+        for (ListenerDTO listenerDTO : dtos)
+        {
+            assertEquals(contextId, listenerDTO.servletContextId);
+            serviceIds.add(listenerDTO.serviceId);
+        }
+
+        assertEquals(ID_LISTENER_1.longValue(), dtos[0].serviceId);
+        assertArrayEquals(new String[] { "org.test.interface_1" },
+                dtos[0].types);
+        if (dtos.length > 1)
+        {
+            assertEquals(ID_LISTENER_2.longValue(), dtos[1].serviceId);
+            assertArrayEquals(new String[] { "org.test.interface_1", "org.test.interface_2" }, dtos[1].types);
+        }
+
+        return serviceIds;
+    }
+
+    @Test
+    public void nullValuesInEntities() {
+        ContextHandler contextHandler = setupContext(context_0, "0", ID_0);
+
+        ServletInfo servletInfo = createServletInfo(0,
+                ID_COUNTER.incrementAndGet(),
+                "1",
+                new String[] { "/*" },
+                null,
+                true,
+                Collections.<String, String>emptyMap());
+        Servlet servlet = mock(Servlet.class);
+        ServletHandler servletHandler = new ServletHandler(null, context_0, servletInfo, servlet);
+        when(servlet.getServletInfo()).thenReturn("info_0");
+
+        FilterInfo filterInfo = createFilterInfo(0,
+                ID_COUNTER.incrementAndGet(),
+                "1",
+                null,
+                null,
+                null,
+                true,
+                null,
+                Collections.<String, String>emptyMap());
+        FilterHandler filterHandler = new FilterHandler(null, context_0, mock(Filter.class), filterInfo);
+
+        HandlerRuntime contextRuntime = new HandlerRuntime(asList(servletHandler), asList(filterHandler), Collections.<ErrorPage>emptyList(), ID_0);
+        setupRegistry(asList(contextHandler), asList(contextRuntime),
+                Collections.<Long, Collection<ServiceReference<?>>>emptyMap());
+
+        RuntimeDTO runtimeDTO = new RuntimeDTOBuilder(registry, runtimeAttributes).build();
+
+        assertEquals(1, runtimeDTO.servletContextDTOs.length);
+        assertEquals(1, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
+        assertEquals(1, runtimeDTO.servletContextDTOs[0].filterDTOs.length);
+
+        assertEquals(emptyMap(), runtimeDTO.servletContextDTOs[0].servletDTOs[0].initParams);
+
+        assertEquals(emptyMap(), runtimeDTO.servletContextDTOs[0].filterDTOs[0].initParams);
+        assertEquals(0, runtimeDTO.servletContextDTOs[0].filterDTOs[0].patterns.length);
+        assertEquals(0, runtimeDTO.servletContextDTOs[0].filterDTOs[0].regexs.length);
+    }
+
+    @Test
+    public void contextWithNoEntities() {
+        ContextHandler contextHandler_0 = setupContext(context_0, "0", ID_0);
+        ContextHandler contextHandler_A = setupContext(context_A, "A", ID_A);
+
+        setupRegistry(asList(contextHandler_0, contextHandler_A),
+                asList(HandlerRuntime.empty(ID_0), HandlerRuntime.empty(ID_A)),
+                Collections.<Long, Collection<ServiceReference<?>>>emptyMap());
+
+        RuntimeDTO runtimeDTO = new RuntimeDTOBuilder(registry, runtimeAttributes).build();
+
+        assertEquals(2, runtimeDTO.servletContextDTOs.length);
+        assertEquals(0, runtimeDTO.servletContextDTOs[0].servletDTOs.length);
+        assertEquals(0, runtimeDTO.servletContextDTOs[0].filterDTOs.length);
+        assertEquals(0, runtimeDTO.servletContextDTOs[1].servletDTOs.length);
+        assertEquals(0, runtimeDTO.servletContextDTOs[1].filterDTOs.length);
+    }
+
+    @Test
+    public void missingPatternInServletThrowsException()
+    {
+        expectedException.expect(IllegalArgumentException.class);
+        expectedException.expectMessage("patterns");
+
+        ContextHandler contextHandler = setupContext(context_0, "0", ID_0);
+
+        ServletInfo servletInfo = createServletInfo(0,
+                ID_COUNTER.incrementAndGet(),
+                "1",
+                null,
+                null,
+                true,
+                Collections.<String, String>emptyMap());
+        Servlet servlet = mock(Servlet.class);
+        ServletHandler servletHandler = new ServletHandler(null, context_0, servletInfo, servlet);
+        when(servlet.getServletInfo()).thenReturn("info_0");
+
+        HandlerRuntime contextRuntime = new HandlerRuntime(asList(servletHandler),
+                Collections.<FilterHandler>emptyList(),
+                Collections.<ErrorPage>emptyList(),
+                ID_0);
+        setupRegistry(asList(contextHandler), asList(contextRuntime),
+                Collections.<Long, Collection<ServiceReference<?>>> emptyMap());
+
+        new RuntimeDTOBuilder(registry, runtimeAttributes).build();
+    }
+}
\ No newline at end of file
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java
index 14840a2..cb7d720 100644
--- a/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/BaseIntegrationTest.java
@@ -20,7 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.ops4j.pax.exam.Constants.START_LEVEL_SYSTEM_BUNDLES;
 import static org.ops4j.pax.exam.Constants.START_LEVEL_TEST_BUNDLE;
 import static org.ops4j.pax.exam.CoreOptions.bootDelegationPackage;
@@ -38,11 +38,11 @@
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.Collection;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.Scanner;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 import javax.servlet.Filter;
@@ -64,10 +64,10 @@
 import org.ops4j.pax.exam.junit.Configuration;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.cm.ConfigurationAdmin;
-import org.osgi.service.cm.ConfigurationEvent;
-import org.osgi.service.cm.ConfigurationListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.http.HttpService;
 import org.osgi.service.http.NamespaceException;
@@ -89,7 +89,7 @@
         {
             this(null, null);
         }
-        
+
         public TestFilter(CountDownLatch initLatch, CountDownLatch destroyLatch)
         {
             m_initLatch = initLatch;
@@ -134,7 +134,7 @@
         {
             this(null, null);
         }
-        
+
         public TestServlet(CountDownLatch initLatch, CountDownLatch destroyLatch)
         {
             m_initLatch = initLatch;
@@ -308,7 +308,7 @@
             bootDelegationPackage("sun.*"),
             cleanCaches(),
             CoreOptions.systemProperty("logback.configurationFile").value("file:src/test/resources/logback.xml"), //
-//            CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787"),
+            //            CoreOptions.vmOption("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8787"),
 
             mavenBundle("org.slf4j", "slf4j-api").version("1.6.5").startLevel(START_LEVEL_SYSTEM_BUNDLES),
             mavenBundle("ch.qos.logback", "logback-core").version("1.0.6").startLevel(START_LEVEL_SYSTEM_BUNDLES),
@@ -372,61 +372,28 @@
         return result;
     }
 
-    protected org.osgi.service.cm.Configuration configureHttpService(Dictionary<?, ?> props) throws Exception
+    protected void configureHttpService(Dictionary<?, ?> props) throws Exception
     {
         final String pid = "org.apache.felix.http";
-        ServiceTracker tracker = new ServiceTracker(m_context, ConfigurationAdmin.class.getName(), null);
-        tracker.open();
 
-        ServiceRegistration reg = null;
-        org.osgi.service.cm.Configuration config = null;
+        Collection<ServiceReference<ManagedService>> serviceRefs = m_context.getServiceReferences(ManagedService.class, String.format("(%s=%s)", Constants.SERVICE_PID, pid));
+        assertNotNull("Unable to obtain managed configuration for " + pid, serviceRefs);
 
-        try
+        for (ServiceReference<ManagedService> serviceRef : serviceRefs)
         {
-            ConfigurationAdmin configAdmin = (ConfigurationAdmin) tracker.waitForService(TimeUnit.SECONDS.toMillis(5));
-            assertNotNull("No configuration admin service found?!", configAdmin);
-
-            final CountDownLatch latch = new CountDownLatch(1);
-            final int configEvent = (props != null) ? ConfigurationEvent.CM_UPDATED : ConfigurationEvent.CM_DELETED;
-
-            config = configAdmin.getConfiguration(pid, null);
-
-            reg = m_context.registerService(ConfigurationListener.class.getName(), new ConfigurationListener()
+            ManagedService service = m_context.getService(serviceRef);
+            try
             {
-                @Override
-                public void configurationEvent(ConfigurationEvent event)
-                {
-                    if (pid.equals(event.getPid()) && event.getType() == configEvent)
-                    {
-                        latch.countDown();
-                    }
-                }
-            }, null);
-
-            if (props != null)
-            {
-                config.update(props);
+                service.updated(props);
             }
-            else
+            catch (ConfigurationException ex)
             {
-                config.delete();
+                fail("Invalid configuration provisioned: " + ex.getMessage());
             }
-
-            assertTrue("Configuration not provisioned in time!", latch.await(5, TimeUnit.SECONDS));
-
-            // Needed to get Jetty restarted...
-            TimeUnit.MILLISECONDS.sleep(150);
-
-            return config;
-        }
-        finally
-        {
-            if (reg != null)
+            finally
             {
-                reg.unregister();
+                m_context.ungetService(serviceRef);
             }
-
-            tracker.close();
         }
     }
 
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/HttpJettyTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/HttpJettyTest.java
index 5666360..1869ae3 100644
--- a/http/itest/src/test/java/org/apache/felix/http/itest/HttpJettyTest.java
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/HttpJettyTest.java
@@ -226,23 +226,23 @@
             }
         };
 
-        register("/test1", servlet1);
-        register("/test2", servlet2);
-        register("/test.*", filter);
+        register("/test/1", servlet1);
+        register("/test/2", servlet2);
+        register("/test/.*", filter);
 
         assertTrue(initLatch.await(5, TimeUnit.SECONDS));
 
-        assertContent("1.1", createURL("/test1"));
-        assertContent("2.1", createURL("/test2"));
-        assertContent("2.2", createURL("/test2"));
-        assertContent("1.2", createURL("/test1"));
-        assertContent("2.3", createURL("/test2"));
+        assertContent("1.1", createURL("/test/1"));
+        assertContent("2.1", createURL("/test/2"));
+        assertContent("2.2", createURL("/test/2"));
+        assertContent("1.2", createURL("/test/1"));
+        assertContent("2.3", createURL("/test/2"));
 
-        assertResponseCode(SC_FORBIDDEN, createURL("/test2?param=forbidden"));
-        assertResponseCode(SC_NOT_FOUND, createURL("/?test=forbidden"));
+        assertResponseCode(SC_FORBIDDEN, createURL("/test/2?param=forbidden"));
+        assertResponseCode(SC_NOT_FOUND, createURL("/test?param=not_recognized"));
 
-        assertContent("2.4", createURL("/test2"));
-        assertContent("1.3", createURL("/test1"));
+        assertContent("2.4", createURL("/test/2"));
+        assertContent("1.3", createURL("/test/1"));
 
         assertResponseCode(SC_FORBIDDEN, createURL("/test?param=forbidden"));
 
@@ -259,8 +259,8 @@
     @Test
     public void testHandleSecurityInFilterOk() throws Exception
     {
-        CountDownLatch initLatch = new CountDownLatch(1);
-        CountDownLatch destroyLatch = new CountDownLatch(1);
+        CountDownLatch initLatch = new CountDownLatch(2);
+        CountDownLatch destroyLatch = new CountDownLatch(2);
 
         HttpContext context = new HttpContext()
         {
@@ -286,15 +286,20 @@
                 }
                 else if (request.getParameter("commit") != null)
                 {
-                    response.getWriter().append("Not allowed!");
-                    response.flushBuffer();
+                    if (!response.isCommitted())
+                    {
+                        response.getWriter().append("Not allowed!");
+                        response.flushBuffer();
+                    }
                 }
                 return false;
             }
         };
 
         TestFilter filter = new TestFilter(initLatch, destroyLatch);
+        TestServlet servlet = new TestServlet(initLatch, destroyLatch);
 
+        register("/foo", servlet, context);
         register("/.*", filter, context);
 
         URL url1 = createURL("/foo");
@@ -310,6 +315,7 @@
         assertContent(SC_OK, "Not allowed!", url4);
 
         unregister(filter);
+        unregister(servlet);
 
         assertTrue(destroyLatch.await(5, TimeUnit.SECONDS));
 
@@ -334,7 +340,7 @@
             {
                 assertEquals("", request.getContextPath());
                 assertEquals("/foo", request.getServletPath());
-                assertEquals("/a", request.getPathInfo()); // /a,b/c;d/e.f;g/h
+                assertEquals("/a;b/c;d/e;f;g/h", request.getPathInfo());
                 assertEquals("/foo/a;b/c;d/e;f;g/h", request.getRequestURI());
                 assertEquals("i=j+k&l=m", request.getQueryString());
             }
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/RequestDispatchTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/RequestDispatchTest.java
index 57cc6f7..5fba1f7 100644
--- a/http/itest/src/test/java/org/apache/felix/http/itest/RequestDispatchTest.java
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/RequestDispatchTest.java
@@ -45,7 +45,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
-import org.osgi.service.cm.Configuration;
 import org.osgi.service.http.NamespaceException;
 
 /**
@@ -152,7 +151,7 @@
                 {
                     assertEquals("", req.getContextPath());
                     assertEquals("/test", req.getServletPath());
-                    assertEquals("/test/forward", req.getPathInfo()); // XXX ?
+                    assertEquals("/forward", req.getPathInfo());
                     assertEquals("/test/forward", req.getRequestURI());
                     assertEquals("bar=qux&quu", req.getQueryString());
 
@@ -349,7 +348,7 @@
     public void testDispatchOnNonRootContextPathOk() throws Exception
     {
         // Configure HTTP on a different context path...
-        Configuration config = configureHttpService(createDictionary("org.apache.felix.http.context_path", "/context", "org.osgi.service.http.port", "8080"));
+        configureHttpService(createDictionary("org.apache.felix.http.context_path", "/context", "org.osgi.service.http.port", "8080"));
 
         try
         {
@@ -359,7 +358,7 @@
         }
         finally
         {
-            config.delete();
+            configureHttpService(null);
         }
     }