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
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1210612 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java
new file mode 100644
index 0000000..00521f7
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/HttpWhiteboardConstants.java
@@ -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()
+ {
+ }
+}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
index e6dd0d4..7ab9ecb 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/WhiteboardActivator.java
@@ -23,7 +23,6 @@
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 @@
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());
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java
index e503ff2..0ad4d97 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/AbstractMapping.java
@@ -16,19 +16,35 @@
*/
package org.apache.felix.http.whiteboard.internal.manager;
-import org.osgi.service.http.HttpContext;
-import org.osgi.service.http.HttpService;
import java.util.Hashtable;
-public abstract class AbstractMapping
-{
- private final HttpContext context;
- private final Hashtable<String, String> initParams;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+import org.osgi.service.http.HttpService;
- public AbstractMapping(HttpContext context)
+abstract class AbstractMapping
+{
+ private final Bundle bundle;
+ private HttpContext context;
+ private final Hashtable<String, String> initParams;
+ private boolean registered;
+
+ protected AbstractMapping(final Bundle bundle)
+ {
+ 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;
- this.initParams = new Hashtable<String, String>();
}
public final HttpContext getContext()
@@ -41,6 +57,16 @@
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);
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java
index 0e0378e..7adbbcf 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManager.java
@@ -16,30 +16,290 @@
*/
package org.apache.felix.http.whiteboard.internal.manager;
+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;
-import org.osgi.service.log.LogService;
-import org.osgi.framework.ServiceReference;
-import javax.servlet.Servlet;
-import javax.servlet.Filter;
-public interface ExtenderManager
+public final class ExtenderManager
{
- public void add(HttpContext service, ServiceReference ref);
+ private HttpService httpService;
+ private final HashMap<ServiceReference, AbstractMapping> mapping;
+ private final HttpContextManager contextManager;
- public void remove(HttpContext service);
+ public ExtenderManager()
+ {
+ this.mapping = new HashMap<ServiceReference, AbstractMapping>();
+ this.contextManager = new HttpContextManager();
+ }
- public void add(Filter service, ServiceReference ref);
+ static boolean isEmpty(final String value)
+ {
+ return value == null || value.length() == 0;
+ }
- public void remove(Filter service);
+ private String getStringProperty(ServiceReference ref, String key)
+ {
+ Object value = ref.getProperty(key);
+ return (value instanceof String) ? (String)value : null;
+ }
- public void add(Servlet service, ServiceReference ref);
+ 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;
+ }
- public void remove(Servlet service);
+ private int getIntProperty(ServiceReference ref, String key, int defValue)
+ {
+ Object value = ref.getProperty(key);
+ if (value == null) {
+ return defValue;
+ }
- public void setHttpService(HttpService service);
+ try {
+ return Integer.parseInt(value.toString());
+ } catch (Exception e) {
+ return defValue;
+ }
+ }
- public void unsetHttpService();
+ private void addInitParams(ServiceReference ref, AbstractMapping mapping)
+ {
+ for (String key : ref.getPropertyKeys()) {
+ if (key.startsWith(HttpWhiteboardConstants.INIT_PREFIX)) {
+ String paramKey = key.substring(HttpWhiteboardConstants.INIT_PREFIX.length());
+ String paramValue = getStringProperty(ref, key);
- public void unregisterAll();
+ if (paramValue != null) {
+ mapping.getInitParams().put(paramKey, paramValue);
+ }
+ }
+ }
+ }
+
+ public void add(HttpContext service, ServiceReference ref)
+ {
+ 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)
+ {
+ Collection<AbstractMapping> mappings = this.contextManager.removeHttpContext(service);
+ if (mappings != null)
+ {
+ for (AbstractMapping mapping : mappings)
+ {
+ unregisterMapping(mapping);
+ }
+ }
+ }
+
+ private void getHttpContext(AbstractMapping mapping, ServiceReference ref)
+ {
+ Bundle bundle = ref.getBundle();
+ String contextId = getStringProperty(ref, HttpWhiteboardConstants.CONTEXT_ID);
+ this.contextManager.getHttpContext(bundle, contextId, mapping);
+ }
+
+ 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, HttpWhiteboardConstants.PATTERN);
+
+ if (isEmpty(pattern)) {
+ SystemLogger.debug("Ignoring Filter Service " + ref + ", " + HttpWhiteboardConstants.PATTERN
+ + " is missing or empty");
+ return;
+ }
+
+ FilterMapping mapping = new FilterMapping(ref.getBundle(), service, pattern, ranking);
+ getHttpContext(mapping, ref);
+ addInitParams(ref, mapping);
+ addMapping(ref, mapping);
+ }
+
+ public void add(Servlet service, ServiceReference ref)
+ {
+ 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(ref.getBundle(), service, alias);
+ getHttpContext(mapping, ref);
+ addInitParams(ref, mapping);
+ addMapping(ref, mapping);
+ }
+
+ public void remove(ServiceReference ref)
+ {
+ removeMapping(ref);
+ }
+
+ public synchronized void setHttpService(HttpService service)
+ {
+ this.httpService = service;
+ if (this.httpService instanceof ExtHttpService) {
+ SystemLogger.info("Detected extended HttpService. Filters enabled.");
+ } else {
+ SystemLogger.info("Detected standard HttpService. Filters disabled.");
+ }
+
+ registerAll();
+ }
+
+ public synchronized void unsetHttpService()
+ {
+ unregisterAll();
+ this.httpService = null;
+ }
+
+ public synchronized void unregisterAll()
+ {
+ AbstractMapping[] mappings = null;
+ HttpService service;
+ synchronized (this) {
+ service = this.httpService;
+ if (service != null) {
+ Collection<AbstractMapping> values = this.mapping.values();
+ mappings = values.toArray(new AbstractMapping[values.size()]);
+ }
+ }
+ if (mappings != null) {
+ for (AbstractMapping mapping : mappings) {
+ mapping.unregister(service);
+ }
+ }
+ }
+
+ private synchronized void registerAll()
+ {
+ AbstractMapping[] mappings = null;
+ HttpService service;
+ synchronized (this) {
+ service = this.httpService;
+ if (service != null) {
+ Collection<AbstractMapping> values = this.mapping.values();
+ mappings = values.toArray(new AbstractMapping[values.size()]);
+ }
+ }
+ if (mappings != null) {
+ for (AbstractMapping mapping : mappings) {
+ mapping.register(service);
+ }
+ }
+ }
+
+ private synchronized void addMapping(ServiceReference ref, AbstractMapping mapping)
+ {
+ this.mapping.put(ref, mapping);
+ this.registerMapping(mapping);
+ }
+
+ private synchronized void removeMapping(ServiceReference ref)
+ {
+ 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 void unregisterMapping(AbstractMapping mapping)
+ {
+ HttpService httpService = this.httpService;
+ if (httpService != null)
+ {
+ mapping.unregister(httpService);
+ }
+ }
+
+ /**
+ * 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)
+ {
+ return new HashMap<Object, AbstractMapping>(this.mapping);
+ }
+ }
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
deleted file mode 100644
index 909ab85..0000000
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerImpl.java
+++ /dev/null
@@ -1,229 +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.whiteboard.internal.manager;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-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.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
-{
- 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 HttpContextManager contextManager;
-
- public ExtenderManagerImpl()
- {
- this.mapping = new HashMap<Object, AbstractMapping>();
- this.contextManager = new HttpContextManager();
- }
-
- private String getStringProperty(ServiceReference ref, String key)
- {
- Object value = ref.getProperty(key);
- return (value instanceof String) ? (String)value : null;
- }
-
- private int getIntProperty(ServiceReference ref, String key, int defValue)
- {
- Object value = ref.getProperty(key);
- if (value == null) {
- return defValue;
- }
-
- try {
- return Integer.parseInt(value.toString());
- } catch (Exception e) {
- return defValue;
- }
- }
-
- 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());
- String paramValue = getStringProperty(ref, key);
-
- if (paramValue != null) {
- mapping.getInitParams().put(paramKey, paramValue);
- }
- }
- }
- }
-
- 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);
- }
- }
-
- public void remove(HttpContext service)
- {
- this.contextManager.removeHttpContext(service);
- }
-
- private HttpContext getHttpContext(ServiceReference ref)
- {
- Bundle bundle = ref.getBundle();
- String contextId = getStringProperty(ref, CONTEXT_ID_KEY);
-
- if (contextId != null) {
- return this.contextManager.getHttpContext(bundle, contextId);
- } else {
- return new DefaultHttpContext(bundle);
- }
- }
-
- public void add(Filter service, ServiceReference ref)
- {
- int ranking = getIntProperty(ref, Constants.SERVICE_RANKING, 0);
- String pattern = getStringProperty(ref, PATTERN_KEY);
-
- if (pattern == null) {
- return;
- }
-
- FilterMapping mapping = new FilterMapping(getHttpContext(ref), service, pattern, ranking);
- addInitParams(ref, mapping);
- addMapping(service, mapping);
- }
-
- public void remove(Filter service)
- {
- removeMapping(service);
- }
-
- public void add(Servlet service, ServiceReference ref)
- {
- String alias = getStringProperty(ref, ALIAS_KEY);
- if (alias == null) {
- return;
- }
-
- ServletMapping mapping = new ServletMapping(getHttpContext(ref), service, alias);
- addInitParams(ref, mapping);
- addMapping(service, mapping);
- }
-
- public void remove(Servlet service)
- {
- removeMapping(service);
- }
-
- public synchronized void setHttpService(HttpService service)
- {
- this.httpService = service;
- if (this.httpService instanceof ExtHttpService) {
- SystemLogger.info("Detected extended HttpService. Filters enabled.");
- } else {
- SystemLogger.info("Detected standard HttpService. Filters disabled.");
- }
-
- registerAll();
- }
-
- public synchronized void unsetHttpService()
- {
- unregisterAll();
- this.httpService = null;
- }
-
- public synchronized void unregisterAll()
- {
- AbstractMapping[] mappings = null;
- HttpService service;
- synchronized (this) {
- service = this.httpService;
- if (service != null) {
- Collection<AbstractMapping> values = this.mapping.values();
- mappings = values.toArray(new AbstractMapping[values.size()]);
- }
- }
- if (mappings != null) {
- for (AbstractMapping mapping : mappings) {
- mapping.unregister(service);
- }
- }
- }
-
- private synchronized void registerAll()
- {
- AbstractMapping[] mappings = null;
- HttpService service;
- synchronized (this) {
- service = this.httpService;
- if (service != null) {
- Collection<AbstractMapping> values = this.mapping.values();
- mappings = values.toArray(new AbstractMapping[values.size()]);
- }
- }
- if (mappings != null) {
- for (AbstractMapping mapping : mappings) {
- mapping.register(service);
- }
- }
- }
-
- private synchronized void addMapping(Object key, AbstractMapping mapping)
- {
- this.mapping.put(key, mapping);
- if (this.httpService != null) {
- mapping.register(this.httpService);
- }
- }
-
- private synchronized void removeMapping(Object key)
- {
- AbstractMapping mapping = this.mapping.remove(key);
- if ((mapping != null) && (this.httpService != null)) {
- mapping.unregister(this.httpService);
- }
- }
-
- Map<String, HttpContext> getHttpContexts() {
- return this.contextManager.getHttpContexts();
- }
-
- Map<Object, AbstractMapping> getMappings()
- {
- synchronized (this)
- {
- return new HashMap<Object, AbstractMapping>(this.mapping);
- }
- }
-}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
index 275aa4f..961dcc5 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/FilterMapping.java
@@ -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 @@
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 @@
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);
}
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
index 58d4f74..34b5fb2 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManager.java
@@ -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 @@
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;
- }
-
- public synchronized HttpContext getHttpContext(Bundle bundle, String contextId)
- {
- String id = createId(bundle, contextId);
- HttpContext context = 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 + "]");
+ if (bundle != null)
+ {
+ return bundle.getBundleId() + "-" + ((contextId == null) ? "" : contextId);
}
- return context;
+ return createId(contextId);
}
- public synchronized void removeHttpContext(HttpContext context)
+ 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);
+ HttpContextHolder holder = this.idMap.get(id);
+
+ // shared context
+ if (holder == null)
+ {
+ id = createId(contextId);
+ holder = this.idMap.get(id);
+ }
+
+ // 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 ungetHttpContext(Bundle bundle, String contextId, AbstractMapping mapping)
+ {
+ // 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 Collection<AbstractMapping> addHttpContext(Bundle bundle, String contextId, HttpContext context)
+ {
+ String id = createId(bundle, contextId);
+ 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) {
- this.idMap.remove(id);
+ 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;
}
- public synchronized void addHttpContext(Bundle bundle, String contextId, HttpContext context)
+ synchronized Map<String, HttpContextHolder> getHttpContexts()
{
- String id = createId(bundle, contextId);
- this.idMap.put(id, context);
- this.contextMap.put(context, id);
+ return new HashMap<String, HttpContextHolder>(this.idMap);
}
- public synchronized Map<String, HttpContext> getHttpContexts()
+ synchronized Map<String, Set<AbstractMapping>> getOrphanMappings()
{
- return new HashMap<String, HttpContext>(this.idMap);
+ return new HashMap<String, Set<AbstractMapping>>(this.orphanMappings);
+ }
+
+ static class HttpContextHolder
+ {
+ 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;
+ }
}
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java
index 408f7e3..b261177 100755
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/HttpWhiteboardWebConsolePlugin.java
@@ -27,13 +27,13 @@
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 @@
return "Http Whiteboard";
}
- public HttpWhiteboardWebConsolePlugin(final ExtenderManagerImpl extMgr)
+ public HttpWhiteboardWebConsolePlugin(final ExtenderManager extMgr)
{
this.extMgr = extMgr;
}
@@ -92,12 +92,12 @@
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 @@
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 @@
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 @@
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();
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
index 362e582..67b8d8c 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/manager/ServletMapping.java
@@ -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 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);
+ }
}
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java
index c2590bf..e8a1f5c 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/AbstractTracker.java
@@ -23,7 +23,7 @@
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 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);
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java
index a2640dd..ba4fa58 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/FilterTracker.java
@@ -39,12 +39,12 @@
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);
}
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java
index 60af219..9238d36 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpContextTracker.java
@@ -25,7 +25,7 @@
extends AbstractTracker<HttpContext>
{
private final ExtenderManager manager;
-
+
public HttpContextTracker(BundleContext context, ExtenderManager manager)
{
super(context, HttpContext.class);
@@ -39,11 +39,11 @@
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);
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java
index af61564..4188a7a 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/HttpServiceTracker.java
@@ -42,7 +42,7 @@
// Do nothing
}
- protected void removed(HttpService service)
+ protected void removed(HttpService service, ServiceReference ref)
{
this.manager.unsetHttpService();
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java
index 2dcae99..7853c0d 100644
--- a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/internal/tracker/ServletTracker.java
@@ -39,12 +39,12 @@
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);
}
}
diff --git a/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java
new file mode 100644
index 0000000..c213e31
--- /dev/null
+++ b/http/whiteboard/src/main/java/org/apache/felix/http/whiteboard/package-info.java
@@ -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;
+
diff --git a/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerTest.java b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerTest.java
new file mode 100644
index 0000000..a8ad61b
--- /dev/null
+++ b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ExtenderManagerTest.java
@@ -0,0 +1,876 @@
+/*
+ * 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.internal.manager;
+
+import static org.mockito.Mockito.when;
+
+import java.util.Dictionary;
+
+import javax.servlet.Filter;
+import javax.servlet.Servlet;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.collections.BidiMap;
+import org.apache.commons.collections.bidimap.DualHashBidiMap;
+import org.apache.felix.http.api.ExtHttpService;
+import org.apache.felix.http.whiteboard.HttpWhiteboardConstants;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.HttpContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ExtenderManagerTest
+{
+
+ private static final String SAMPLE_CONTEXT_ID = "some.context.id";
+
+ private static final long BUNDLE_1_ID = 1L;
+
+ private static final long BUNDLE_2_ID = 2L;
+
+ private static final String SERVLET_1_ALIAS = "/servet1";
+
+ private static final String SERVLET_1_1_ALIAS = "/servet1_1";
+
+ private static final String SERVLET_2_ALIAS = "/servet2";
+
+ private MockExtHttpService httpService;
+
+ @Mock
+ private HttpContext sampleContext;
+
+ @Mock
+ private Bundle bundle1;
+
+ @Mock
+ private Bundle bundle2;
+
+ @Mock
+ private ExtServlet servlet1;
+
+ @Mock
+ private ExtServlet servlet1_1;
+
+ @Mock
+ private ExtServlet servlet2;
+
+ @Mock
+ private ExtFilter filter1;
+
+ @Mock
+ private ExtFilter filter1_1;
+
+
+ @Mock
+ private ExtFilter filter2;
+
+ @Mock
+ private ServiceReference servlet1Reference;
+
+ @Mock
+ private ServiceReference servlet1_1Reference;
+
+ @Mock
+ private ServiceReference servlet2Reference;
+
+ @Mock
+ private ServiceReference filter1Reference;
+
+ @Mock
+ private ServiceReference filter1_1Reference;
+
+ @Mock
+ private ServiceReference filter2Reference;
+
+ @Mock
+ private ServiceReference httpContextReference;
+
+ @Before
+ public void setup()
+ {
+ when(bundle1.getBundleId()).thenReturn(BUNDLE_1_ID);
+ when(bundle2.getBundleId()).thenReturn(BUNDLE_2_ID);
+ when(httpContextReference.getBundle()).thenReturn(bundle1);
+
+ when(servlet1Reference.getBundle()).thenReturn(bundle1);
+ when(servlet1Reference.getPropertyKeys()).thenReturn(new String[0]);
+ when(servlet1Reference.getProperty(HttpWhiteboardConstants.ALIAS)).thenReturn(SERVLET_1_ALIAS);
+
+ when(servlet1_1Reference.getBundle()).thenReturn(bundle1);
+ when(servlet1_1Reference.getPropertyKeys()).thenReturn(new String[0]);
+ when(servlet1_1Reference.getProperty(HttpWhiteboardConstants.ALIAS)).thenReturn(SERVLET_1_1_ALIAS);
+
+ when(servlet2Reference.getBundle()).thenReturn(bundle2);
+ when(servlet2Reference.getPropertyKeys()).thenReturn(new String[0]);
+ when(servlet2Reference.getProperty(HttpWhiteboardConstants.ALIAS)).thenReturn(SERVLET_2_ALIAS);
+ when(servlet2Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+
+ when(filter1Reference.getBundle()).thenReturn(bundle1);
+ when(filter1Reference.getPropertyKeys()).thenReturn(new String[0]);
+ when(filter1Reference.getProperty(HttpWhiteboardConstants.PATTERN)).thenReturn(SERVLET_1_ALIAS);
+
+ when(filter1_1Reference.getBundle()).thenReturn(bundle1);
+ when(filter1_1Reference.getPropertyKeys()).thenReturn(new String[0]);
+ when(filter1_1Reference.getProperty(HttpWhiteboardConstants.PATTERN)).thenReturn(SERVLET_1_1_ALIAS);
+
+ when(filter2Reference.getBundle()).thenReturn(bundle2);
+ when(filter2Reference.getPropertyKeys()).thenReturn(new String[0]);
+ when(filter2Reference.getProperty(HttpWhiteboardConstants.PATTERN)).thenReturn(SERVLET_2_ALIAS);
+ when(filter2Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+
+ this.httpService = new MockExtHttpService();
+ }
+
+ @After
+ public void tearDown()
+ {
+ this.httpService = null;
+ }
+
+ @Test
+ public void test_no_servlets_no_filters()
+ {
+ ExtenderManager em = new ExtenderManager();
+
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ em.setHttpService(null);
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ }
+
+ @Test
+ public void test_servlet_per_bundle()
+ {
+ ExtenderManager em = new ExtenderManager();
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // setup a context without context ID
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ em.remove(sampleContext);
+
+ // set up a context with context ID and not shared
+ final String id = HttpContextManagerTest.createId(bundle1, SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // register servlet1 from bundle1
+ when(servlet1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(servlet1, servlet1Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1, this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, servlet1.getHttpContext());
+
+ // register servlet2 from bundle2
+ em.add(servlet2, servlet2Reference);
+
+ TestCase.assertEquals(2, em.getMappings().size());
+ TestCase.assertSame(servlet2, ((ServletMapping) em.getMappings().get(servlet2Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertNull(this.httpService.getServlets().get(SERVLET_2_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(servlet2Reference)));
+
+ // unregister servlet2
+ em.remove(servlet2Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1, this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister servlet1
+ em.remove(servlet1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().get(id).getMappings().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_servlet_shared()
+ {
+ ExtenderManager em = new ExtenderManager();
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // set up a context with context ID and shared
+ final String id = HttpContextManagerTest.createId(SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_SHARED)).thenReturn("true");
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // register servlet1 from bundle1
+ when(servlet1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(servlet1, servlet1Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1, this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, servlet1.getHttpContext());
+
+ // register servlet2 from bundle2
+ em.add(servlet2, servlet2Reference);
+
+ TestCase.assertEquals(2, em.getMappings().size());
+ TestCase.assertSame(servlet2, ((ServletMapping) em.getMappings().get(servlet2Reference)).getServlet());
+ TestCase.assertEquals(2, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(2, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet2, this.httpService.getServlets().get(SERVLET_2_ALIAS));
+ TestCase.assertEquals(0, em.getOrphanMappings().size());
+ TestCase.assertSame(sampleContext, servlet2.getHttpContext());
+
+ // unregister servlet2
+ em.remove(servlet2Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1, this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister servlet1
+ em.remove(servlet1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().get(id).getMappings().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_servlet_no_context_id()
+ {
+ ExtenderManager em = new ExtenderManager();
+ final String id1 = HttpContextManagerTest.createId(bundle1, null);
+ final String id2 = HttpContextManagerTest.createId(bundle2, null);
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+
+ // register servlet1 from bundle1
+ em.add(servlet1, servlet1Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id1).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1, this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(DefaultHttpContext.class, servlet1.getHttpContext().getClass());
+
+ // register servlet2 from bundle2
+ when(servlet2Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn("");
+ em.add(servlet2, servlet2Reference);
+
+ TestCase.assertEquals(2, em.getMappings().size());
+ TestCase.assertSame(servlet2, ((ServletMapping) em.getMappings().get(servlet2Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id2).getMappings().size());
+ TestCase.assertEquals(2, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet2, this.httpService.getServlets().get(SERVLET_2_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(DefaultHttpContext.class, servlet2.getHttpContext().getClass());
+
+ // different HttpContext instances per servlet/per bundle
+ TestCase.assertNotSame(servlet1.getHttpContext(), servlet2.getHttpContext());
+
+ // register servlet 1_1 from bundle 1
+ em.add(servlet1_1, servlet1_1Reference);
+
+ TestCase.assertEquals(3, em.getMappings().size());
+ TestCase.assertSame(servlet1_1, ((ServletMapping) em.getMappings().get(servlet1_1Reference)).getServlet());
+ TestCase.assertEquals(2, em.getHttpContexts().get(id1).getMappings().size());
+ TestCase.assertEquals(3, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1_1, this.httpService.getServlets().get(SERVLET_1_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(DefaultHttpContext.class, servlet1_1.getHttpContext().getClass());
+
+ // same HttpContext instances per servlet in same bundle
+ TestCase.assertSame(servlet1.getHttpContext(), servlet1_1.getHttpContext());
+ }
+
+ @Test
+ public void test_servlet_before_context_per_bundle()
+ {
+ ExtenderManager em = new ExtenderManager();
+ final String id = HttpContextManagerTest.createId(bundle1, SAMPLE_CONTEXT_ID);
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // register servlet1 from bundle1
+ when(servlet1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(servlet1, servlet1Reference);
+
+ // servlet not registered with HttpService yet
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertNull(this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(servlet1Reference)));
+
+ // set up a context with context ID and not shared
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // servlet registered with HttpService
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1, this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, servlet1.getHttpContext());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertNull(this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(servlet1Reference)));
+
+ // unregister servlet1
+ em.remove(servlet1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_servlet_before_context_shared()
+ {
+ ExtenderManager em = new ExtenderManager();
+ final String id = HttpContextManagerTest.createId(SAMPLE_CONTEXT_ID);
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // register servlet1 from bundle1
+ when(servlet1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(servlet1, servlet1Reference);
+
+ // servlet not registered with HttpService yet
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertNull(this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(servlet1Reference)));
+
+ // set up a context with context ID and not shared
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_SHARED)).thenReturn(true);
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // servlet registered with HttpService
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet1, this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, servlet1.getHttpContext());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(servlet1, ((ServletMapping) em.getMappings().get(servlet1Reference)).getServlet());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertNull(this.httpService.getServlets().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(servlet1Reference)));
+
+ // unregister servlet1
+ em.remove(servlet1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_filter_per_bundle()
+ {
+ ExtenderManager em = new ExtenderManager();
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // setup a context without context ID
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ em.remove(sampleContext);
+
+ // set up a context with context ID and not shared
+ final String id = HttpContextManagerTest.createId(bundle1, SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // register filter1 from bundle1
+ when(filter1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(filter1, filter1Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1, this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, filter1.getHttpContext());
+
+ // register filter2 from bundle2
+ em.add(filter2, filter2Reference);
+
+ TestCase.assertEquals(2, em.getMappings().size());
+ TestCase.assertSame(filter2, ((FilterMapping) em.getMappings().get(filter2Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertNull(this.httpService.getFilters().get(SERVLET_2_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(filter2Reference)));
+
+ // unregister filter2
+ em.remove(filter2Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1, this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister filter1
+ em.remove(filter1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().get(id).getMappings().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_filter_shared()
+ {
+ ExtenderManager em = new ExtenderManager();
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // set up a context with context ID and shared
+ final String id = HttpContextManagerTest.createId(SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_SHARED)).thenReturn("true");
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // register filter1 from bundle1
+ when(filter1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(filter1, filter1Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1, this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, filter1.getHttpContext());
+
+ // register filter2 from bundle2
+ em.add(filter2, filter2Reference);
+
+ TestCase.assertEquals(2, em.getMappings().size());
+ TestCase.assertSame(filter2, ((FilterMapping) em.getMappings().get(filter2Reference)).getFilter());
+ TestCase.assertEquals(2, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(2, this.httpService.getFilters().size());
+ TestCase.assertSame(filter2, this.httpService.getFilters().get(SERVLET_2_ALIAS));
+ TestCase.assertEquals(0, em.getOrphanMappings().size());
+ TestCase.assertSame(sampleContext, filter2.getHttpContext());
+
+ // unregister filter2
+ em.remove(filter2Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1, this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister filter1
+ em.remove(filter1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().get(id).getMappings().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_filter_no_context_id()
+ {
+ ExtenderManager em = new ExtenderManager();
+ final String id1 = HttpContextManagerTest.createId(bundle1, null);
+ final String id2 = HttpContextManagerTest.createId(bundle2, null);
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+
+ // register filter1 from bundle1
+ em.add(filter1, filter1Reference);
+
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id1).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1, this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(DefaultHttpContext.class, filter1.getHttpContext().getClass());
+
+ // register filter2 from bundle2
+ when(filter2Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn("");
+ em.add(filter2, filter2Reference);
+
+ TestCase.assertEquals(2, em.getMappings().size());
+ TestCase.assertSame(filter2, ((FilterMapping) em.getMappings().get(filter2Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id2).getMappings().size());
+ TestCase.assertEquals(2, this.httpService.getFilters().size());
+ TestCase.assertSame(filter2, this.httpService.getFilters().get(SERVLET_2_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(DefaultHttpContext.class, filter2.getHttpContext().getClass());
+
+ // different HttpContext instances per servlet/per bundle
+ TestCase.assertNotSame(filter1.getHttpContext(), filter2.getHttpContext());
+
+ // register servlet 1_1 from bundle 1
+ em.add(filter1_1, filter1_1Reference);
+
+ TestCase.assertEquals(3, em.getMappings().size());
+ TestCase.assertSame(filter1_1, ((FilterMapping) em.getMappings().get(filter1_1Reference)).getFilter());
+ TestCase.assertEquals(2, em.getHttpContexts().get(id1).getMappings().size());
+ TestCase.assertEquals(3, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1_1, this.httpService.getFilters().get(SERVLET_1_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(DefaultHttpContext.class, filter1_1.getHttpContext().getClass());
+
+ // same HttpContext instances per servlet in same bundle
+ TestCase.assertSame(filter1.getHttpContext(), filter1_1.getHttpContext());
+ }
+
+ @Test
+ public void test_filter_before_context_per_bundle()
+ {
+ ExtenderManager em = new ExtenderManager();
+ final String id = HttpContextManagerTest.createId(bundle1, SAMPLE_CONTEXT_ID);
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // register filter1 from bundle1
+ when(filter1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(filter1, filter1Reference);
+
+ // servlet not registered with HttpService yet
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertNull(this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(filter1Reference)));
+
+ // set up a context with context ID and not shared
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // servlet registered with HttpService
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1, this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, filter1.getHttpContext());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertNull(this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(filter1Reference)));
+
+ // unregister filter1
+ em.remove(filter1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_filter_before_context_shared()
+ {
+ ExtenderManager em = new ExtenderManager();
+ final String id = HttpContextManagerTest.createId(SAMPLE_CONTEXT_ID);
+
+ // prepare with http service
+ em.setHttpService(this.httpService);
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ // register filter1 from bundle1
+ when(filter1Reference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ em.add(filter1, filter1Reference);
+
+ // servlet not registered with HttpService yet
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertNull(this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(filter1Reference)));
+
+ // set up a context with context ID and not shared
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_ID)).thenReturn(SAMPLE_CONTEXT_ID);
+ when(httpContextReference.getProperty(HttpWhiteboardConstants.CONTEXT_SHARED)).thenReturn(true);
+ em.add(sampleContext, httpContextReference);
+ TestCase.assertEquals(1, em.getHttpContexts().size());
+
+ // servlet registered with HttpService
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(1, em.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter1, this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ TestCase.assertSame(sampleContext, filter1.getHttpContext());
+
+ // unregister context
+ em.remove(sampleContext);
+ TestCase.assertEquals(1, em.getMappings().size());
+ TestCase.assertSame(filter1, ((FilterMapping) em.getMappings().get(filter1Reference)).getFilter());
+ TestCase.assertEquals(0, em.getHttpContexts().size());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertNull(this.httpService.getFilters().get(SERVLET_1_ALIAS));
+ TestCase.assertEquals(1, em.getOrphanMappings().size());
+ TestCase.assertEquals(1, em.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(em.getOrphanMappings().get(SAMPLE_CONTEXT_ID)
+ .contains(em.getMappings().get(filter1Reference)));
+
+ // unregister filter1
+ em.remove(filter1Reference);
+ TestCase.assertTrue(em.getMappings().isEmpty());
+ TestCase.assertTrue(em.getHttpContexts().isEmpty());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ TestCase.assertTrue(em.getOrphanMappings().isEmpty());
+ }
+
+ static interface ExtFilter extends Filter
+ {
+ HttpContext getHttpContext();
+ }
+
+ static interface ExtServlet extends Servlet
+ {
+ HttpContext getHttpContext();
+ }
+
+ static final class MockExtHttpService implements ExtHttpService
+ {
+
+ private final BidiMap /* <String, Servlet> */servlets = new DualHashBidiMap();
+ private final BidiMap /* <String, Filter> */filters = new DualHashBidiMap();
+
+ /**
+ * @return BidiMap<String, Servlet>
+ */
+ public BidiMap getServlets()
+ {
+ return servlets;
+ }
+
+ /**
+ * @return BidiMap<String, Filter>
+ */
+ public BidiMap getFilters()
+ {
+ return filters;
+ }
+
+ public void registerServlet(String alias, Servlet servlet, @SuppressWarnings("rawtypes") Dictionary initparams,
+ HttpContext context)
+
+ {
+ // always expect a non-null HttpContext here !!
+ TestCase.assertNotNull(context);
+
+ this.servlets.put(alias, servlet);
+
+ // make HttpContext available
+ when(((ExtServlet) servlet).getHttpContext()).thenReturn(context);
+ }
+
+ public void registerResources(String alias, String name, HttpContext context)
+ {
+ // not used here
+ }
+
+ public void unregister(String alias)
+ {
+ Object servlet = this.servlets.remove(alias);
+ if (servlet instanceof ExtServlet)
+ {
+ when(((ExtServlet) servlet).getHttpContext()).thenReturn(null);
+ }
+ }
+
+ public HttpContext createDefaultHttpContext()
+ {
+ // not used here
+ return null;
+ }
+
+ public void registerFilter(Filter filter, String pattern, @SuppressWarnings("rawtypes") Dictionary initParams,
+ int ranking, HttpContext context)
+ {
+ // always expect a non-null HttpContext here !!
+ TestCase.assertNotNull(context);
+
+ this.filters.put(pattern, filter);
+
+ // make HttpContext available
+ when(((ExtFilter) filter).getHttpContext()).thenReturn(context);
+ }
+
+ public void unregisterFilter(Filter filter)
+ {
+ this.filters.removeValue(filter);
+ when(((ExtFilter) filter).getHttpContext()).thenReturn(null);
+ }
+
+ public void unregisterServlet(Servlet servlet)
+ {
+ this.servlets.removeValue(servlet);
+ when(((ExtServlet) servlet).getHttpContext()).thenReturn(null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/FilterMappingTest.java b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/FilterMappingTest.java
new file mode 100644
index 0000000..3fda558
--- /dev/null
+++ b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/FilterMappingTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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.internal.manager;
+
+import static org.mockito.Mockito.when;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FilterMappingTest
+{
+
+ private static final String SAMPLE_CONTEXT_ID = "some.context.id";
+
+ private static final long BUNDLE_ID = 1L;
+
+ private static final String FILTER_PATTERN = "/sample/*";
+
+ private static final int FILTER_RANKING = 1234;
+
+ @Mock
+ private HttpContext sampleContext;
+
+ @Mock
+ private Bundle bundle;
+
+ @Mock
+ private ExtenderManagerTest.ExtFilter filter;
+
+ private ExtenderManagerTest.MockExtHttpService httpService;
+
+ @Before
+ public void setup()
+ {
+ when(bundle.getBundleId()).thenReturn(BUNDLE_ID);
+
+ this.httpService = new ExtenderManagerTest.MockExtHttpService();
+ }
+
+ @After
+ public void tearDown()
+ {
+ this.httpService = null;
+ }
+
+ @Test
+ public void test_with_context()
+ {
+ FilterMapping fm = new FilterMapping(bundle, filter, FILTER_PATTERN, FILTER_RANKING);
+ TestCase.assertSame(bundle, fm.getBundle());
+ TestCase.assertSame(filter, fm.getFilter());
+ TestCase.assertEquals(FILTER_PATTERN, fm.getPattern());
+ TestCase.assertEquals(FILTER_RANKING, fm.getRanking());
+
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertNotNull(fm.getInitParams());
+ TestCase.assertTrue(fm.getInitParams().isEmpty());
+ TestCase.assertFalse(fm.isRegistered());
+
+ fm.setContext(sampleContext);
+ TestCase.assertSame(sampleContext, fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ fm.register(this.httpService);
+ TestCase.assertSame(sampleContext, fm.getContext());
+ TestCase.assertTrue(fm.isRegistered());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter, this.httpService.getFilters().get(FILTER_PATTERN));
+ TestCase.assertSame(sampleContext, filter.getHttpContext());
+
+ fm.unregister(this.httpService);
+ TestCase.assertSame(sampleContext, fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+
+ fm.setContext(null);
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ }
+
+ @Test
+ public void test_context_delayed()
+ {
+ FilterMapping fm = new FilterMapping(bundle, filter, FILTER_PATTERN, FILTER_RANKING);
+ TestCase.assertSame(bundle, fm.getBundle());
+ TestCase.assertSame(filter, fm.getFilter());
+ TestCase.assertEquals(FILTER_PATTERN, fm.getPattern());
+ TestCase.assertEquals(FILTER_RANKING, fm.getRanking());
+
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertNotNull(fm.getInitParams());
+ TestCase.assertTrue(fm.getInitParams().isEmpty());
+ TestCase.assertFalse(fm.isRegistered());
+
+ fm.register(this.httpService);
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ fm.unregister(httpService);
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ fm.setContext(sampleContext);
+ TestCase.assertSame(sampleContext, fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+ }
+
+ @Test
+ public void test_unset_context()
+ {
+ FilterMapping fm = new FilterMapping(bundle, filter, FILTER_PATTERN, FILTER_RANKING);
+ TestCase.assertSame(bundle, fm.getBundle());
+ TestCase.assertSame(filter, fm.getFilter());
+ TestCase.assertEquals(FILTER_PATTERN, fm.getPattern());
+ TestCase.assertEquals(FILTER_RANKING, fm.getRanking());
+
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertNotNull(fm.getInitParams());
+ TestCase.assertTrue(fm.getInitParams().isEmpty());
+ TestCase.assertFalse(fm.isRegistered());
+
+ fm.setContext(sampleContext);
+ TestCase.assertSame(sampleContext, fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertTrue(this.httpService.getFilters().isEmpty());
+
+ fm.register(this.httpService);
+ TestCase.assertSame(sampleContext, fm.getContext());
+ TestCase.assertTrue(fm.isRegistered());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter, this.httpService.getFilters().get(FILTER_PATTERN));
+ TestCase.assertSame(sampleContext, filter.getHttpContext());
+
+ // does not unregister yet
+ fm.setContext(null);
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertTrue(fm.isRegistered());
+ TestCase.assertEquals(1, this.httpService.getFilters().size());
+ TestCase.assertSame(filter, this.httpService.getFilters().get(FILTER_PATTERN));
+ TestCase.assertSame(sampleContext, filter.getHttpContext());
+
+ fm.unregister(this.httpService);
+ TestCase.assertNull(fm.getContext());
+ TestCase.assertFalse(fm.isRegistered());
+ TestCase.assertEquals(0, this.httpService.getFilters().size());
+ }
+}
diff --git a/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManagerTest.java b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManagerTest.java
new file mode 100644
index 0000000..5ce0a49
--- /dev/null
+++ b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/HttpContextManagerTest.java
@@ -0,0 +1,330 @@
+/*
+ * 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.internal.manager;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.http.whiteboard.internal.manager.HttpContextManager.HttpContextHolder;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+
+import static org.mockito.Mockito.*;
+
+@RunWith(MockitoJUnitRunner.class)
+public class HttpContextManagerTest
+{
+
+ private static final String SAMPLE_CONTEXT_ID = "some.context.id";
+
+ private static final long BUNDLE_1_ID = 1L;
+
+ private static final String BUNDLE_1_ALIAS = "/bundle1";
+
+ private static final long BUNDLE_2_ID = 2L;
+
+ private static final String BUNDLE_2_ALIAS = "/bundle2";
+
+ @Mock
+ private HttpContext sampleContext;
+
+ @Mock
+ private Bundle bundle1;
+
+ @Mock
+ private Bundle bundle2;
+
+ @Before
+ public void setup()
+ {
+ when(bundle1.getBundleId()).thenReturn(BUNDLE_1_ID);
+ when(bundle2.getBundleId()).thenReturn(BUNDLE_2_ID);
+ }
+
+ @Test
+ public void test_HttpContextHolder()
+ {
+ TestCase.assertNotNull(sampleContext);
+
+ final HttpContextHolder h1 = new HttpContextHolder(sampleContext);
+ TestCase.assertSame(sampleContext, h1.getContext());
+ TestCase.assertTrue(h1.getMappings().isEmpty());
+
+ ServletMapping sm = new ServletMapping(bundle1, null, "");
+ h1.addMapping(sm);
+ TestCase.assertSame(sampleContext, sm.getContext());
+ TestCase.assertEquals(1, h1.getMappings().size());
+ TestCase.assertTrue(h1.getMappings().contains(sm));
+
+ h1.removeMapping(sm);
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertTrue(h1.getMappings().isEmpty());
+ }
+
+ @Test
+ public void test_add_remove_HttpContext_per_Bundle()
+ {
+ final HttpContextManager hcm = new HttpContextManager();
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+
+ Collection<AbstractMapping> mappings = hcm.addHttpContext(bundle1, SAMPLE_CONTEXT_ID, sampleContext);
+ TestCase.assertNotNull(mappings);
+ TestCase.assertTrue(mappings.isEmpty());
+
+ String holderId = createId(bundle1, SAMPLE_CONTEXT_ID);
+ Map<String, HttpContextHolder> holders = hcm.getHttpContexts();
+ TestCase.assertEquals(1, holders.size());
+ TestCase.assertSame(sampleContext, holders.get(holderId).getContext());
+ TestCase.assertEquals(mappings, holders.get(holderId).getMappings());
+
+ Collection<AbstractMapping> removedMappings = hcm.removeHttpContext(sampleContext);
+ TestCase.assertNotNull(removedMappings);
+ TestCase.assertTrue(removedMappings.isEmpty());
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+ }
+
+ @Test
+ public void test_add_remove_HttpContext_shared()
+ {
+ final HttpContextManager hcm = new HttpContextManager();
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+
+ Collection<AbstractMapping> mappings = hcm.addHttpContext(null, SAMPLE_CONTEXT_ID, sampleContext);
+ TestCase.assertNotNull(mappings);
+ TestCase.assertTrue(mappings.isEmpty());
+
+ String holderId = createId(SAMPLE_CONTEXT_ID);
+ Map<String, HttpContextHolder> holders = hcm.getHttpContexts();
+ TestCase.assertEquals(1, holders.size());
+ TestCase.assertSame(sampleContext, holders.get(holderId).getContext());
+ TestCase.assertEquals(mappings, holders.get(holderId).getMappings());
+
+ Collection<AbstractMapping> removedMappings = hcm.removeHttpContext(sampleContext);
+ TestCase.assertNotNull(removedMappings);
+ TestCase.assertTrue(removedMappings.isEmpty());
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+ }
+
+ @Test
+ public void test_get_unget_HttpContext_per_bundle_same_bundle()
+ {
+ final HttpContextManager hcm = new HttpContextManager();
+ final String id = createId(bundle1, SAMPLE_CONTEXT_ID);
+ hcm.addHttpContext(bundle1, SAMPLE_CONTEXT_ID, sampleContext);
+
+ // Servlet 1 gets the context
+ final ServletMapping bundle1Servlet = new ServletMapping(bundle1, null, BUNDLE_1_ALIAS);
+ HttpContext ctx1 = hcm.getHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ TestCase.assertNotNull(ctx1);
+ TestCase.assertSame(ctx1, bundle1Servlet.getContext());
+ TestCase.assertSame(sampleContext, ctx1);
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().contains(bundle1Servlet));
+ Map<String, Set<AbstractMapping>> orphans1 = hcm.getOrphanMappings();
+ TestCase.assertTrue(orphans1.isEmpty());
+
+ // unregister servlet again --> all references clear
+ hcm.ungetHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ TestCase.assertNull(bundle1Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().isEmpty());
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+
+ // register servlet, unregister context --> orphan
+ hcm.getHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ hcm.removeHttpContext(sampleContext);
+ TestCase.assertNull(bundle1Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+ TestCase.assertEquals(1, hcm.getOrphanMappings().size());
+ TestCase.assertEquals(1, hcm.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(hcm.getOrphanMappings().get(SAMPLE_CONTEXT_ID).contains(bundle1Servlet));
+
+ // cleanup
+ hcm.ungetHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ TestCase.assertNull(bundle1Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+ }
+
+ public void test_get_unget_HttpContext_per_bundle_other_bundle()
+ {
+ final HttpContextManager hcm = new HttpContextManager();
+
+ final String id1 = createId(bundle1, SAMPLE_CONTEXT_ID);
+ hcm.addHttpContext(bundle1, SAMPLE_CONTEXT_ID, sampleContext);
+
+ // Servlet 2 is an orphan
+ final ServletMapping bundle2Servlet = new ServletMapping(bundle2, null, BUNDLE_2_ALIAS);
+ HttpContext ctx2 = hcm.getHttpContext(bundle2, SAMPLE_CONTEXT_ID, bundle2Servlet);
+ TestCase.assertNull(ctx2);
+ TestCase.assertNull(bundle2Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id1).getMappings().isEmpty());
+ Map<String, Set<AbstractMapping>> orphans2 = hcm.getOrphanMappings();
+ TestCase.assertEquals(1, orphans2.size());
+ TestCase.assertEquals(1, orphans2.get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(orphans2.get(SAMPLE_CONTEXT_ID).contains(bundle2Servlet));
+
+ // unregister unused context for bundle1
+ hcm.removeHttpContext(sampleContext);
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+
+ // register context for bundle2
+ final String id2 = createId(bundle1, SAMPLE_CONTEXT_ID);
+ hcm.addHttpContext(bundle2, SAMPLE_CONTEXT_ID, sampleContext);
+ TestCase.assertEquals(1, hcm.getHttpContexts().size());
+ TestCase.assertSame(sampleContext, hcm.getHttpContexts().get(id2).getContext());
+
+ TestCase.assertSame(sampleContext, bundle2Servlet.getContext());
+ TestCase.assertEquals(1, hcm.getHttpContexts().get(id2).getMappings().size());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id2).getMappings().contains(bundle2Servlet));
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+
+ // cleanup
+ hcm.ungetHttpContext(bundle2, SAMPLE_CONTEXT_ID, bundle2Servlet);
+ TestCase.assertNull(bundle2Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id2).getMappings().isEmpty());
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_get_unget_HttpContext_shared()
+ {
+ final HttpContextManager hcm = new HttpContextManager();
+ final String id = createId(SAMPLE_CONTEXT_ID);
+ hcm.addHttpContext(null, SAMPLE_CONTEXT_ID, sampleContext);
+
+ // Servlet 1 gets the context
+ final ServletMapping bundle1Servlet = new ServletMapping(bundle1, null, BUNDLE_1_ALIAS);
+ HttpContext ctx1 = hcm.getHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ TestCase.assertNotNull(ctx1);
+ TestCase.assertSame(ctx1, bundle1Servlet.getContext());
+ TestCase.assertSame(sampleContext, ctx1);
+ TestCase.assertEquals(1, hcm.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().contains(bundle1Servlet));
+ Map<String, Set<AbstractMapping>> orphans1 = hcm.getOrphanMappings();
+ TestCase.assertTrue(orphans1.isEmpty());
+
+ // unregister serlvet 1 --> all references clear
+ hcm.ungetHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ TestCase.assertNull(bundle1Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().isEmpty());
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+
+ // Servlet 2 gets the context
+ final ServletMapping bundle2Servlet = new ServletMapping(bundle2, null, BUNDLE_2_ALIAS);
+ HttpContext ctx2 = hcm.getHttpContext(bundle2, SAMPLE_CONTEXT_ID, bundle2Servlet);
+ TestCase.assertNotNull(ctx2);
+ TestCase.assertSame(ctx2, bundle2Servlet.getContext());
+ TestCase.assertSame(sampleContext, ctx2);
+ TestCase.assertEquals(1, hcm.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().contains(bundle2Servlet));
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+
+ // register Servlet 1 again --> gets context
+ hcm.getHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ HttpContext ctx3 = hcm.getHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ TestCase.assertNotNull(ctx3);
+ TestCase.assertSame(ctx3, bundle1Servlet.getContext());
+ TestCase.assertSame(sampleContext, ctx3);
+ TestCase.assertEquals(2, hcm.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().contains(bundle1Servlet));
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+
+ // unregister context --> all references clear
+ hcm.removeHttpContext(sampleContext);
+ TestCase.assertNull(bundle1Servlet.getContext());
+ TestCase.assertNull(bundle2Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+ TestCase.assertEquals(1, hcm.getOrphanMappings().size());
+ TestCase.assertEquals(2, hcm.getOrphanMappings().get(SAMPLE_CONTEXT_ID).size());
+ TestCase.assertTrue(hcm.getOrphanMappings().get(SAMPLE_CONTEXT_ID).contains(bundle1Servlet));
+ TestCase.assertTrue(hcm.getOrphanMappings().get(SAMPLE_CONTEXT_ID).contains(bundle2Servlet));
+
+ // register context --> servlets 1, 2 get context
+ hcm.addHttpContext(null, SAMPLE_CONTEXT_ID, sampleContext);
+ TestCase.assertSame(sampleContext, bundle1Servlet.getContext());
+ TestCase.assertSame(sampleContext, bundle2Servlet.getContext());
+ TestCase.assertEquals(2, hcm.getHttpContexts().get(id).getMappings().size());
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().contains(bundle1Servlet));
+ TestCase.assertTrue(hcm.getHttpContexts().get(id).getMappings().contains(bundle2Servlet));
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+
+ // cleanup
+ hcm.removeHttpContext(sampleContext);
+ hcm.ungetHttpContext(bundle1, SAMPLE_CONTEXT_ID, bundle1Servlet);
+ hcm.ungetHttpContext(bundle2, SAMPLE_CONTEXT_ID, bundle2Servlet);
+ TestCase.assertNull(bundle1Servlet.getContext());
+ TestCase.assertNull(bundle2Servlet.getContext());
+ TestCase.assertTrue(hcm.getHttpContexts().isEmpty());
+ TestCase.assertTrue(hcm.getOrphanMappings().isEmpty());
+ }
+
+ @Test
+ public void test_createId_Bundle_String()
+ {
+ TestCase.assertEquals(BUNDLE_1_ID + "-", createId(bundle1, null));
+ TestCase.assertEquals(BUNDLE_1_ID + "-", createId(bundle1, ""));
+ TestCase.assertEquals(BUNDLE_1_ID + "-" + SAMPLE_CONTEXT_ID, createId(bundle1, SAMPLE_CONTEXT_ID));
+ }
+
+ @Test
+ public void test_createId_String()
+ {
+ TestCase.assertEquals("shared-", createId(null));
+ TestCase.assertEquals("shared-", createId(""));
+ TestCase.assertEquals("shared-" + SAMPLE_CONTEXT_ID, createId(SAMPLE_CONTEXT_ID));
+ }
+
+ static String createId(String contextId)
+ {
+ try
+ {
+ Method m = HttpContextManager.class.getDeclaredMethod("createId", String.class);
+ m.setAccessible(true);
+ return (String) m.invoke(null, contextId);
+ }
+ catch (Throwable t)
+ {
+ TestCase.fail(t.toString());
+ return null; // compiler satisfaction
+ }
+ }
+
+ static String createId(Bundle bundle, String contextId)
+ {
+ try
+ {
+ Method m = HttpContextManager.class.getDeclaredMethod("createId", Bundle.class, String.class);
+ m.setAccessible(true);
+ return (String) m.invoke(null, bundle, contextId);
+ }
+ catch (Throwable t)
+ {
+ TestCase.fail(t.toString());
+ return null; // compiler satisfaction
+ }
+ }
+}
diff --git a/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ServletMappingTest.java b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ServletMappingTest.java
new file mode 100644
index 0000000..2596838
--- /dev/null
+++ b/http/whiteboard/src/test/java/org/apache/felix/http/whiteboard/internal/manager/ServletMappingTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.internal.manager;
+
+import static org.mockito.Mockito.when;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.osgi.framework.Bundle;
+import org.osgi.service.http.HttpContext;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ServletMappingTest
+{
+
+ private static final String SAMPLE_CONTEXT_ID = "some.context.id";
+
+ private static final long BUNDLE_ID = 1L;
+
+ private static final String SERVLET_ALIAS = "/bundle";
+
+ @Mock
+ private HttpContext sampleContext;
+
+ @Mock
+ private Bundle bundle;
+
+ @Mock
+ private ExtenderManagerTest.ExtServlet servlet;
+
+ private ExtenderManagerTest.MockExtHttpService httpService;
+
+ @Before
+ public void setup()
+ {
+ when(bundle.getBundleId()).thenReturn(BUNDLE_ID);
+
+ this.httpService = new ExtenderManagerTest.MockExtHttpService();
+ }
+
+ @After
+ public void tearDown()
+ {
+ this.httpService = null;
+ }
+
+ @Test
+ public void test_with_context()
+ {
+ ServletMapping sm = new ServletMapping(bundle, servlet, SERVLET_ALIAS);
+ TestCase.assertSame(bundle, sm.getBundle());
+ TestCase.assertSame(servlet, sm.getServlet());
+ TestCase.assertEquals(SERVLET_ALIAS, sm.getAlias());
+
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertNotNull(sm.getInitParams());
+ TestCase.assertTrue(sm.getInitParams().isEmpty());
+ TestCase.assertFalse(sm.isRegistered());
+
+ sm.setContext(sampleContext);
+ TestCase.assertSame(sampleContext, sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+
+ sm.register(this.httpService);
+ TestCase.assertSame(sampleContext, sm.getContext());
+ TestCase.assertTrue(sm.isRegistered());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet, this.httpService.getServlets().get(SERVLET_ALIAS));
+ TestCase.assertSame(sampleContext, servlet.getHttpContext());
+
+ sm.unregister(this.httpService);
+ TestCase.assertSame(sampleContext, sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+
+ sm.setContext(null);
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ }
+
+ @Test
+ public void test_context_delayed()
+ {
+ ServletMapping sm = new ServletMapping(bundle, servlet, SERVLET_ALIAS);
+ TestCase.assertSame(bundle, sm.getBundle());
+ TestCase.assertSame(servlet, sm.getServlet());
+ TestCase.assertEquals(SERVLET_ALIAS, sm.getAlias());
+
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertNotNull(sm.getInitParams());
+ TestCase.assertTrue(sm.getInitParams().isEmpty());
+ TestCase.assertFalse(sm.isRegistered());
+
+ sm.register(this.httpService);
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+
+ sm.unregister(httpService);
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+
+ sm.setContext(sampleContext);
+ TestCase.assertSame(sampleContext, sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+ }
+
+ @Test
+ public void test_unset_context()
+ {
+ ServletMapping sm = new ServletMapping(bundle, servlet, SERVLET_ALIAS);
+ TestCase.assertSame(bundle, sm.getBundle());
+ TestCase.assertSame(servlet, sm.getServlet());
+ TestCase.assertEquals(SERVLET_ALIAS, sm.getAlias());
+
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertNotNull(sm.getInitParams());
+ TestCase.assertTrue(sm.getInitParams().isEmpty());
+ TestCase.assertFalse(sm.isRegistered());
+
+ sm.setContext(sampleContext);
+ TestCase.assertSame(sampleContext, sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertTrue(this.httpService.getServlets().isEmpty());
+
+ sm.register(this.httpService);
+ TestCase.assertSame(sampleContext, sm.getContext());
+ TestCase.assertTrue(sm.isRegistered());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet, this.httpService.getServlets().get(SERVLET_ALIAS));
+ TestCase.assertSame(sampleContext, servlet.getHttpContext());
+
+ // does not unregister yet
+ sm.setContext(null);
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertTrue(sm.isRegistered());
+ TestCase.assertEquals(1, this.httpService.getServlets().size());
+ TestCase.assertSame(servlet, this.httpService.getServlets().get(SERVLET_ALIAS));
+ TestCase.assertSame(sampleContext, servlet.getHttpContext());
+
+ sm.unregister(this.httpService);
+ TestCase.assertNull(sm.getContext());
+ TestCase.assertFalse(sm.isRegistered());
+ TestCase.assertEquals(0, this.httpService.getServlets().size());
+ }
+}