FELIX-4060 : Implement HTTP Service Update (RFC-189)

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1655753 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
index a3ee0d5..71045e9 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/FilterHandler.java
@@ -34,35 +34,28 @@
 public final class FilterHandler extends AbstractHandler implements Comparable<FilterHandler>
 {
     private final Filter filter;
-    private final Pattern regex;
-    private final int ranking;
-
-    public FilterHandler(ExtServletContext context, Filter filter, String pattern, int ranking, String name)
-    {
-        super(context, name);
-        this.filter = filter;
-        this.ranking = ranking;
-        this.regex = Pattern.compile(pattern);
-    }
+    private final FilterInfo filterInfo;
 
     public FilterHandler(ExtServletContext context, Filter filter, FilterInfo filterInfo)
     {
-        // TODO
         super(context, filterInfo.name);
         this.filter = filter;
-        this.ranking = filterInfo.ranking;
-        this.regex = Pattern.compile(filterInfo.regexs[0]);
+        this.filterInfo = filterInfo;
     }
 
     @Override
     public int compareTo(FilterHandler other)
     {
-        if (other.ranking == this.ranking)
+        if (other.filterInfo.ranking == this.filterInfo.ranking)
         {
-            return 0;
+            if (other.filterInfo.serviceId == this.filterInfo.serviceId)
+            {
+                return 0;
+            }
+            return other.filterInfo.serviceId > this.filterInfo.serviceId ? -1 : 1;
         }
 
-        return (other.ranking > this.ranking) ? 1 : -1;
+        return (other.filterInfo.ranking > this.filterInfo.ranking) ? 1 : -1;
     }
 
     @Override
@@ -78,12 +71,44 @@
 
     public String getPattern()
     {
-        return regex.toString();
+        final StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        if ( this.filterInfo.regexs != null )
+        {
+            for(final String p : this.filterInfo.regexs)
+            {
+                if ( first )
+                {
+                    first = false;
+                }
+                else
+                {
+                    sb.append(", ");
+                }
+                sb.append(p);
+            }
+        }
+        if ( this.filterInfo.patterns != null )
+        {
+            for(final String p : this.filterInfo.patterns)
+            {
+                if ( first )
+                {
+                    first = false;
+                }
+                else
+                {
+                    sb.append(", ");
+                }
+                sb.append(p);
+            }
+        }
+        return sb.toString();
     }
 
     public int getRanking()
     {
-        return ranking;
+        return filterInfo.ranking;
     }
 
     public void handle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException
@@ -113,7 +138,30 @@
             uri = "/";
         }
 
-        return this.regex.matcher(uri).matches();
+        if ( this.filterInfo.patterns != null )
+        {
+            for(final String p : this.filterInfo.patterns)
+            {
+                if ( Pattern.compile(p.replace(".", "\\.").replace("*", ".*")).matcher(uri).matches() )
+                {
+                    return true;
+                }
+            }
+        }
+        if ( this.filterInfo.regexs != null )
+        {
+            for(final String p : this.filterInfo.regexs)
+            {
+                if ( Pattern.compile(p).matcher(uri).matches() )
+                {
+                    return true;
+                }
+            }
+        }
+
+        // TODO implement servlet name matching
+
+        return false;
     }
 
     final void doHandle(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
index f93377f..5a1efbc 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ServletHandler.java
@@ -31,6 +31,7 @@
 import static org.apache.felix.http.base.internal.util.UriUtils.concat;
 
 import java.io.IOException;
+import java.util.regex.Pattern;
 
 import javax.servlet.DispatcherType;
 import javax.servlet.RequestDispatcher;
@@ -236,6 +237,7 @@
 
     private final String alias;
     private final Servlet servlet;
+    private final Pattern pattern;
 
     public ServletHandler(final ExtServletContext context,
             final Servlet servlet,
@@ -245,6 +247,7 @@
         super(context, servletInfo.name);
         this.servlet = servlet;
         this.alias = alias;
+        this.pattern = Pattern.compile(alias.replace(".", "\\.").replace("*", ".*"));
     }
 
     @Override
@@ -330,10 +333,11 @@
         {
             return uri.startsWith(this.alias);
         }
-        else
+        else if ( uri.equals(this.alias) || uri.startsWith(this.alias + "/") )
         {
-            return uri.equals(this.alias) || uri.startsWith(this.alias + "/");
+            return true;
         }
+        return this.pattern.matcher(uri).matches();
     }
 
     final void doHandle(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException
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 73571d5..41bef17 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
@@ -35,7 +35,6 @@
  * This class only provides information used at registration time, and as such differs slightly from {@link DTO}s like, {@link FilterDTO}.
  * </p>
  *
- * TODO - we should move this to the same place as {@link ExtHttpServiceRuntime}.
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @ConsumerType
@@ -81,6 +80,7 @@
      * Specifies the ranking order in which this filter should be called. Higher rankings are called first.
      */
     public int ranking = 0;
+    public long serviceId;
 
     /**
      * The dispatcher associations for the servlet filter.
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 55363a5..ab1ef3a 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
@@ -34,7 +34,6 @@
  * This class only provides information used at registration time, and as such differs slightly from {@link DTO}s like, {@link ServletDTO}.
  * </p>
  *
- * TODO - we should move this to the same place as {@link ExtHttpServiceRuntime}.
  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
  */
 @ConsumerType
@@ -72,4 +71,7 @@
      * The {@link HttpContext} for the servlet.
      */
     public HttpContext context;
+
+    public int ranking = 0;
+    public long serviceId;
 }
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 e937ae3..82b1eae 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
@@ -21,6 +21,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
 
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
@@ -49,6 +50,8 @@
     private final HashSet<Filter> localFilters;
     private final ServletContextManager contextManager;
 
+    private final AtomicLong serviceIdCounter = new AtomicLong(-1);
+
     public HttpServiceImpl(Bundle bundle, ServletContext context, HandlerRegistry handlerRegistry, ServletContextAttributeListener servletAttributeListener, boolean sharedContextAttributes)
     {
         this.bundle = bundle;
@@ -90,7 +93,18 @@
         return new DefaultHttpContext(this.bundle);
     }
 
-    public void registerFilter(Filter filter, FilterInfo filterInfo) throws ServletException
+    /**
+     * TODO As the filter can be registered with multiple patterns
+     *      we shouldn't pass the filter object around in order to
+     *      be able to get different instances (prototype scope).
+     *      Or we do the splitting into single pattern registrations
+     *      already before calling registerFilter()
+     * @param servlet
+     * @param servletInfo
+     * @throws ServletException
+     * @throws NamespaceException
+     */
+    public void registerFilter(final Filter filter, final FilterInfo filterInfo)
     {
         if (filter == null)
         {
@@ -111,10 +125,17 @@
 
         FilterHandler handler = new FilterHandler(getServletContext(filterInfo.context), filter, filterInfo);
         handler.setInitParams(filterInfo.initParams);
-        this.handlerRegistry.addFilter(handler);
+        try {
+            this.handlerRegistry.addFilter(handler);
+        } catch (ServletException e) {
+            // TODO create failure DTO
+        }
         this.localFilters.add(filter);
     }
 
+    /**
+     * @see org.apache.felix.http.api.ExtHttpService#registerFilter(javax.servlet.Filter, java.lang.String, java.util.Dictionary, int, org.osgi.service.http.HttpContext)
+     */
     @Override
     public void registerFilter(Filter filter, String pattern, Dictionary initParams, int ranking, HttpContext context) throws ServletException
     {
@@ -122,11 +143,28 @@
         {
             throw new IllegalArgumentException("Filter must not be null");
         }
-        String filterName = null; // XXX
-        FilterHandler handler = new FilterHandler(getServletContext(context), filter, pattern, ranking, filterName);
-        handler.setInitParams(initParams);
-        this.handlerRegistry.addFilter(handler);
-        this.localFilters.add(filter);
+        final FilterInfo info = new FilterInfo();
+        if ( initParams != null && initParams.size() > 0 )
+        {
+            info.initParams = new HashMap<String, String>();
+            Enumeration e = initParams.keys();
+            while (e.hasMoreElements())
+            {
+                Object key = e.nextElement();
+                Object value = initParams.get(key);
+
+                if ((key instanceof String) && (value instanceof String))
+                {
+                    info.initParams.put((String) key, (String) value);
+                }
+            }
+        }
+        info.patterns = new String[] {pattern};
+        info.context = context;
+        info.ranking = ranking;
+        info.serviceId = serviceIdCounter.getAndDecrement();
+
+        this.registerFilter(filter, info);
     }
 
     @Override
@@ -159,7 +197,7 @@
      * @throws ServletException
      * @throws NamespaceException
      */
-    public void registerServlet(Servlet servlet, ServletInfo servletInfo) throws ServletException, NamespaceException
+    public void registerServlet(Servlet servlet, ServletInfo servletInfo)
     {
         if (servlet == null)
         {
@@ -181,11 +219,40 @@
         for(final String pattern : servletInfo.patterns) {
             final ServletHandler handler = new ServletHandler(getServletContext(servletInfo.context), servlet, servletInfo, pattern);
             handler.setInitParams(servletInfo.initParams);
-            this.handlerRegistry.addServlet(handler);
+            try {
+                this.handlerRegistry.addServlet(handler);
+            } catch (ServletException e) {
+                // TODO create failure DTO
+            } catch (NamespaceException e) {
+                // TODO create failure DTO
+            }
             this.localServlets.add(servlet);
         }
     }
 
+    public void unregisterServlet(final Servlet servlet, final ServletInfo servletInfo)
+    {
+        if (servletInfo == null)
+        {
+            throw new IllegalArgumentException("ServletInfo cannot be null!");
+        }
+        if ( servletInfo.patterns != null )
+        {
+            this.handlerRegistry.removeServlet(servlet, true);
+            this.localServlets.remove(servlet);
+        }
+    }
+
+    public void unregisterFilter(final Filter filter, final FilterInfo filterInfo)
+    {
+        if (filterInfo == null)
+        {
+            throw new IllegalArgumentException("FilterInfo cannot be null!");
+        }
+        this.handlerRegistry.removeFilter(filter, true);
+        this.localFilters.remove(filter);
+    }
+
     /**
      * @see org.osgi.service.http.HttpService#registerServlet(java.lang.String, javax.servlet.Servlet, java.util.Dictionary, org.osgi.service.http.HttpContext)
      */
@@ -213,6 +280,8 @@
                 }
             }
         }
+        info.ranking = 0;
+        info.serviceId = serviceIdCounter.getAndDecrement();
         info.patterns = new String[] {alias};
         info.context = context;
 
@@ -253,12 +322,20 @@
         }
     }
 
+    /**
+     * Old whiteboard support
+     * @see org.apache.felix.http.api.ExtHttpService#unregisterFilter(javax.servlet.Filter)
+     */
     @Override
     public void unregisterFilter(Filter filter)
     {
         unregisterFilter(filter, true);
     }
 
+    /**
+     * Old whiteboard support
+     * @see org.apache.felix.http.api.ExtHttpService#unregisterServlet(javax.servlet.Servlet)
+     */
     @Override
     public void unregisterServlet(Servlet servlet)
     {
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
index ce7fce3..158a9fc 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ExtenderManager.java
@@ -29,6 +29,7 @@
 import org.apache.felix.http.base.internal.logger.SystemLogger;
 import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.service.HttpServiceImpl;
 import org.apache.felix.http.base.internal.whiteboard.HttpContextManager.HttpContextHolder;
 import org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker;
 import org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextHelperTracker;
@@ -37,7 +38,6 @@
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
-import org.osgi.service.http.HttpContext;
 import org.osgi.service.http.HttpService;
 import org.osgi.service.http.context.ServletContextHelper;
 import org.osgi.service.http.runtime.dto.ResourceDTO;
@@ -47,8 +47,6 @@
 @SuppressWarnings({ "deprecation" })
 public final class ExtenderManager
 {
-    static final String TYPE_FILTER = "f";
-    static final String TYPE_SERVLET = "s";
     static final String TYPE_RESOURCE = "r";
 
     /**
@@ -173,32 +171,30 @@
         }
     }
 
-    private void addInitParams(ServiceReference ref, AbstractMapping mapping)
+    /**
+     * Get the init parameters.
+     */
+    private Map<String, String> getInitParams(final ServiceReference<?> ref, final String prefix)
     {
-        for (String key : ref.getPropertyKeys())
+        Map<String, String> result = null;
+        for (final String key : ref.getPropertyKeys())
         {
-            String prefixKey = null;
-
-            if (mapping instanceof FilterMapping && key.startsWith(FILTER_INIT_PREFIX))
+            if ( key.startsWith(prefix))
             {
-                prefixKey = FILTER_INIT_PREFIX;
-            }
-            else if (mapping instanceof ServletMapping && key.startsWith(SERVLET_INIT_PREFIX))
-            {
-                prefixKey = SERVLET_INIT_PREFIX;
-            }
-
-            if (prefixKey != null)
-            {
-                String paramKey = key.substring(prefixKey.length());
-                String paramValue = getStringProperty(ref, key);
+                final String paramKey = key.substring(prefix.length());
+                final String paramValue = getStringProperty(ref, key);
 
                 if (paramValue != null)
                 {
-                    mapping.getInitParams().put(paramKey, paramValue);
+                    if ( result == null )
+                    {
+                        result = new HashMap<String, String>();
+                    }
+                    result.put(paramKey, paramValue);
                 }
             }
         }
+        return result;
     }
 
     public void add(ServletContextHelper service, ServiceReference ref)
@@ -268,17 +264,6 @@
         }
     }
 
-    private HttpContext getHttpContext(AbstractMapping mapping, ServiceReference ref)
-    {
-        Bundle bundle = ref.getBundle();
-        String contextName = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT);
-        if (!isEmpty(contextName))
-        {
-            return this.contextManager.getHttpContext(bundle, contextName, mapping, true);
-        }
-        return this.contextManager.getHttpContext(bundle, null, mapping);
-    }
-
     private void ungetHttpContext(AbstractMapping mapping, ServiceReference ref)
     {
         Bundle bundle = ref.getBundle();
@@ -293,18 +278,24 @@
 
     public void add(final Filter service, final ServiceReference ref)
     {
-        FilterInfo filterInfo = new FilterInfo();
-        filterInfo.name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME);
-        if ( filterInfo.name == null || filterInfo.name.isEmpty() )
+        final FilterInfo filterInfo = createFilterInfo(ref, true);
+        if ( filterInfo != null )
         {
-            filterInfo.name = service.getClass().getName();
+            ((HttpServiceImpl)this.httpService).registerFilter(service, filterInfo);
         }
-        filterInfo.asyncSupported = getBooleanProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED);
-        filterInfo.servletNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET);
-        filterInfo.patterns = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN);
-        filterInfo.ranking = getIntProperty(ref, Constants.SERVICE_RANKING, 0);
+    }
 
-        String[] dispatcherNames = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER);
+    private FilterInfo createFilterInfo(final ServiceReference<?> filterRef, final boolean log)
+    {
+        final FilterInfo filterInfo = new FilterInfo();
+        filterInfo.name = getStringProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_NAME);
+        filterInfo.asyncSupported = getBooleanProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_ASYNC_SUPPORTED);
+        filterInfo.servletNames = getStringArrayProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_SERVLET);
+        filterInfo.patterns = getStringArrayProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN);
+        filterInfo.ranking = getIntProperty(filterRef, Constants.SERVICE_RANKING, 0);
+        filterInfo.serviceId = (Long)filterRef.getProperty(Constants.SERVICE_ID);
+        filterInfo.initParams = getInitParams(filterRef, FILTER_INIT_PREFIX);
+        String[] dispatcherNames = getStringArrayProperty(filterRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_DISPATCHER);
         if (dispatcherNames != null && dispatcherNames.length > 0)
         {
             DispatcherType[] dispatchers = new DispatcherType[dispatcherNames.length];
@@ -317,50 +308,64 @@
 
         if (isEmpty(filterInfo.patterns))
         {
-            SystemLogger.debug("Ignoring Filter Service " + ref + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN +
-                    " is missing or empty");
-            return;
+            if ( log )
+            {
+                SystemLogger.debug("Ignoring Filter Service " + filterRef + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_FILTER_PATTERN +
+                        " is missing or empty");
+            }
+            return null;
         }
 
-        FilterMapping mapping = new FilterMapping(ref.getBundle(), service, filterInfo);
-        filterInfo.context = getHttpContext(mapping, ref); // XXX
-        addInitParams(ref, mapping);
-        addMapping(TYPE_FILTER, ref, mapping);
+        return filterInfo;
     }
 
-    public void add(Servlet service, ServiceReference ref)
+    private ServletInfo createServletInfo(final ServiceReference<?> servletRef, final boolean log)
     {
-        ServletInfo servletInfo = new ServletInfo();
-        servletInfo.name = getStringProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
-        if ( servletInfo.name == null || servletInfo.name.isEmpty() )
-        {
-            servletInfo.name = service.getClass().getName();
-        }
-        servletInfo.errorPage = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE);
-        servletInfo.patterns = getStringArrayProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN);
-        servletInfo.asyncSupported = getBooleanProperty(ref, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED);
+        final ServletInfo servletInfo = new ServletInfo();
+        servletInfo.name = getStringProperty(servletRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_NAME);
+        servletInfo.errorPage = getStringArrayProperty(servletRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ERROR_PAGE);
+        servletInfo.patterns = getStringArrayProperty(servletRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN);
+        servletInfo.asyncSupported = getBooleanProperty(servletRef, HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED);
+        servletInfo.initParams = getInitParams(servletRef, SERVLET_INIT_PREFIX);
+        servletInfo.ranking = getIntProperty(servletRef, Constants.SERVICE_RANKING, 0);
+        servletInfo.serviceId = (Long)servletRef.getProperty(Constants.SERVICE_ID);
 
         if (isEmpty(servletInfo.patterns))
         {
-            SystemLogger.debug("Ignoring Servlet Service " + ref + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN +
-                    "is missing or empty");
-            return;
+            if ( log ) {
+                SystemLogger.debug("Ignoring Servlet Service " + servletRef + ", " + HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN +
+                        "is missing or empty");
+            }
+            return null;
         }
-
-        final ServletMapping mapping = new ServletMapping(ref.getBundle(), service, servletInfo);
-        servletInfo.context = getHttpContext(mapping, ref); // XXX
-        addInitParams(ref, mapping);
-        addMapping(TYPE_SERVLET, ref, mapping);
+        return servletInfo;
     }
 
-    public void removeFilter(ServiceReference ref)
+    public void add(Servlet service, ServiceReference<?> ref)
     {
-        removeMapping(TYPE_FILTER, ref);
+        final ServletInfo servletInfo = createServletInfo(ref, true);
+        if ( servletInfo != null )
+        {
+            ((HttpServiceImpl)this.httpService).registerServlet(service, servletInfo);
+        }
     }
 
-    public void removeServlet(ServiceReference ref)
+    public void removeFilter(final Filter service, ServiceReference ref)
     {
-        removeMapping(TYPE_SERVLET, ref);
+        final FilterInfo filterInfo = createFilterInfo(ref, false);
+        if ( filterInfo != null )
+        {
+            ((HttpServiceImpl)this.httpService).unregisterFilter(service, filterInfo);
+        }
+    }
+
+    public void removeServlet(Servlet service, ServiceReference ref)
+    {
+        final ServletInfo servletInfo = createServletInfo(ref, false);
+        if ( servletInfo != null )
+        {
+            ((HttpServiceImpl)this.httpService).unregisterServlet(service, servletInfo);
+        }
     }
 
     private synchronized void unregisterAll()
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/FilterMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/FilterMapping.java
deleted file mode 100644
index 58819b2..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/FilterMapping.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.felix.http.base.internal.whiteboard;
-
-import javax.servlet.Filter;
-
-import org.apache.felix.http.base.internal.logger.SystemLogger;
-import org.apache.felix.http.base.internal.runtime.FilterInfo;
-import org.apache.felix.http.base.internal.service.HttpServiceImpl;
-import org.osgi.framework.Bundle;
-import org.osgi.service.http.HttpService;
-
-public final class FilterMapping extends AbstractMapping
-{
-    private final Filter filter;
-    private final FilterInfo filterInfo;
-
-    public FilterMapping(Bundle bundle, Filter filter, FilterInfo filterInfo)
-    {
-        super(bundle);
-        this.filter = filter;
-        this.filterInfo = filterInfo;
-    }
-
-    @Override
-    public void register(HttpService httpService)
-    {
-        if (!isRegistered() && (httpService instanceof HttpServiceImpl) && getContext() != null)
-        {
-            register((HttpServiceImpl) httpService);
-        }
-        else
-        {
-            // Warn the user that something strange is going on...
-            SystemLogger.warning("Unable to register filter for " + this.filterInfo.name + ", as no ExtHttpService seems to be present!", null);
-        }
-    }
-
-    @Override
-    public void unregister(HttpService httpService)
-    {
-        if (isRegistered() && (httpService instanceof HttpServiceImpl))
-        {
-            unregister((HttpServiceImpl) httpService);
-        }
-        else
-        {
-            // Warn the user that something strange is going on...
-            SystemLogger.warning("Unable to unregister filter for " + this.filterInfo.name + ", as no ExtHttpService seems to be present!", null);
-        }
-    }
-
-    Filter getFilter()
-    {
-        return filter;
-    }
-
-    private void register(HttpServiceImpl httpService)
-    {
-        if (!isRegistered() && getContext() != null)
-        {
-            try
-            {
-                httpService.registerFilter(this.filter, this.filterInfo);
-                setRegistered(true);
-            }
-            catch (Exception e)
-            {
-                // Warn that something might have gone astray...
-                SystemLogger.warning("Failed to register filter for " + this.filterInfo.name, null);
-                SystemLogger.debug("Failed to register filter for " + this.filterInfo.name + "; details:", e);
-            }
-        }
-    }
-
-    private void unregister(HttpServiceImpl httpService)
-    {
-        if (isRegistered())
-        {
-            try
-            {
-                httpService.unregisterFilter(this.filter);
-            }
-            catch (Exception e)
-            {
-                // Warn that something might have gone astray...
-                SystemLogger.debug("Failed to unregister filter for " + this.filterInfo.name, e);
-            }
-            finally
-            {
-                // Best effort: avoid mappings that are registered which is reality aren't registered...
-                setRegistered(false);
-            }
-        }
-    }
-}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletMapping.java
deleted file mode 100644
index ff8b98b..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/ServletMapping.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.felix.http.base.internal.whiteboard;
-
-import javax.servlet.Servlet;
-
-import org.apache.felix.http.base.internal.logger.SystemLogger;
-import org.apache.felix.http.base.internal.runtime.ServletInfo;
-import org.apache.felix.http.base.internal.service.HttpServiceImpl;
-import org.osgi.framework.Bundle;
-import org.osgi.service.http.HttpService;
-
-public final class ServletMapping extends AbstractMapping
-{
-    private final Servlet servlet;
-    private final ServletInfo servletInfo;
-
-    public ServletMapping(Bundle bundle, Servlet servlet, ServletInfo servletInfo)
-    {
-        super(bundle);
-        this.servlet = servlet;
-        this.servletInfo = servletInfo;
-    }
-
-    @Override
-    public void register(HttpService httpService)
-    {
-        if (!isRegistered() && (httpService instanceof HttpServiceImpl) && getContext() != null)
-        {
-            this.servletInfo.context = getContext(); // XXX
-            try
-            {
-                ((HttpServiceImpl) httpService).registerServlet(this.servlet, this.servletInfo);
-                setRegistered(true);
-            }
-            catch (Exception e)
-            {
-                // Warn that something might have gone astray...
-                SystemLogger.warning("Failed to register servlet for " + this.servletInfo.name, e);
-            }
-            setRegistered(true);
-        }
-    }
-
-    @Override
-    public void unregister(HttpService httpService)
-    {
-        if (isRegistered() && (httpService instanceof HttpServiceImpl))
-        {
-            try
-            {
-                ((HttpServiceImpl) httpService).unregisterServlet(this.servlet);
-            }
-            catch (Exception e)
-            {
-                // Warn that something might have gone astray...
-                SystemLogger.debug("Failed to unregister servlet for " + this.servletInfo.name, e);
-            }
-            finally
-            {
-                // Best effort: avoid mappings that are registered which is reality aren't registered...
-                setRegistered(false);
-            }
-        }
-    }
-
-    Servlet getServlet()
-    {
-        return this.servlet;
-    }
-}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
index 7c5a576..5cec516 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/FilterTracker.java
@@ -67,6 +67,6 @@
     @Override
     protected void removed(Filter service, ServiceReference ref)
     {
-        this.manager.removeFilter(ref);
+        this.manager.removeFilter(service, ref);
     }
 }
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java
index 47360a5..ff57dc4 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/whiteboard/tracker/ServletTracker.java
@@ -65,6 +65,6 @@
     @Override
     protected void removed(Servlet service, ServiceReference ref)
     {
-        this.manager.removeServlet(ref);
+        this.manager.removeServlet(service, ref);
     }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
index 8aaebac..600aac3 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/FilterHandlerTest.java
@@ -34,6 +34,7 @@
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -41,6 +42,7 @@
 {
     private Filter filter;
 
+    @Override
     @Before
     public void setUp()
     {
@@ -237,6 +239,7 @@
         assertTrue(h4.matches("/a/b/"));
     }
 
+    @Override
     protected AbstractHandler createHandler()
     {
         return createHandler("dummy", 0);
@@ -244,6 +247,9 @@
 
     private FilterHandler createHandler(String pattern, int ranking)
     {
-        return new FilterHandler(this.context, this.filter, pattern, ranking, null /* name */);
+        final FilterInfo info = new FilterInfo();
+        info.regexs = new String[] {pattern};
+        info.ranking = ranking;
+        return new FilterHandler(this.context, this.filter, info);
     }
 }
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
index 59cdada..033af49 100644
--- a/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/handler/HandlerRegistryTest.java
@@ -27,6 +27,7 @@
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
 import org.apache.felix.http.base.internal.runtime.ServletInfo;
 import org.junit.Test;
 import org.mockito.Mockito;
@@ -181,14 +182,23 @@
         HandlerRegistry hr = new HandlerRegistry();
 
         Filter filter = Mockito.mock(Filter.class);
-        FilterHandler handler = new FilterHandler(null, filter, "/aha", 1, "oho");
+        final FilterInfo info = new FilterInfo();
+        info.name = "oho";
+        info.patterns = new String[] {"/aha"};
+        info.ranking = 1;
+
+        FilterHandler handler = new FilterHandler(null, filter, info);
         assertEquals("Precondition", 0, hr.getFilters().length);
         hr.addFilter(handler);
         Mockito.verify(filter, Mockito.times(1)).init(Mockito.any(FilterConfig.class));
         assertEquals(1, hr.getFilters().length);
         assertSame(handler, hr.getFilters()[0]);
 
-        FilterHandler handler2 = new FilterHandler(null, filter, "/hihi", 2, "haha");
+        final FilterInfo info2 = new FilterInfo();
+        info2.name = "haha";
+        info2.patterns = new String[] {"/hihi"};
+        info2.ranking = 2;
+        FilterHandler handler2 = new FilterHandler(null, filter, info2);
         try
         {
             hr.addFilter(handler2);
@@ -212,7 +222,11 @@
         final HandlerRegistry hr = new HandlerRegistry();
 
         Filter filter = Mockito.mock(Filter.class);
-        final FilterHandler otherHandler = new FilterHandler(null, filter, "/two", 99, "two");
+        final FilterInfo info = new FilterInfo();
+        info.name = "two";
+        info.patterns = new String[] {"/two"};
+        info.ranking = 99;
+        final FilterHandler otherHandler = new FilterHandler(null, filter, info);
 
         Mockito.doAnswer(new Answer<Void>()
         {
@@ -231,7 +245,11 @@
             }
         }).when(filter).init(Mockito.any(FilterConfig.class));
 
-        FilterHandler handler = new FilterHandler(null, filter, "/one", 1, "one");
+        final FilterInfo info2 = new FilterInfo();
+        info2.name = "one";
+        info2.patterns = new String[] {"/one"};
+        info2.ranking = 1;
+        FilterHandler handler = new FilterHandler(null, filter, info2);
 
         try
         {
@@ -262,7 +280,11 @@
         ServletHandler servletHandler2 = new ServletHandler(null, servlet2, info2, "/ff");
         hr.addServlet(servletHandler2);
         Filter filter = Mockito.mock(Filter.class);
-        FilterHandler filterHandler = new FilterHandler(null, filter, "/f", 0, "f");
+        final FilterInfo fi = new FilterInfo();
+        fi.name = "f";
+        fi.patterns = new String[] {"/f"};
+        fi.ranking = 0;
+        FilterHandler filterHandler = new FilterHandler(null, filter, fi);
         hr.addFilter(filterHandler);
 
         assertEquals(2, hr.getServlets().length);