FELIX-4546 : Implement HttpServiceRuntime service. Apply patch from Thomas Baier

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1674868 13f79535-47bb-0310-9956-ffa450edef68
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 8fa9788..d435bee 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
@@ -17,7 +17,6 @@
 package org.apache.felix.http.base.internal.handler;
 
 import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_SERVLET_CONTEXT_FAILURE;
-import static org.osgi.service.http.runtime.dto.DTOConstants.FAILURE_REASON_VALIDATION_FAILED;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -267,45 +266,23 @@
         {
             servletRegistry.addServlet(handler);
         }
-        else if (errorPages != null && errorPages.length > 0)
+        if (errorPages != null && errorPages.length > 0)
         {
             getRegistry(handler.getContextServiceId()).addErrorPage(handler, errorPages);
         }
-        else
-        {
-            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_VALIDATION_FAILED,
-                "Neither patterns nor errorPages specified for " + handler.getName());
-        }
     }
 
     public synchronized void removeServlet(Servlet servlet, boolean destroy)
     {
-        try
-        {
-            servletRegistry.removeServlet(servlet, destroy);
-        }
-        catch (RegistrationFailureException e)
-        {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
+        servletRegistry.removeServlet(servlet, destroy);
     }
 
     public synchronized Servlet removeServlet(ServletInfo servletInfo)
     {
-        try
-        {
-            return servletRegistry.removeServlet(servletInfo);
-        }
-        catch (RegistrationFailureException e)
-        {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-        return null;
+        return servletRegistry.removeServlet(servletInfo);
     }
 
-    public synchronized void removeServlet(long contextId, ServletInfo servletInfo) throws RegistrationFailureException
+    public synchronized void removeServlet(long contextId, ServletInfo servletInfo)
     {
         String[] patterns = servletInfo.getPatterns();
         if (patterns != null && patterns.length > 0)
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 6fc17e5..aa7a4e6 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
@@ -27,6 +27,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.TreeSet;
 
 import javax.servlet.DispatcherType;
@@ -52,6 +53,7 @@
     private volatile ErrorsMapping errorsMapping = new ErrorsMapping();
 
     private final HandlerRankingMultimap<String> registeredErrorPages = new HandlerRankingMultimap<String>();
+    private final Set<ServletHandler> initFailures = new TreeSet<ServletHandler>();
 
     private final long serviceId;
 
@@ -132,7 +134,7 @@
     void addErrorPage(ServletHandler handler, String[] errorPages) throws RegistrationFailureException
     {
         Update<String> update = this.registeredErrorPages.add(errorPages, handler);
-        initHandlers(update.getInit());
+        initFirstHandler(update.getInit());
         this.errorsMapping = this.errorsMapping.update(update.getActivated(), update.getDeactivated());
         destroyHandlers(update.getDestroy());
     }
@@ -195,7 +197,7 @@
         return null;
     }
 
-    void removeErrorPage(ServletInfo info) throws RegistrationFailureException
+    void removeErrorPage(ServletInfo info)
     {
         ServletHandler handler = getServletHandler(info);
         if (handler == null)
@@ -204,9 +206,39 @@
         }
         String[] errorPages = handler.getServletInfo().getErrorPage();
         Update<String> update = this.registeredErrorPages.remove(errorPages, handler);
-        initHandlers(update.getInit());
-        this.errorsMapping = this.errorsMapping.update(update.getActivated(), update.getDeactivated());
+        Set<ServletHandler> initializedHandlers = initHandlers(update.getInit());
+        Map<String, ServletHandler> activated = update.getActivated();
+        activated = removeFailures(activated, initializedHandlers);
+        try
+        {
+            this.errorsMapping = this.errorsMapping.update(activated, update.getDeactivated());
+        }
+        catch (RegistrationFailureException e)
+        {
+            //this should not happen
+            throw new RuntimeException("Exception on error page removal", e);
+        }
         destroyHandlers(update.getDestroy());
+        this.initFailures.remove(handler);
+    }
+
+    private Map<String, ServletHandler> removeFailures(Map<String, ServletHandler> activated,
+        Set<ServletHandler> initializedHandlers)
+    {
+        if (activated.size() == initializedHandlers.size())
+        {
+            return activated;
+        }
+
+        Map<String, ServletHandler> result = new HashMap<String, ServletHandler>();
+        for (Map.Entry<String, ServletHandler> entry : activated.entrySet())
+        {
+            if (initializedHandlers.contains(entry.getValue()))
+            {
+                result.put(entry.getKey(), entry.getValue());
+            }
+        }
+        return result;
     }
 
     private ServletHandler getServletHandler(final ServletInfo servletInfo)
@@ -233,20 +265,44 @@
         return null;
     }
 
-    private void initHandlers(Collection<ServletHandler> handlers) throws RegistrationFailureException
+    private void initFirstHandler(Collection<ServletHandler> handlers) throws RegistrationFailureException
     {
+        if (handlers.isEmpty())
+        {
+            return;
+        }
+
+        ServletHandler handler = handlers.iterator().next();
+        try
+        {
+            handler.init();
+        }
+        catch (ServletException e)
+        {
+            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
+        }
+    }
+
+    private Set<ServletHandler> initHandlers(Collection<ServletHandler> handlers)
+    {
+        Set<ServletHandler> success = new TreeSet<ServletHandler>();
+        List<ServletHandler> failure = new ArrayList<ServletHandler>();
         for (ServletHandler servletHandler : handlers)
         {
             try
             {
                 servletHandler.init();
+                success.add(servletHandler);
             }
             catch (ServletException e)
             {
-                // TODO we should collect this cases and not throw an exception immediately
-                throw new RegistrationFailureException(servletHandler.getServletInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
+                failure.add(servletHandler);
             }
         }
+
+        this.initFailures.addAll(failure);
+
+        return success;
     }
 
     private void destroyHandlers(Collection<? extends AbstractHandler<?>> servletHandlers)
@@ -298,6 +354,10 @@
 
     private boolean referencesDispatcherType(FilterHandler handler, DispatcherType dispatcherType)
     {
+        if (dispatcherType == null)
+        {
+            return true;
+        }
         return Arrays.asList(handler.getFilterInfo().getDispatcher()).contains(dispatcherType);
     }
 
@@ -355,18 +415,18 @@
             errorPages.add(ErrorPageRuntime.fromServletRuntime(servletHandler));
         }
 
-        addShadowedHandlers(failureRuntimeBuilder, this.registeredErrorPages.getShadowedValues());
+        addFailures(failureRuntimeBuilder, this.registeredErrorPages.getShadowedValues(), FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+        addFailures(failureRuntimeBuilder, this.initFailures, FAILURE_REASON_EXCEPTION_ON_INIT);
 
-        return new ContextRuntime(filterRuntimes,
-                errorPages,
-                this.serviceId);
+        return new ContextRuntime(filterRuntimes, errorPages, this.serviceId);
     }
 
-    private void addShadowedHandlers(FailureRuntime.Builder failureRuntimeBuilder, Collection<ServletHandler> handlers)
+    private void addFailures(FailureRuntime.Builder failureRuntimeBuilder, Collection<ServletHandler> handlers, int failureCode)
     {
         for (ServletHandler handler : handlers)
         {
-            failureRuntimeBuilder.add(handler.getServletInfo(), FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+            failureRuntimeBuilder.add(handler.getServletInfo(), failureCode);
         }
     }
+
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRegistry.java
index ab8daa9..82edda4 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandlerRegistry.java
@@ -29,6 +29,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.SortedSet;
 import java.util.TreeMap;
 import java.util.TreeSet;
@@ -52,6 +53,7 @@
 {
     private final HandlerRankingMultimap<String> registeredServletHandlers;
     private final SortedSet<ServletHandler> allServletHandlers = new TreeSet<ServletHandler>();
+    private final Set<ServletHandler> initFailures = new TreeSet<ServletHandler>();
 
     private volatile ContextRegistry contextRegistry;
 
@@ -89,7 +91,7 @@
         if (this.allServletHandlers.contains(handler))
         {
             throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_SERVICE_ALREAY_USED,
-                "Filter instance " + handler.getName() + " already registered");
+                "Servlet instance " + handler.getName() + " already registered");
         }
 
         registerServlet(handler);
@@ -99,20 +101,12 @@
 
     private void registerServlet(ServletHandler handler) throws RegistrationFailureException
     {
-        String contextPath = contextRegistry.getPath(handler.getContextServiceId());
-        if (contextPath == null)
-        {
-            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_SERVLET_CONTEXT_FAILURE);
-        }
-        contextPath = contextPath.equals("/") ? "" : contextPath;
-        List<String> patterns = new ArrayList<String>(asList(handler.getServletInfo().getPatterns()));
-        for (int i = 0; i < patterns.size(); i++)
-        {
-            patterns.set(i, contextPath + patterns.get(i));
-        }
+        long contextId = handler.getContextServiceId();
+        List<String> patterns = getFullPathsChecked(handler);
+
         Update<String> update = this.registeredServletHandlers.add(patterns, handler);
-        initHandlers(update.getInit());
-        contextRegistry = contextRegistry.updateServletMapping(update, handler.getContextServiceId());
+        initFirstHandler(update.getInit());
+        contextRegistry = contextRegistry.updateServletMapping(update.getActivated(), update.getDeactivated(), contextId);
         destroyHandlers(update.getDestroy());
     }
 
@@ -128,17 +122,17 @@
         this.registeredServletHandlers.clear();
     }
 
-    synchronized Servlet removeServlet(ServletInfo servletInfo) throws RegistrationFailureException
+    synchronized Servlet removeServlet(ServletInfo servletInfo)
     {
         return removeServlet(0L, servletInfo, true);
     }
 
-    synchronized Servlet removeServlet(Long contextId, ServletInfo servletInfo) throws RegistrationFailureException
+    synchronized Servlet removeServlet(Long contextId, ServletInfo servletInfo)
     {
         return removeServlet(contextId, servletInfo, true);
     }
 
-    synchronized Servlet removeServlet(Long contextId, ServletInfo servletInfo, final boolean destroy) throws RegistrationFailureException
+    synchronized Servlet removeServlet(Long contextId, ServletInfo servletInfo, final boolean destroy)
     {
         ServletHandler handler = getServletHandler(servletInfo);
         if (handler == null)
@@ -173,7 +167,7 @@
         return null;
     }
 
-    synchronized void removeServlet(Servlet servlet, final boolean destroy) throws RegistrationFailureException
+    synchronized void removeServlet(Servlet servlet, final boolean destroy)
     {
         Iterator<ServletHandler> it = this.allServletHandlers.iterator();
         List<ServletHandler> removals = new ArrayList<ServletHandler>();
@@ -192,39 +186,108 @@
         }
     }
 
-    private void removeServlet(ServletHandler handler, boolean destroy) throws RegistrationFailureException
+    private void removeServlet(ServletHandler handler, boolean destroy)
+    {
+        long contextId = handler.getContextServiceId();
+        List<String> patterns = getFullPaths(handler);
+        Update<String> update = this.registeredServletHandlers.remove(patterns, handler);
+        Set<ServletHandler> initializedHandlers = initHandlers(update.getInit());
+        Map<String, ServletHandler> activated = update.getActivated();
+        activated = removeFailures(activated, initializedHandlers);
+        contextRegistry = contextRegistry.updateServletMapping(activated, update.getDeactivated(), contextId);
+        if (destroy)
+        {
+            destroyHandlers(update.getDestroy());
+        }
+        this.initFailures.remove(handler);
+        this.allServletHandlers.remove(handler);
+    }
+
+    private Map<String, ServletHandler> removeFailures(Map<String, ServletHandler> activated,
+        Set<ServletHandler> initializedHandlers)
+    {
+        if (activated.size() == initializedHandlers.size())
+        {
+            return activated;
+        }
+
+        Map<String, ServletHandler> result = new HashMap<String, ServletHandler>();
+        for (Map.Entry<String, ServletHandler> entry : activated.entrySet())
+        {
+            if (initializedHandlers.contains(entry.getValue()))
+            {
+                result.put(entry.getKey(), entry.getValue());
+            }
+        }
+        return result;
+    }
+
+    private List<String> getFullPathsChecked(ServletHandler handler) throws RegistrationFailureException
     {
         String contextPath = contextRegistry.getPath(handler.getContextServiceId());
+        if (contextPath == null)
+        {
+            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_SERVLET_CONTEXT_FAILURE);
+        }
+        return getFullPaths(contextPath, handler);
+    }
+
+    private List<String> getFullPaths(ServletHandler handler)
+    {
+        String contextPath = contextRegistry.getPath(handler.getContextServiceId());
+        return getFullPaths(contextPath, handler);
+    }
+
+    private List<String> getFullPaths(String contextPath, ServletHandler handler)
+    {
         contextPath = contextPath.equals("/") ? "" : contextPath;
+
         List<String> patterns = new ArrayList<String>(asList(handler.getServletInfo().getPatterns()));
         for (int i = 0; i < patterns.size(); i++)
         {
             patterns.set(i, contextPath + patterns.get(i));
         }
-        Update<String> update = this.registeredServletHandlers.remove(patterns, handler);
-        initHandlers(update.getInit());
-        contextRegistry = contextRegistry.updateServletMapping(update, handler.getContextServiceId());
-        if (destroy)
-        {
-            destroyHandlers(update.getDestroy());
-        }
-        this.allServletHandlers.remove(handler);
+        return patterns;
     }
 
-    private void initHandlers(Collection<ServletHandler> handlers) throws RegistrationFailureException
+    private void initFirstHandler(Collection<ServletHandler> handlers) throws RegistrationFailureException
     {
+        if (handlers.isEmpty())
+        {
+            return;
+        }
+
+        ServletHandler handler = handlers.iterator().next();
+        try
+        {
+            handler.init();
+        }
+        catch (ServletException e)
+        {
+            throw new RegistrationFailureException(handler.getServletInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
+        }
+    }
+
+    private Set<ServletHandler> initHandlers(Collection<ServletHandler> handlers)
+    {
+        Set<ServletHandler> success = new TreeSet<ServletHandler>();
+        List<ServletHandler> failure = new ArrayList<ServletHandler>();
         for (ServletHandler servletHandler : handlers)
         {
             try
             {
                 servletHandler.init();
+                success.add(servletHandler);
             }
             catch (ServletException e)
             {
-                // TODO we should collect this cases and not throw an exception immediately
-                throw new RegistrationFailureException(servletHandler.getServletInfo(), FAILURE_REASON_EXCEPTION_ON_INIT, e);
+                failure.add(servletHandler);
             }
         }
+
+        this.initFailures.addAll(failure);
+
+        return success;
     }
 
     private void destroyHandlers(Collection<? extends AbstractHandler<?>> servletHandlers)
@@ -248,7 +311,8 @@
 
     synchronized ServletRegistryRuntime getRuntime(FailureRuntime.Builder failureRuntimeBuilder)
     {
-        addShadowedHandlers(failureRuntimeBuilder, this.registeredServletHandlers.getShadowedValues());
+        addFailures(failureRuntimeBuilder, this.registeredServletHandlers.getShadowedValues(), FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+        addFailures(failureRuntimeBuilder, this.initFailures, FAILURE_REASON_EXCEPTION_ON_INIT);
 
         Collection<ServletRuntime> servletRuntimes = new TreeSet<ServletRuntime>(ServletRuntime.COMPARATOR);
         Collection<ServletRuntime> resourceRuntimes = new TreeSet<ServletRuntime>(ServletRuntime.COMPARATOR);
@@ -266,11 +330,11 @@
         return new ServletRegistryRuntime(servletRuntimes, resourceRuntimes);
     }
 
-    private void addShadowedHandlers(FailureRuntime.Builder failureRuntimeBuilder, Collection<ServletHandler> handlers)
+    private void addFailures(FailureRuntime.Builder failureRuntimeBuilder, Collection<ServletHandler> handlers, int failureCode)
     {
         for (ServletHandler handler : handlers)
         {
-            failureRuntimeBuilder.add(handler.getServletInfo(), FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+            failureRuntimeBuilder.add(handler.getServletInfo(), failureCode);
         }
     }
 
@@ -381,7 +445,7 @@
             return ids;
         }
 
-        ContextRegistry updateServletMapping(Update<String> update, long contextId)
+        ContextRegistry updateServletMapping(Map<String, ServletHandler> activated, Map<String, ServletHandler> deactivated, long contextId)
         {
             Map<Long, HandlerMapping<ServletHandler>> newServletMappingsPerContext = new HashMap<Long, HandlerMapping<ServletHandler>>(servletMappingsPerContext);
             HandlerMapping<ServletHandler> servletMapping = newServletMappingsPerContext.get(contextId);
@@ -389,7 +453,7 @@
             {
                 servletMapping = new HandlerMapping<ServletHandler>();
             }
-            newServletMappingsPerContext.put(contextId, servletMapping.update(convert(update.getActivated()), convert(update.getDeactivated())));
+            newServletMappingsPerContext.put(contextId, servletMapping.update(convert(activated), convert(deactivated)));
             return new ContextRegistry(contextRankingsPerId, contextRankings, newServletMappingsPerContext);
         }
 
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 b9173e1..b2dc85e 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
@@ -157,7 +157,7 @@
     @Override
     public boolean isValid()
     {
-        return super.isValid() && (isEmpty(this.patterns) ^ isEmpty(this.errorPage));
+        return super.isValid() && !(isEmpty(this.patterns) && isEmpty(this.errorPage));
     }
 
     public String getName()
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
index 99361f8..ddcde61 100644
--- 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
@@ -36,7 +36,7 @@
         this.dtoFactory = dtoFactory;
     }
 
-    Collection<U> build(Collection<T> whiteboardServices, long servletContextId)
+    Collection<U> build(Collection<? extends T> whiteboardServices, long servletContextId)
     {
         List<U> dtoList = new ArrayList<U>();
         for (T whiteboardService : whiteboardServices)
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RequestInfoDTOBuilder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RequestInfoDTOBuilder.java
new file mode 100644
index 0000000..9c1a9be
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/runtime/dto/RequestInfoDTOBuilder.java
@@ -0,0 +1,66 @@
+/*
+ * 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 org.apache.felix.http.base.internal.handler.FilterHandler;
+import org.apache.felix.http.base.internal.handler.HandlerRegistry;
+import org.apache.felix.http.base.internal.handler.ServletHandler;
+import org.osgi.service.http.runtime.dto.FilterDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
+
+public final class RequestInfoDTOBuilder
+{
+    private static final FilterDTO[] FILTER_DTO_ARRAY = new FilterDTO[0];
+
+    private final HandlerRegistry registry;
+    private final String path;
+
+    public RequestInfoDTOBuilder(HandlerRegistry registry, String path)
+    {
+        this.registry = registry;
+        this.path = path;
+    }
+
+    public RequestInfoDTO build()
+    {
+        ServletHandler servletHandler = registry.getServletHandler(path);
+        FilterHandler[] filterHandlers = registry.getFilterHandlers(servletHandler, null, path);
+        Long contextServiceId = servletHandler.getContextServiceId();
+
+        RequestInfoDTO requestInfoDTO = new RequestInfoDTO();
+        requestInfoDTO.path = path;
+        requestInfoDTO.servletContextId = contextServiceId;
+
+        requestInfoDTO.filterDTOs = FilterDTOBuilder.create()
+                .build(asList(filterHandlers), contextServiceId)
+                .toArray(FILTER_DTO_ARRAY);
+
+        if (servletHandler.getServletInfo().isResource())
+        {
+            requestInfoDTO.resourceDTO = ResourceDTOBuilder.create()
+                    .buildDTO(servletHandler, contextServiceId);
+        }
+        else
+        {
+            requestInfoDTO.servletDTO = ServletDTOBuilder.create()
+                    .buildDTO(servletHandler, contextServiceId);
+        }
+        return requestInfoDTO;
+    }
+}
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
index d6b2eab..aae2679 100644
--- 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
@@ -23,6 +23,7 @@
 
 import org.apache.felix.http.base.internal.handler.HandlerRegistry;
 import org.apache.felix.http.base.internal.runtime.dto.RegistryRuntime;
+import org.apache.felix.http.base.internal.runtime.dto.RequestInfoDTOBuilder;
 import org.apache.felix.http.base.internal.runtime.dto.RuntimeDTOBuilder;
 import org.apache.felix.http.base.internal.whiteboard.WhiteboardManager;
 import org.osgi.service.http.runtime.HttpServiceRuntime;
@@ -31,7 +32,7 @@
 
 public final class HttpServiceRuntimeImpl implements HttpServiceRuntime
 {
-    private final Hashtable<String, Object> attributes = new Hashtable<String, Object>();
+    private volatile Hashtable<String, Object> attributes = new Hashtable<String, Object>();
 
     private final HandlerRegistry registry;
     private final WhiteboardManager contextManager;
@@ -44,7 +45,7 @@
     }
 
     @Override
-    public synchronized RuntimeDTO getRuntimeDTO()
+    public RuntimeDTO getRuntimeDTO()
     {
         RegistryRuntime runtime = contextManager.getRuntime(registry);
         RuntimeDTOBuilder runtimeDTOBuilder = new RuntimeDTOBuilder(runtime, attributes);
@@ -54,25 +55,28 @@
     @Override
     public RequestInfoDTO calculateRequestInfoDTO(String path)
     {
-        return null;
+        return new RequestInfoDTOBuilder(registry, path).build();
     }
 
     public synchronized void setAttribute(String name, Object value)
     {
-        attributes.put(name, value);
+        Hashtable<String, Object> newAttributes = new Hashtable<String, Object>(attributes);
+        newAttributes.put(name, value);
+        attributes = newAttributes;
     }
 
-    public synchronized void setAllAttributes(Dictionary<String, Object> attributes)
+    public synchronized void setAllAttributes(Dictionary<String, Object> newAttributes)
     {
-        this.attributes.clear();
-        for (String key :list(attributes.keys()))
+        Hashtable<String, Object> replacement = new Hashtable<String, Object>();
+        for (String key : list(newAttributes.keys()))
         {
-            this.attributes.put(key, attributes.get(key));
+            replacement.put(key, newAttributes.get(key));
         }
+        attributes = replacement;
     }
 
-    public synchronized Dictionary<String, Object> getAttributes()
+    public Dictionary<String, Object> getAttributes()
     {
-        return attributes;
+        return new Hashtable<String, Object>(attributes);
     }
 }
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 65f6863..29523ce 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
@@ -74,10 +74,6 @@
         {
             throw e;
         }
-        catch (final ServletException e)
-        {
-            throw new RegistrationFailureException(servletInfo, FAILURE_REASON_EXCEPTION_ON_INIT, e);
-        }
     }
 
     /**
diff --git a/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java b/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java
index 2c022a6..5dee199 100644
--- a/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java
+++ b/http/itest/src/test/java/org/apache/felix/http/itest/HttpServiceRuntimeTest.java
@@ -48,11 +48,15 @@
 import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN;
 import static org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -67,10 +71,13 @@
 import javax.servlet.http.HttpSessionAttributeListener;
 import javax.servlet.http.HttpSessionListener;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.junit.ExamReactorStrategy;
 import org.ops4j.pax.exam.junit.JUnit4TestRunner;
+import org.ops4j.pax.exam.spi.reactors.EagerSingleStagedReactorFactory;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
@@ -79,14 +86,37 @@
 import org.osgi.service.http.runtime.HttpServiceRuntime;
 import org.osgi.service.http.runtime.dto.FailedErrorPageDTO;
 import org.osgi.service.http.runtime.dto.FailedServletDTO;
+import org.osgi.service.http.runtime.dto.RequestInfoDTO;
 import org.osgi.service.http.runtime.dto.RuntimeDTO;
 import org.osgi.service.http.runtime.dto.ServletContextDTO;
 
 @RunWith(JUnit4TestRunner.class)
+@ExamReactorStrategy( EagerSingleStagedReactorFactory.class )
 public class HttpServiceRuntimeTest extends BaseIntegrationTest
 {
+    Collection<ServiceRegistration<?>> registrations = new ArrayList<ServiceRegistration<?>>();
+
     private static final long DEFAULT_SLEEP = 100;
 
+    @After
+    public void unregisterServices() throws Exception
+    {
+        for (ServiceRegistration<?> serviceRegistration : registrations)
+        {
+            try
+            {
+                serviceRegistration.unregister();
+            }
+            catch (Exception e)
+            {
+                // already unregistered
+            }
+        }
+        registrations.clear();
+
+        Thread.sleep(100);
+    }
+
     private void registerServlet(String name, String path) throws InterruptedException
     {
         CountDownLatch initLatch = new CountDownLatch(1);
@@ -109,7 +139,7 @@
         Dictionary<String, ?> properties = createDictionary(context == null ?
                 propertyEntries.subList(0, 4).toArray() : propertyEntries.toArray());
 
-        m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties));
     }
 
     private void registerFilter(String name, String path) throws InterruptedException
@@ -134,7 +164,7 @@
         Dictionary<String, ?> properties = createDictionary(context == null ?
                 propertyEntries.subList(0, 4).toArray() : propertyEntries.toArray());
 
-        m_context.registerService(Filter.class.getName(), new TestFilter(initLatch, null), properties);
+        registrations.add(m_context.registerService(Filter.class.getName(), new TestFilter(initLatch, null), properties));
     }
 
     private void registerResource(String prefix, String path) throws InterruptedException
@@ -152,7 +182,7 @@
         Dictionary<String, ?> properties = createDictionary(context == null ?
                 propertyEntries.subList(0, 4).toArray() : propertyEntries.toArray());
 
-        m_context.registerService(TestResource.class.getName(), new TestResource(), properties);
+        registrations.add(m_context.registerService(TestResource.class.getName(), new TestResource(), properties));
         awaitServiceRegistration();
     }
 
@@ -178,7 +208,7 @@
         Dictionary<String, ?> properties = createDictionary(context == null ?
                 propertyEntries.subList(0, 4).toArray() : propertyEntries.toArray());
 
-        m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties));
     }
 
     private void registerListener(Class<?> listenerClass, boolean useWithWhiteboard) throws InterruptedException
@@ -195,7 +225,7 @@
         Dictionary<String, ?> properties = createDictionary(context == null ?
                 propertyEntries.subList(0, 2).toArray() : propertyEntries.toArray());
 
-        m_context.registerService(listenerClass.getName(), mock(listenerClass), properties);
+        registrations.add(m_context.registerService(listenerClass.getName(), mock(listenerClass), properties));
         awaitServiceRegistration();
     }
 
@@ -206,6 +236,7 @@
                 HTTP_WHITEBOARD_CONTEXT_PATH, path);
 
         ServiceRegistration<?> contextRegistration = m_context.registerService(ServletContextHelper.class.getName(), mock(ServletContextHelper.class), properties);
+        registrations.add(contextRegistration);
         awaitServiceRegistration();
         return contextRegistration;
     }
@@ -383,6 +414,61 @@
     }
 
     @Test
+    public void dtosForSuccesfullyRegisteredErrorPageForClientErrorCodes() throws Exception
+    {
+        dtosForSuccesfullyRegisteredErrorPageWithWildcardErrorCode("4xx", 400);
+    }
+
+    @Test
+    public void dtosForSuccesfullyRegisteredErrorPageForClientErrorCodesCaseInsensitive() throws Exception
+    {
+        dtosForSuccesfullyRegisteredErrorPageWithWildcardErrorCode("4xX", 400);
+    }
+
+    @Test
+    public void dtosForSuccesfullyRegisteredErrorPageForServerErrorCodes() throws Exception
+    {
+        dtosForSuccesfullyRegisteredErrorPageWithWildcardErrorCode("5xx", 500);
+    }
+
+    @Test
+    public void dtosForSuccesfullyRegisteredErrorPageForServerErrorCodesCaseInsensitive() throws Exception
+    {
+        dtosForSuccesfullyRegisteredErrorPageWithWildcardErrorCode("5XX", 500);
+    }
+
+    public void dtosForSuccesfullyRegisteredErrorPageWithWildcardErrorCode(String code, long startCode) throws Exception
+    {
+        registerErrorPage("error page 1", asList(code));
+
+        HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
+        assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
+
+        RuntimeDTO runtimeDTOWithErrorPage = serviceRuntime.getRuntimeDTO();
+
+        assertEquals(0, runtimeDTOWithErrorPage.failedServletDTOs.length);
+        assertEquals(0, runtimeDTOWithErrorPage.failedErrorPageDTOs.length);
+
+        ServletContextDTO contextDTO = assertDefaultContext(runtimeDTOWithErrorPage);
+        assertEquals(1, contextDTO.errorPageDTOs.length);
+        assertEquals("error page 1", contextDTO.errorPageDTOs[0].name);
+        assertContainsAllHundredFrom(startCode, contextDTO.errorPageDTOs[0].errorCodes);
+    }
+
+    private void assertContainsAllHundredFrom(Long start, long[] errorCodes)
+    {
+        assertEquals(100, errorCodes.length);
+        SortedSet<Long> distinctErrorCodes = new TreeSet<Long>();
+        for (Long code : errorCodes)
+        {
+            distinctErrorCodes.add(code);
+        }
+        assertEquals(100, distinctErrorCodes.size());
+        assertEquals(start, distinctErrorCodes.first());
+        assertEquals(Long.valueOf(start + 99), distinctErrorCodes.last());
+    }
+
+    @Test
     public void dtosForSuccesfullyRegisteredListeners() throws Exception
     {
         // register a servlet context listenere as first listener
@@ -562,7 +648,7 @@
             }
         };
 
-        m_context.registerService(Servlet.class.getName(), failingServlet, properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), failingServlet, properties));
         awaitServiceRegistration(initLatch);
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
@@ -575,6 +661,81 @@
     }
 
     @Test
+    public void exceptionInServletInitDuringServletRemovalAppearsAsFailure() throws ServletException, InterruptedException
+    {
+        Dictionary<String, ?> properties1 = createDictionary(
+            HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet1",
+            HTTP_WHITEBOARD_SERVLET_NAME, "servlet1");
+
+        final CountDownLatch initLatch1 = new CountDownLatch(1);
+
+        @SuppressWarnings("serial")
+        Servlet failingServlet1 = new TestServlet(initLatch1, null) {
+            @Override
+            public void init() throws ServletException
+            {
+                //fail when initialized the second time
+                if (initLatch1.getCount() == 0)
+                {
+                    throw new ServletException();
+                }
+                super.init();
+            }
+        };
+
+        Dictionary<String, ?> properties2 = createDictionary(
+            HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet2",
+            HTTP_WHITEBOARD_SERVLET_NAME, "servlet2");
+
+        final CountDownLatch initLatch2 = new CountDownLatch(1);
+        @SuppressWarnings("serial")
+        Servlet failingServlet2 = new TestServlet(initLatch2, null) {
+            @Override
+            public void init() throws ServletException
+            {
+                //fail when initialized the second time
+                if (initLatch2.getCount() == 0)
+                {
+                    throw new ServletException();
+                }
+                super.init();
+            }
+        };
+
+        Dictionary<String, ?> propertiesShadowing = createDictionary(
+            HTTP_WHITEBOARD_SERVLET_PATTERN, asList("/servlet1", "/servlet2"),
+            HTTP_WHITEBOARD_SERVLET_NAME, "servletShadowing",
+            SERVICE_RANKING, Integer.MAX_VALUE);
+
+        CountDownLatch initLatchShadowing = new CountDownLatch(1);
+        Servlet servletShadowing = new TestServlet(initLatchShadowing, null);
+
+        registrations.add(m_context.registerService(Servlet.class.getName(), failingServlet1, properties1));
+        registrations.add(m_context.registerService(Servlet.class.getName(), failingServlet2, properties2));
+        awaitServiceRegistration(initLatch1);
+        awaitServiceRegistration(initLatch2);
+
+        ServiceRegistration<?> shadowingRegistration = m_context.registerService(Servlet.class.getName(), servletShadowing, propertiesShadowing);
+        registrations.add(shadowingRegistration);
+        awaitServiceRegistration(initLatchShadowing);
+
+        HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
+        assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
+
+        RuntimeDTO runtimeDTO = serviceRuntime.getRuntimeDTO();
+        assertEquals(2, runtimeDTO.failedServletDTOs.length);
+        assertEquals(FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE, runtimeDTO.failedServletDTOs[0].failureReason);
+        assertEquals(FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE, runtimeDTO.failedServletDTOs[1].failureReason);
+
+        shadowingRegistration.unregister();
+
+        runtimeDTO = serviceRuntime.getRuntimeDTO();
+        assertEquals(2, runtimeDTO.failedServletDTOs.length);
+        assertEquals(FAILURE_REASON_EXCEPTION_ON_INIT, runtimeDTO.failedServletDTOs[0].failureReason);
+        assertEquals(FAILURE_REASON_EXCEPTION_ON_INIT, runtimeDTO.failedServletDTOs[1].failureReason);
+    }
+
+    @Test
     public void exceptionInFilterInitAppearsAsFailure() throws ServletException, InterruptedException
     {
         Dictionary<String, ?> properties = createDictionary(
@@ -592,7 +753,7 @@
             }
         };
 
-        m_context.registerService(Filter.class.getName(), failingFilter, properties);
+        registrations.add(m_context.registerService(Filter.class.getName(), failingFilter, properties));
         awaitServiceRegistration(initLatch);
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
@@ -663,7 +824,7 @@
     {
         Dictionary<String, ?> properties = createDictionary(HTTP_WHITEBOARD_CONTEXT_PATH, "");
 
-        m_context.registerService(ServletContextHelper.class.getName(), mock(ServletContextHelper.class), properties);
+        registrations.add(m_context.registerService(ServletContextHelper.class.getName(), mock(ServletContextHelper.class), properties));
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
         assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
@@ -743,7 +904,7 @@
                 HTTP_WHITEBOARD_SERVLET_NAME, "servlet",
                 HTTP_WHITEBOARD_TARGET, "(org.osgi.service.http.port=8282)");
 
-        m_context.registerService(Servlet.class.getName(), new TestServlet(), properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), new TestServlet(), properties));
         awaitServiceRegistration();
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
@@ -764,7 +925,7 @@
         Dictionary<String, ?> properties = createDictionary(HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet");
 
         CountDownLatch initLatch = new CountDownLatch(1);
-        m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties));
         awaitServiceRegistration(initLatch);
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
@@ -781,27 +942,33 @@
 
     // As specified in OSGi Compendium Release 6, Chapter 140.4.1
     @Test
-    public void patternAndErrorPageSpecifiedInvalidAndAppearsAsFailure() throws InterruptedException
+    public void patternAndErrorPageSpecified() throws InterruptedException
     {
         Dictionary<String, ?> properties = createDictionary(
                 HTTP_WHITEBOARD_SERVLET_PATTERN, "/servlet",
                 HTTP_WHITEBOARD_SERVLET_NAME, "servlet",
                 HTTP_WHITEBOARD_SERVLET_ERROR_PAGE, asList("400"));
 
-        m_context.registerService(Servlet.class.getName(), new TestServlet(), properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), new TestServlet(), properties));
         awaitServiceRegistration();
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
         assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
 
-        ServletContextDTO defaultContext = assertDefaultContext(serviceRuntime.getRuntimeDTO());
-        assertEquals(0, defaultContext.servletDTOs.length);
-        assertEquals(0, defaultContext.errorPageDTOs.length);
+        RuntimeDTO runtimeDTO = serviceRuntime.getRuntimeDTO();
+        ServletContextDTO defaultContext = assertDefaultContext(runtimeDTO);
 
-        assertEquals(0, serviceRuntime.getRuntimeDTO().failedServletDTOs.length);
-        assertEquals(1, serviceRuntime.getRuntimeDTO().failedErrorPageDTOs.length);
-        assertEquals("servlet", serviceRuntime.getRuntimeDTO().failedErrorPageDTOs[0].name);
-        assertEquals(FAILURE_REASON_VALIDATION_FAILED, serviceRuntime.getRuntimeDTO().failedErrorPageDTOs[0].failureReason);
+        assertEquals(0, runtimeDTO.failedErrorPageDTOs.length);
+        assertEquals(0, runtimeDTO.failedServletDTOs.length);
+
+        assertEquals(1, defaultContext.servletDTOs.length);
+        assertEquals(1, defaultContext.errorPageDTOs.length);
+
+        assertEquals("servlet", defaultContext.servletDTOs[0].name);
+        assertEquals("servlet", defaultContext.errorPageDTOs[0].name);
+
+        assertArrayEquals(new String[] { "/servlet" }, defaultContext.servletDTOs[0].patterns);
+        assertArrayEquals(new long[] { 400 }, defaultContext.errorPageDTOs[0].errorCodes);
     }
 
     // As specified in OSGi Compendium Release 6, Chapter 140.4.1
@@ -828,6 +995,7 @@
         CountDownLatch destroyLatch = new CountDownLatch(1);
         TestServlet testServlet = new TestServlet(initLatch, destroyLatch);
         ServiceRegistration<?> higherRankingServlet = m_context.registerService(Servlet.class.getName(), testServlet, properties);
+        registrations.add(higherRankingServlet);
 
         RuntimeDTO runtimeWithShadowedServlet = serviceRuntime.getRuntimeDTO();
         awaitServiceRegistration(initLatch);
@@ -876,6 +1044,7 @@
         CountDownLatch destroyLatch = new CountDownLatch(1);
         TestServlet testServlet = new TestServlet(initLatch, destroyLatch);
         ServiceRegistration<?> higherRankingServlet = m_context.registerService(Servlet.class.getName(), testServlet, properties);
+        registrations.add(higherRankingServlet);
         awaitServiceRegistration(initLatch);
 
         RuntimeDTO runtimeWithShadowedErrorPage = serviceRuntime.getRuntimeDTO();
@@ -960,7 +1129,7 @@
     {
         Dictionary<String, ?> properties = createDictionary(HTTP_WHITEBOARD_LISTENER, "invalid");
 
-        m_context.registerService(ServletRequestListener.class.getName(), mock(ServletRequestListener.class), properties);
+        registrations.add(m_context.registerService(ServletRequestListener.class.getName(), mock(ServletRequestListener.class), properties));
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
         assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
@@ -1004,6 +1173,7 @@
                 SERVICE_RANKING, Integer.MAX_VALUE);
 
         ServiceRegistration<?> secondContext = m_context.registerService(ServletContextHelper.class.getName(), mock(ServletContextHelper.class), properties);
+        registrations.add(secondContext);
         Long secondContextId = (Long) secondContext.getReference().getProperty(Constants.SERVICE_ID);
 
         runtimeDTO = serviceRuntime.getRuntimeDTO();
@@ -1078,7 +1248,7 @@
         // Neither pattern nor error page specified
         Dictionary<String, ?> properties = createDictionary(HTTP_WHITEBOARD_SERVLET_NAME, "servlet");
 
-        m_context.registerService(Servlet.class.getName(), new TestServlet(), properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), new TestServlet(), properties));
         awaitServiceRegistration();
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
@@ -1101,7 +1271,7 @@
                 HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + "test", "testValue");
 
         CountDownLatch initLatch = new CountDownLatch(1);
-        m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties);
+        registrations.add(m_context.registerService(Servlet.class.getName(), new TestServlet(initLatch, null), properties));
         awaitServiceRegistration(initLatch);
 
         HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
@@ -1137,6 +1307,37 @@
         }
     }
 
+    @Test
+    public void requestInfoDTO() throws Exception
+    {
+        registerServlet("servlet", "/default");
+        registerFilter("filter1", "/default");
+        registerFilter("filter2", "/default");
+
+        HttpServiceRuntime serviceRuntime = (HttpServiceRuntime) getService(HttpServiceRuntime.class.getName());
+        assertNotNull("HttpServiceRuntime unavailable", serviceRuntime);
+
+        ServletContextDTO defaultContext = assertDefaultContext(serviceRuntime.getRuntimeDTO());
+        long defaultContextId = defaultContext.serviceId;
+
+        RequestInfoDTO requestInfoDTO = serviceRuntime.calculateRequestInfoDTO("/default");
+        assertEquals("/default", requestInfoDTO.path);
+        assertEquals(defaultContextId, requestInfoDTO.servletContextId);
+        assertEquals("servlet", requestInfoDTO.servletDTO.name);
+        assertEquals(2, requestInfoDTO.filterDTOs.length);
+        assertEquals("filter1", requestInfoDTO.filterDTOs[0].name);
+        assertEquals("filter2", requestInfoDTO.filterDTOs[1].name);
+    }
+
+    @Test
+    public void serviceEndpointPropertyIsSet()
+    {
+        String[] endpoint = (String[]) m_context.getServiceReference(HttpServiceRuntime.class).getProperty(HTTP_SERVICE_ENDPOINT_ATTRIBUTE);
+        assertEquals(1, endpoint.length);
+        assertTrue(endpoint[0].startsWith("http://"));
+        assertTrue(endpoint[0].endsWith(":8080/"));
+    }
+
     private ServletContextDTO assertDefaultContext(RuntimeDTO runtimeDTO)
     {
         assertTrue(1 < runtimeDTO.servletContextDTOs.length);