You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by fm...@apache.org on 2011/12/05 21:29:09 UTC

svn commit: r1210612 [1/2] - in /felix/trunk/http/whiteboard/src: main/java/org/apache/felix/http/whiteboard/ main/java/org/apache/felix/http/whiteboard/internal/ main/java/org/apache/felix/http/whiteboard/internal/manager/ main/java/org/apache/felix/h...

Author: fmeschbe
Date: Mon Dec  5 20:29:08 2011
New Revision: 1210612

URL: http://svn.apache.org/viewvc?rev=1210612&view=rev
Log:
FELIX-3226 Improve HttpContext whiteboard support
  - export constants
  - allow for sharing HttpContext services across bundles
    (must be declared)
  - delay Servlet/Filter registration as long as HttpContext
    service referred to is missing
  - add unit tests
FELIX-2882 service instance as key
  - replace the service instances by the service reference
    as the key of the mappings

Added:
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java
      - copied, changed from r1203903, felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java
    felix/trunk/http/whiteboard/src/test/
    felix/trunk/http/whiteboard/src/test/java/
    felix/trunk/http/whiteboard/src/test/java/org/
    felix/trunk/http/whiteboard/src/test/java/org/apache/
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerTest.java
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/FilterMappingTest.java
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManagerTest.java
    felix/trunk/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ServletMappingTest.java
Removed:
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
Modified:
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java
    felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java

Added: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java?rev=1210612&view=auto
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java (added)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java Mon Dec  5 20:29:08 2011
@@ -0,0 +1,128 @@
+/*
+ * 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.whiteboard;
+
+/**
+ * The <code>HttpWhiteboardConstants</code> defines constants for values
+ * used by the Http Whiteboard registration support.
+ *
+ * @since Http Whiteboard Bundle 2.3.0
+ */
+public class HttpWhiteboardConstants
+{
+
+    /**
+     * The service registration property indicating the name of a
+     * <code>HttpContext</code> service.
+     * <p>
+     * If the property is set to a non-empty string for an
+     * <code>HttpContext</code> service it indicates the name by which it may be
+     * referred to by <code>Servlet</code> and <code>Filter</code> services.
+     * This is also a required registration property for
+     * <code>HttpService</code> services to be accepted by the Http Whiteboard
+     * registration.
+     * <p>
+     * If the property is set for a <code>Servlet</code> or <code>Filter</code>
+     * services it indicates the name of a registered <code>HttpContext</code>
+     * which is to be used for the registration with the Http Service. If the
+     * property is not set for a <code>Servlet</code> or <code>Filter</code>
+     * services or its value is the empty string, a default HttpContext is used
+     * which does no security handling and has no MIME type support and which
+     * returns resources from the servlet's or the filter's bundle.
+     * <p>
+     * The value of this service registration property is a single string.
+     */
+    public static final String CONTEXT_ID = "contextId";
+
+    /**
+     * The service registration property indicating whether a
+     * <code>HttpContext</code> service registered with the {@link #CONTEXT_ID}
+     * service registration
+     * property is shared across bundles or not. By default
+     * <code>HttpContext</code> services are only available to
+     * <code>Servlet</code> and <code>Filter</code> services registered by the
+     * same bundle.
+     * <p>
+     * If this property is set to <code>true</code> for <code>HttpContext</code>
+     * service, it may be referred to by <code>Servlet</code> or
+     * <code>Filter</code> services from different bundles.
+     * <p>
+     * <b>Recommendation:</b> Shared <code>HttpContext</code> services should
+     * either not implement the <code>getResource</code> at all or be registered
+     * as service factories to ensure no access to foreign bundle resources is
+     * not allowed through this backdoor.
+     * <p>
+     * The value of this service registration is a single boolean or string.
+     * Only if the boolean value is <code>true</code> (either by
+     * <code>Boolean.booleanValue()</code> or by
+     * <code>Boolean.valueOf(String)</code>) will the <code>HttpContext</code>
+     * be shared.
+     */
+    public static final String CONTEXT_SHARED = "context.shared";
+
+    /**
+     * The service registration property indicating the registration alias
+     * for a <code>Servlet</code> service. This value is used as the
+     * alias parameter for the <code>HttpService.registerServlet</code> call.
+     * <p>
+     * A <code>Servlet</code> service registered with this service property may
+     * also provide a {@link #CONTEXT_ID} property which referrs to a
+     * <code>HttpContext</code> service. If such a service is not registered
+     * (yet), the servlet will not be registered with the Http Service. Once the
+     * <code>HttpContext</code> service becomes available, the servlet is
+     * registered.
+     * <p>
+     * The value of this service registration property is a single string
+     * starting with a slash.
+     */
+    public static final String ALIAS = "alias";
+
+    /**
+     * The service registration property indicating the URL patter
+     * for a <code>Filter</code> service. This value is used as the
+     * pattern parameter for the <code>ExtHttpService.registerFilter</code>
+     * call.
+     * <p>
+     * A <code>Filter</code> service registered with this service property may
+     * also provide a {@link #CONTEXT_ID} property which referrs to a
+     * <code>HttpContext</code> service. If such a service is not registered
+     * (yet), the filter will not be registered with the Http Service. Once the
+     * <code>HttpContext</code> service becomes available, the filter is
+     * registered.
+     * <p>
+     * The value of this service registration property is a single string being
+     * a regular expression.
+     * <p>
+     * <b>Note:</b> <code>Filter</code> services are only supported if the Http
+     * Service implements the
+     * <code>org.apache.felix.http.api.ExtHttpService</code> interface.
+     */
+    public static final String PATTERN = "pattern";
+
+    /**
+     * Prefix for service registration properties being used as init parameters
+     * for the <code>Servlet</code> and <code>Filter</code> initialization.
+     */
+    public static final String INIT_PREFIX = "init.";
+
+    // no instances
+    private HttpWhiteboardConstants()
+    {
+    }
+}

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java Mon Dec  5 20:29:08 2011
@@ -23,7 +23,6 @@ import org.apache.felix.http.whiteboard.
 import org.apache.felix.http.whiteboard.internal.tracker.HttpContextTracker;
 import org.apache.felix.http.whiteboard.internal.tracker.ServletTracker;
 import org.apache.felix.http.whiteboard.internal.tracker.HttpServiceTracker;
-import org.apache.felix.http.whiteboard.internal.manager.ExtenderManagerImpl;
 import org.apache.felix.http.whiteboard.internal.manager.ExtenderManager;
 import org.apache.felix.http.whiteboard.internal.manager.HttpWhiteboardWebConsolePlugin;
 import org.apache.felix.http.base.internal.AbstractActivator;
@@ -46,13 +45,13 @@ public final class WhiteboardActivator
     protected void doStart()
         throws Exception
     {
-        this.manager = new ExtenderManagerImpl();
+        this.manager = new ExtenderManager();
         addTracker(new HttpContextTracker(getBundleContext(), this.manager));
         addTracker(new FilterTracker(getBundleContext(), this.manager));
         addTracker(new ServletTracker(getBundleContext(), this.manager));
         addTracker(new HttpServiceTracker(getBundleContext(), this.manager));
 
-        HttpWhiteboardWebConsolePlugin plugin = new HttpWhiteboardWebConsolePlugin((ExtenderManagerImpl) this.manager);
+        HttpWhiteboardWebConsolePlugin plugin = new HttpWhiteboardWebConsolePlugin(this.manager);
         Hashtable<String, Object> props = new Hashtable<String, Object>();
         props.put("felix.webconsole.label", plugin.getLabel());
         props.put("felix.webconsole.title", plugin.getTitle());

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java Mon Dec  5 20:29:08 2011
@@ -16,19 +16,35 @@
  */
 package org.apache.felix.http.whiteboard.internal.manager;
 
+import java.util.Hashtable;
+
+import org.osgi.framework.Bundle;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.http.HttpService;
-import java.util.Hashtable;
 
-public abstract class AbstractMapping
+abstract class AbstractMapping
 {
-    private final HttpContext context;
+    private final Bundle bundle;
+    private HttpContext context;
     private final Hashtable<String, String> initParams;
+    private boolean registered;
 
-    public AbstractMapping(HttpContext context)
+    protected AbstractMapping(final Bundle bundle)
     {
-        this.context = context;
+        this.bundle = bundle;
+        this.context = null;
         this.initParams = new Hashtable<String, String>();
+        this.registered = false;
+    }
+
+    public Bundle getBundle()
+    {
+        return bundle;
+    }
+
+    public void setContext(HttpContext context)
+    {
+        this.context = context;
     }
 
     public final HttpContext getContext()
@@ -41,6 +57,16 @@ public abstract class AbstractMapping
         return this.initParams;
     }
 
+    boolean isRegistered()
+    {
+        return registered;
+    }
+
+    void setRegistered(boolean registered)
+    {
+        this.registered = registered;
+    }
+
     public abstract void register(HttpService httpService);
 
     public abstract void unregister(HttpService httpService);

Copied: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java (from r1203903, felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java)
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java?p2=felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java&p1=felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java&r1=1203903&r2=1210612&rev=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java Mon Dec  5 20:29:08 2011
@@ -19,42 +19,58 @@ package org.apache.felix.http.whiteboard
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import javax.servlet.Filter;
 import javax.servlet.Servlet;
 
 import org.apache.felix.http.api.ExtHttpService;
 import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.whiteboard.HttpWhiteboardConstants;
+import org.apache.felix.http.whiteboard.internal.manager.HttpContextManager.HttpContextHolder;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.http.HttpContext;
 import org.osgi.service.http.HttpService;
 
-public final class ExtenderManagerImpl
-    implements ExtenderManager
+public final class ExtenderManager
 {
-    private final static String CONTEXT_ID_KEY = "contextId";
-    private final static String PATTERN_KEY = "pattern";
-    private final static String ALIAS_KEY = "alias";
-    private final static String INIT_KEY_PREFIX = "init.";
-
     private HttpService httpService;
-    private final HashMap<Object, AbstractMapping> mapping;
+    private final HashMap<ServiceReference, AbstractMapping> mapping;
     private final HttpContextManager contextManager;
 
-    public ExtenderManagerImpl()
+    public ExtenderManager()
     {
-        this.mapping = new HashMap<Object, AbstractMapping>();
+        this.mapping = new HashMap<ServiceReference, AbstractMapping>();
         this.contextManager = new HttpContextManager();
     }
 
+    static boolean isEmpty(final String value)
+    {
+        return value == null || value.length() == 0;
+    }
+
     private String getStringProperty(ServiceReference ref, String key)
     {
         Object value = ref.getProperty(key);
         return (value instanceof String) ? (String)value : null;
     }
 
+    private boolean getBooleanProperty(ServiceReference ref, String key)
+    {
+        Object value = ref.getProperty(key);
+        if (value instanceof String)
+        {
+            return Boolean.valueOf((String) value);
+        }
+        else if (value instanceof Boolean)
+        {
+            return ((Boolean) value).booleanValue();
+        }
+        return false;
+    }
+
     private int getIntProperty(ServiceReference ref, String key, int defValue)
     {
         Object value = ref.getProperty(key);
@@ -72,8 +88,8 @@ public final class ExtenderManagerImpl
     private void addInitParams(ServiceReference ref, AbstractMapping mapping)
     {
         for (String key : ref.getPropertyKeys()) {
-            if (key.startsWith(INIT_KEY_PREFIX)) {
-                String paramKey = key.substring(INIT_KEY_PREFIX.length());
+            if (key.startsWith(HttpWhiteboardConstants.INIT_PREFIX)) {
+                String paramKey = key.substring(HttpWhiteboardConstants.INIT_PREFIX.length());
                 String paramValue = getStringProperty(ref, key);
 
                 if (paramValue != null) {
@@ -85,64 +101,86 @@ public final class ExtenderManagerImpl
 
     public void add(HttpContext service, ServiceReference ref)
     {
-        Bundle bundle = ref.getBundle();
-        String contextId = getStringProperty(ref, CONTEXT_ID_KEY);
-        if (contextId != null) {
-            this.contextManager.addHttpContext(bundle, contextId, service);
+        String contextId = getStringProperty(ref, HttpWhiteboardConstants.CONTEXT_ID);
+        if (!isEmpty(contextId))
+        {
+            boolean shared = getBooleanProperty(ref, HttpWhiteboardConstants.CONTEXT_SHARED);
+            Bundle bundle = shared ? null : ref.getBundle();
+            Collection<AbstractMapping> mappings = this.contextManager.addHttpContext(bundle, contextId, service);
+            for (AbstractMapping mapping : mappings)
+            {
+                registerMapping(mapping);
+            }
+        }
+        else
+        {
+            SystemLogger.debug("Ignoring HttpContext Service " + ref + ", " + HttpWhiteboardConstants.CONTEXT_ID
+                + " is missing or empty");
         }
     }
 
     public void remove(HttpContext service)
     {
-        this.contextManager.removeHttpContext(service);
+        Collection<AbstractMapping> mappings = this.contextManager.removeHttpContext(service);
+        if (mappings != null)
+        {
+            for (AbstractMapping mapping : mappings)
+            {
+                unregisterMapping(mapping);
+            }
+        }
     }
 
-    private HttpContext getHttpContext(ServiceReference ref)
+    private void getHttpContext(AbstractMapping mapping, ServiceReference ref)
     {
         Bundle bundle = ref.getBundle();
-        String contextId = getStringProperty(ref, CONTEXT_ID_KEY);
+        String contextId = getStringProperty(ref, HttpWhiteboardConstants.CONTEXT_ID);
+        this.contextManager.getHttpContext(bundle, contextId, mapping);
+    }
 
-        if (contextId != null) {
-            return this.contextManager.getHttpContext(bundle, contextId);
-        } else {
-            return new DefaultHttpContext(bundle);
-        }
+    private void ungetHttpContext(AbstractMapping mapping, ServiceReference ref)
+    {
+        Bundle bundle = ref.getBundle();
+        String contextId = getStringProperty(ref, HttpWhiteboardConstants.CONTEXT_ID);
+        this.contextManager.ungetHttpContext(bundle, contextId, mapping);
     }
 
     public void add(Filter service, ServiceReference ref)
     {
         int ranking = getIntProperty(ref, Constants.SERVICE_RANKING, 0);
-        String pattern = getStringProperty(ref, PATTERN_KEY);
+        String pattern = getStringProperty(ref, HttpWhiteboardConstants.PATTERN);
 
-        if (pattern == null) {
+        if (isEmpty(pattern)) {
+            SystemLogger.debug("Ignoring Filter Service " + ref + ", " + HttpWhiteboardConstants.PATTERN
+                + " is missing or empty");
             return;
         }
 
-        FilterMapping mapping = new FilterMapping(getHttpContext(ref), service, pattern, ranking);
+        FilterMapping mapping = new FilterMapping(ref.getBundle(), service, pattern, ranking);
+        getHttpContext(mapping, ref);
         addInitParams(ref, mapping);
-        addMapping(service, mapping);
-    }
-
-    public void remove(Filter service)
-    {
-        removeMapping(service);
+        addMapping(ref, mapping);
     }
 
     public void add(Servlet service, ServiceReference ref)
     {
-        String alias = getStringProperty(ref, ALIAS_KEY);
-        if (alias == null) {
+        String alias = getStringProperty(ref, HttpWhiteboardConstants.ALIAS);
+        if (isEmpty(alias))
+        {
+            SystemLogger.debug("Ignoring Servlet Service " + ref + ", " + HttpWhiteboardConstants.ALIAS
+                + " is missing or empty");
             return;
         }
 
-        ServletMapping mapping = new ServletMapping(getHttpContext(ref), service, alias);
+        ServletMapping mapping = new ServletMapping(ref.getBundle(), service, alias);
+        getHttpContext(mapping, ref);
         addInitParams(ref, mapping);
-        addMapping(service, mapping);
+        addMapping(ref, mapping);
     }
 
-    public void remove(Servlet service)
+    public void remove(ServiceReference ref)
     {
-        removeMapping(service);
+        removeMapping(ref);
     }
 
     public synchronized void setHttpService(HttpService service)
@@ -199,26 +237,64 @@ public final class ExtenderManagerImpl
     	}
     }
 
-    private synchronized void addMapping(Object key, AbstractMapping mapping)
+    private synchronized void addMapping(ServiceReference ref, AbstractMapping mapping)
+    {
+        this.mapping.put(ref, mapping);
+        this.registerMapping(mapping);
+    }
+
+    private synchronized void removeMapping(ServiceReference ref)
     {
-        this.mapping.put(key, mapping);
-        if (this.httpService != null) {
-            mapping.register(this.httpService);
+        AbstractMapping mapping = this.mapping.remove(ref);
+        if (mapping != null)
+        {
+            ungetHttpContext(mapping, ref);
+            unregisterMapping(mapping);
+        }
+    }
+
+    private void registerMapping(AbstractMapping mapping)
+    {
+        HttpService httpService = this.httpService;
+        if (httpService != null)
+        {
+            mapping.register(httpService);
         }
     }
 
-    private synchronized void removeMapping(Object key)
+    private void unregisterMapping(AbstractMapping mapping)
     {
-        AbstractMapping mapping = this.mapping.remove(key);
-        if ((mapping != null) && (this.httpService != null)) {
-            mapping.unregister(this.httpService);
+        HttpService httpService = this.httpService;
+        if (httpService != null)
+        {
+            mapping.unregister(httpService);
         }
     }
 
-    Map<String, HttpContext> getHttpContexts() {
+    /**
+     * Returns
+     * {@link org.apache.felix.http.whiteboard.internal.manager.HttpContextManager.HttpContextHolder}
+     * instances of HttpContext services.
+     *
+     * @return
+     */
+    Map<String, HttpContextHolder> getHttpContexts()
+    {
         return this.contextManager.getHttpContexts();
     }
 
+    /**
+     * Returns {@link AbstractMapping} instances for which there is no
+     * registered HttpContext as desired by the context ID.
+     */
+    Map<String, Set<AbstractMapping>> getOrphanMappings()
+    {
+        return this.contextManager.getOrphanMappings();
+    }
+
+    /**
+     * Returns mappings indexed by there owning OSGi service.
+     */
     Map<Object, AbstractMapping> getMappings()
     {
         synchronized (this)

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java Mon Dec  5 20:29:08 2011
@@ -16,12 +16,12 @@
  */
 package org.apache.felix.http.whiteboard.internal.manager;
 
-import org.osgi.service.http.HttpService;
-import org.osgi.service.http.HttpContext;
+import javax.servlet.Filter;
+
 import org.apache.felix.http.api.ExtHttpService;
 import org.apache.felix.http.base.internal.logger.SystemLogger;
-
-import javax.servlet.Filter;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpService;
 
 public final class FilterMapping
     extends AbstractMapping
@@ -30,9 +30,9 @@ public final class FilterMapping
     private final int ranking;
     private final String pattern;
 
-    public FilterMapping(HttpContext context, Filter filter, String pattern, int ranking)
+    public FilterMapping(Bundle bundle, Filter filter, String pattern, int ranking)
     {
-        super(context);
+        super(bundle);
         this.filter = filter;
         this.pattern = pattern;
         this.ranking = ranking;
@@ -62,17 +62,26 @@ public final class FilterMapping
 
     private void register(ExtHttpService httpService)
     {
-        try {
-            httpService.registerFilter(this.filter, this.pattern, getInitParams(), ranking, getContext());
-        } catch (Exception e) {
-            SystemLogger.error("Failed to register filter", e);
+        if (!this.isRegistered() && getContext() != null)
+        {
+            try
+            {
+                httpService.registerFilter(this.filter, this.pattern, getInitParams(), ranking, getContext());
+                setRegistered(true);
+            }
+            catch (Exception e)
+            {
+                SystemLogger.error("Failed to register filter", e);
+            }
         }
     }
 
     public void unregister(HttpService httpService)
     {
-        if (httpService instanceof ExtHttpService) {
-            unregister((ExtHttpService)httpService);
+        if (httpService instanceof ExtHttpService && this.isRegistered())
+        {
+            unregister((ExtHttpService) httpService);
+            setRegistered(false);
         }
     }
 

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java Mon Dec  5 20:29:08 2011
@@ -1,12 +1,12 @@
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
+ * 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
+ * the License. You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -19,59 +19,248 @@ package org.apache.felix.http.whiteboard
 import org.osgi.framework.Bundle;
 import org.osgi.service.http.HttpContext;
 import org.apache.felix.http.base.internal.logger.SystemLogger;
+
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Set;
 
 public final class HttpContextManager
 {
-    private final HashMap<String, HttpContext> idMap;
+    /**
+     * HttpContextHolders indexed by context ID fully configured
+     * with an HttpContext and optional servlets and filters.
+     * <p>
+     * The context ID either includes the bundle ID as the first part of the
+     * name, such as <i>123-sample.context</i> in the case of non-shared
+     * contexts. IDs of shared contexts are prefixed with the fixed string
+     * <code>shared</code> to not mix them with per-bundle contexts.
+     */
+    private final HashMap<String, HttpContextHolder> idMap;
+
+    /**
+     * Reverse mapping of HttpContext services to the context ID with
+     * which they are registered.
+     */
     private final HashMap<HttpContext, String> contextMap;
 
+    /**
+     * Map of servlets and filters registered referring to unregistered
+     * contexts as of yet.
+     */
+    private final HashMap<String, Set<AbstractMapping>> orphanMappings;
+
     public HttpContextManager()
     {
-        this.idMap = new HashMap<String, HttpContext>();
+        this.idMap = new HashMap<String, HttpContextHolder>();
         this.contextMap = new HashMap<HttpContext, String>();
+        this.orphanMappings = new HashMap<String, Set<AbstractMapping>>();
     }
 
-    private String createId(Bundle bundle, String contextId)
+    private static String createId(Bundle bundle, String contextId)
     {
-        return bundle.getBundleId() + "-" + contextId;
+        if (bundle != null)
+        {
+            return bundle.getBundleId() + "-" + ((contextId == null) ? "" : contextId);
+        }
+
+        return createId(contextId);
     }
 
-    public synchronized HttpContext getHttpContext(Bundle bundle, String contextId)
+    private static String createId(String contextId)
     {
+        return "shared-" + ((contextId == null) ? "" : contextId);
+    }
+
+    private static String getContextId(String id)
+    {
+        final int dash = id.indexOf('-');
+        return (dash < 0) ? id : id.substring(dash + 1);
+    }
+
+    public synchronized HttpContext getHttpContext(Bundle bundle, String contextId, AbstractMapping mapping)
+    {
+        // per-bundle context
         String id = createId(bundle, contextId);
-        HttpContext context = this.idMap.get(id);
+        HttpContextHolder holder = this.idMap.get(id);
 
-        if (context == null) {
-            context = new DefaultHttpContext(bundle);
-            this.idMap.put(id, context);
-            this.contextMap.put(context, id);
-            SystemLogger.debug("Added context with id [" + contextId + "]");
-        } else {
-            SystemLogger.debug("Reusing context with id [" + contextId + "]");
+        // shared context
+        if (holder == null)
+        {
+            id = createId(contextId);
+            holder = this.idMap.get(id);
         }
 
-        return context;
+        // no context yet, put the mapping on hold
+        if (holder == null)
+        {
+
+            // care for default context if no context ID
+            if (ExtenderManager.isEmpty(contextId))
+            {
+                addHttpContext(bundle, "", new DefaultHttpContext(bundle));
+                return getHttpContext(bundle, "", mapping);
+            }
+
+            // otherwise context is not here yet
+            Set<AbstractMapping> orphaned = this.orphanMappings.get(contextId);
+            if (orphaned == null)
+            {
+                orphaned = new HashSet<AbstractMapping>();
+                this.orphanMappings.put(contextId, orphaned);
+            }
+            SystemLogger.debug("Holding off mapping with unregistered context with id [" + contextId + "]");
+            orphaned.add(mapping);
+            return null;
+        }
+
+        // otherwise use the context
+        SystemLogger.debug("Reusing context with id [" + contextId + "]");
+        holder.addMapping(mapping);
+        return holder.getContext();
     }
 
-    public synchronized void removeHttpContext(HttpContext context)
+    public synchronized void ungetHttpContext(Bundle bundle, String contextId, AbstractMapping mapping)
     {
-        String id = this.contextMap.remove(context);
-        if (id != null) {
-            this.idMap.remove(id);
+        // per-bundle context
+        String id = createId(bundle, contextId);
+        HttpContextHolder context = this.idMap.get(id);
+
+        // shared context
+        if (context == null)
+        {
+            id = createId(contextId);
+            context = this.idMap.get(id);
+        }
+
+        // remove the mapping if there is a mapped context
+        if (context != null)
+        {
+            context.removeMapping(mapping);
+        }
+        else
+        {
+            Set<AbstractMapping> orphans = this.orphanMappings.get(contextId);
+            if (orphans != null)
+            {
+                orphans.remove(mapping);
+                if (orphans.isEmpty())
+                {
+                    this.orphanMappings.remove(contextId);
+                }
+            }
+
+            // it is not expected but make sure there is no reference
+            mapping.setContext(null);
         }
     }
 
-    public synchronized void addHttpContext(Bundle bundle, String contextId, HttpContext context)
+    public synchronized Collection<AbstractMapping> addHttpContext(Bundle bundle, String contextId, HttpContext context)
     {
         String id = createId(bundle, contextId);
-        this.idMap.put(id, context);
+        HttpContextHolder holder = new HttpContextHolder(context);
+
+        Set<AbstractMapping> orphans = this.orphanMappings.remove(contextId);
+        if (orphans != null)
+        {
+            for (Iterator<AbstractMapping> mi = orphans.iterator(); mi.hasNext();)
+            {
+                AbstractMapping mapping = mi.next();
+                if (bundle == null || bundle.equals(mapping.getBundle()))
+                {
+                    holder.addMapping(mapping);
+                    mi.remove();
+                }
+            }
+
+            // put any remaining orphans back
+            if (!orphans.isEmpty())
+            {
+                this.orphanMappings.put(contextId, orphans);
+            }
+        }
+
+        this.idMap.put(id, holder);
         this.contextMap.put(context, id);
+
+        return holder.getMappings();
+    }
+
+    public synchronized Collection<AbstractMapping> removeHttpContext(HttpContext context)
+    {
+        String id = this.contextMap.remove(context);
+        if (id != null)
+        {
+            HttpContextHolder holder = this.idMap.remove(id);
+            if (holder != null)
+            {
+                Set<AbstractMapping> mappings = holder.getMappings();
+                if (mappings != null && !mappings.isEmpty())
+                {
+                    // keep the orphans around
+                    final String contextId = getContextId(id);
+                    Set<AbstractMapping> orphans = this.orphanMappings.get(contextId);
+                    if (orphans == null)
+                    {
+                        orphans = new HashSet<AbstractMapping>();
+                        this.orphanMappings.put(getContextId(id), orphans);
+                    }
+
+                    for (AbstractMapping mapping : mappings)
+                    {
+                        mapping.setContext(null);
+                        orphans.add(mapping);
+                    }
+                }
+                return mappings;
+            }
+        }
+        return null;
+    }
+
+    synchronized Map<String, HttpContextHolder> getHttpContexts()
+    {
+        return new HashMap<String, HttpContextHolder>(this.idMap);
+    }
+
+    synchronized Map<String, Set<AbstractMapping>> getOrphanMappings()
+    {
+        return new HashMap<String, Set<AbstractMapping>>(this.orphanMappings);
     }
 
-    public synchronized Map<String, HttpContext> getHttpContexts()
+    static class HttpContextHolder
     {
-        return new HashMap<String, HttpContext>(this.idMap);
+        private final HttpContext context;
+        private final Set<AbstractMapping> mappings;
+
+        HttpContextHolder(final HttpContext context)
+        {
+            this.context = context;
+            this.mappings = new HashSet<AbstractMapping>();
+        }
+
+        public HttpContext getContext()
+        {
+            return context;
+        }
+
+        void addMapping(AbstractMapping mapping)
+        {
+            this.mappings.add(mapping);
+            mapping.setContext(this.getContext());
+        }
+
+        void removeMapping(AbstractMapping mapping)
+        {
+            mapping.setContext(null);
+            this.mappings.remove(mapping);
+        }
+
+        public Set<AbstractMapping> getMappings()
+        {
+            return mappings;
+        }
     }
 }

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java Mon Dec  5 20:29:08 2011
@@ -27,13 +27,13 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.osgi.service.http.HttpContext;
+import org.apache.felix.http.whiteboard.internal.manager.HttpContextManager.HttpContextHolder;
 
 @SuppressWarnings("serial")
 public class HttpWhiteboardWebConsolePlugin extends HttpServlet
 {
 
-    private final ExtenderManagerImpl extMgr;
+    private final ExtenderManager extMgr;
 
     public String getLabel()
     {
@@ -45,7 +45,7 @@ public class HttpWhiteboardWebConsolePlu
         return "Http Whiteboard";
     }
 
-    public HttpWhiteboardWebConsolePlugin(final ExtenderManagerImpl extMgr)
+    public HttpWhiteboardWebConsolePlugin(final ExtenderManager extMgr)
     {
         this.extMgr = extMgr;
     }
@@ -92,12 +92,12 @@ public class HttpWhiteboardWebConsolePlu
         pw.println("<th class='content' colspan='3'>HttpContext</td>");
         pw.println("</tr>");
 
-        final Map<String, HttpContext> contexts = extMgr.getHttpContexts();
-        for (Map.Entry<String, HttpContext> handler : contexts.entrySet())
+        final Map<String, HttpContextHolder> contexts = extMgr.getHttpContexts();
+        for (Map.Entry<String, HttpContextHolder> handler : contexts.entrySet())
         {
             pw.println("<tr class='content'>");
             pw.println("<td class='content'>" + handler.getKey() + "</td>");
-            pw.println("<td class='content' colspan='3'>" + handler.getValue() + "</td>");
+            pw.println("<td class='content' colspan='3'>" + handler.getValue().getContext() + "</td>");
             pw.println("</tr>");
         }
     }
@@ -167,10 +167,10 @@ public class HttpWhiteboardWebConsolePlu
     private void printHttpContextServicesTxt(PrintWriter pw)
     {
         pw.println("Registered HttpContext Services");
-        final Map<String, HttpContext> contexts = extMgr.getHttpContexts();
-        for (Map.Entry<String, HttpContext> handler : contexts.entrySet())
+        final Map<String, HttpContextHolder> contexts = extMgr.getHttpContexts();
+        for (Map.Entry<String, HttpContextHolder> handler : contexts.entrySet())
         {
-            pw.println("  " + handler.getKey() + " ==> " + handler.getValue() + "</td>");
+            pw.println("  " + handler.getKey() + " ==> " + handler.getValue().getContext() + "</td>");
         }
         pw.println();
     }
@@ -183,8 +183,8 @@ public class HttpWhiteboardWebConsolePlu
             if (handler.getValue() instanceof ServletMapping)
             {
                 ServletMapping sm = (ServletMapping) handler.getValue();
-                pw.println("  " + sm.getAlias() + " ==> " + sm.getServlet() + " (" + sm.getInitParams() + ", "
-                    + sm.getContext() + ")");
+                pw.printf("  %s ==> %s (%s, %s, %s)%n", sm.getAlias(), sm.getServlet(),
+                    sm.isRegistered() ? "registered" : "unregistered", sm.getInitParams(), sm.getContext());
             }
         }
         pw.println();
@@ -198,8 +198,9 @@ public class HttpWhiteboardWebConsolePlu
             if (handler.getValue() instanceof FilterMapping)
             {
                 FilterMapping fm = (FilterMapping) handler.getValue();
-                pw.println("  " + fm.getPattern() + " ==> " + fm.getFilter() + " (" + fm.getRanking() + ", "
-                    + fm.getInitParams() + ", " + fm.getContext() + ")");
+                pw.printf("  %s ==> %s (%s, %s, %s, %s)%n", fm.getPattern(), fm.getFilter(),
+                    fm.isRegistered() ? "registered" : "unregistered", fm.getRanking(), fm.getInitParams(),
+                    fm.getContext());
             }
         }
         pw.println();

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java Mon Dec  5 20:29:08 2011
@@ -16,21 +16,21 @@
  */
 package org.apache.felix.http.whiteboard.internal.manager;
 
-import org.osgi.service.http.HttpService;
-import org.osgi.service.http.HttpContext;
-import org.apache.felix.http.base.internal.logger.SystemLogger;
-
 import javax.servlet.Servlet;
 
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpService;
+
 public final class ServletMapping
     extends AbstractMapping
 {
     private final Servlet servlet;
     private final String alias;
 
-    public ServletMapping(HttpContext context, Servlet servlet, String alias)
+    public ServletMapping(Bundle bundle, Servlet servlet, String alias)
     {
-        super(context);
+        super(bundle);
         this.servlet = servlet;
         this.alias = alias;
     }
@@ -47,15 +47,26 @@ public final class ServletMapping
 
     public void register(HttpService httpService)
     {
-        try {
-            httpService.registerServlet(this.alias, this.servlet, getInitParams(), getContext());
-        } catch (Exception e) {
-            SystemLogger.error("Failed to register servlet", e);
+        if (!this.isRegistered() && getContext() != null)
+        {
+            try
+            {
+                httpService.registerServlet(this.alias, this.servlet, getInitParams(), getContext());
+                this.setRegistered(true);
+            }
+            catch (Exception e)
+            {
+                SystemLogger.error("Failed to register servlet", e);
+            }
         }
     }
 
     public void unregister(HttpService httpService)
     {
-        httpService.unregister(this.alias);
+        if (this.isRegistered())
+        {
+            httpService.unregister(this.alias);
+            this.setRegistered(false);
+        }
     }
 }

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java Mon Dec  5 20:29:08 2011
@@ -23,7 +23,7 @@ import org.osgi.framework.ServiceReferen
 public abstract class AbstractTracker<T>
     extends ServiceTracker
 {
-    public AbstractTracker(BundleContext context, Class clz)
+    public AbstractTracker(BundleContext context, Class<T> clz)
     {
         super(context, clz.getName(), null);
     }
@@ -49,12 +49,12 @@ public abstract class AbstractTracker<T>
     public final void removedService(ServiceReference ref, Object service)
     {
         super.removedService(ref, service);
-        removed((T)service);
+        removed((T) service, ref);
     }
 
     protected abstract void modified(T service, ServiceReference ref);
 
     protected abstract void added(T service, ServiceReference ref);
 
-    protected abstract void removed(T service);
+    protected abstract void removed(T service, ServiceReference ref);
 }

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java Mon Dec  5 20:29:08 2011
@@ -39,12 +39,12 @@ public final class FilterTracker
 
     protected void modified(Filter service, ServiceReference ref)
     {
-        removed(service);
+        removed(service, ref);
         added(service, ref);
     }
 
-    protected void removed(Filter service)
+    protected void removed(Filter service, ServiceReference ref)
     {
-        this.manager.remove(service);
+        this.manager.remove(ref);
     }
 }

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java Mon Dec  5 20:29:08 2011
@@ -25,7 +25,7 @@ public final class HttpContextTracker
     extends AbstractTracker<HttpContext>
 {
     private final ExtenderManager manager;
-    
+
     public HttpContextTracker(BundleContext context, ExtenderManager manager)
     {
         super(context, HttpContext.class);
@@ -39,11 +39,11 @@ public final class HttpContextTracker
 
     protected void modified(HttpContext service, ServiceReference ref)
     {
-        removed(service);
+        removed(service, ref);
         added(service, ref);
     }
-    
-    protected void removed(HttpContext service)
+
+    protected void removed(HttpContext service, ServiceReference ref)
     {
         this.manager.remove(service);
     }

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java Mon Dec  5 20:29:08 2011
@@ -42,7 +42,7 @@ public final class HttpServiceTracker
         // Do nothing
     }
 
-    protected void removed(HttpService service)
+    protected void removed(HttpService service, ServiceReference ref)
     {
         this.manager.unsetHttpService();
     }

Modified: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java?rev=1210612&r1=1210611&r2=1210612&view=diff
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java (original)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java Mon Dec  5 20:29:08 2011
@@ -39,12 +39,12 @@ public final class ServletTracker
 
     protected void modified(Servlet service, ServiceReference ref)
     {
-        removed(service);
+        removed(service, ref);
         added(service, ref);
     }
-    
-    protected void removed(Servlet service)
+
+    protected void removed(Servlet service, ServiceReference ref)
     {
-        this.manager.remove(service);
+        this.manager.remove(ref);
     }
 }

Added: felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java
URL: http://svn.apache.org/viewvc/felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java?rev=1210612&view=auto
==============================================================================
--- felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java (added)
+++ felix/trunk/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java Mon Dec  5 20:29:08 2011
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+/**
+ * @see: @TODO ref to whiteboard page on felix.apache.org
+ */
+@Version("1.0")
+@Export(optional = "provide:=true")
+package org.apache.felix.http.whiteboard;
+
+import aQute.bnd.annotation.Export;
+import aQute.bnd.annotation.Version;
+