FELIX-4888 : ServletHandler's are not sorted by longest matching path. Implement new servlet registry, start filter holder implementation
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1679833 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/FilterHolder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/FilterHolder.java
new file mode 100644
index 0000000..e7e478c
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/FilterHolder.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler.holder;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+import org.apache.felix.http.base.internal.handler.FilterConfigImpl;
+import org.apache.felix.http.base.internal.logger.SystemLogger;
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+
+/**
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public abstract class FilterHolder implements Comparable<FilterHolder>
+{
+ private final FilterInfo filterInfo;
+
+ private final ServletContext context;
+
+ private volatile Filter filter;
+
+ protected volatile int useCount;
+
+ public FilterHolder(final ServletContext context,
+ final FilterInfo filterInfo)
+ {
+ this.context = context;
+ this.filterInfo = filterInfo;
+ }
+
+ @Override
+ public int compareTo(final FilterHolder other)
+ {
+ return this.filterInfo.compareTo(other.filterInfo);
+ }
+
+ protected ServletContext getContext()
+ {
+ return this.context;
+ }
+
+ protected Filter getFilter()
+ {
+ return filter;
+ }
+
+ protected void setFilter(final Filter f)
+ {
+ this.filter = f;
+ }
+
+ public FilterInfo getFilterInfo()
+ {
+ return this.filterInfo;
+ }
+
+ protected String getName()
+ {
+ String name = this.filterInfo.getName();
+ if (name == null)
+ {
+ name = filter.getClass().getName();
+ }
+ return name;
+ }
+
+ /**
+ * Initialize the object
+ * @return {code -1} on success, a failure reason according to {@link DTOConstants} otherwise.
+ */
+ public int init()
+ {
+ if ( this.useCount > 0 )
+ {
+ return -1;
+ }
+
+ if (this.filter == null)
+ {
+ return DTOConstants.FAILURE_REASON_SERVICE_NOT_GETTABLE;
+ }
+
+ try
+ {
+ filter.init(new FilterConfigImpl(getName(), getContext(), getFilterInfo().getInitParameters()));
+ }
+ catch (final ServletException e)
+ {
+ SystemLogger.error(this.getFilterInfo().getServiceReference(),
+ "Error during calling init() on filter " + this.filter,
+ e);
+ return DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT;
+ }
+ this.useCount++;
+ return -1;
+ }
+
+
+ public boolean destroy()
+ {
+ if (this.filter == null)
+ {
+ return false;
+ }
+
+ this.useCount--;
+ if ( this.useCount == 0 )
+ {
+ try
+ {
+ filter.destroy();
+ }
+ catch ( final Exception ignore )
+ {
+ // we ignore this
+ SystemLogger.error(this.getFilterInfo().getServiceReference(),
+ "Error during calling destroy() on filter " + this.filter,
+ ignore);
+ }
+
+ filter = null;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean dispose()
+ {
+ // fully destroy the filter
+ this.useCount = 1;
+ return this.destroy();
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/HttpServiceFilterHolder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/HttpServiceFilterHolder.java
new file mode 100644
index 0000000..3bde5ae
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/HttpServiceFilterHolder.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler.holder;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletContext;
+
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+
+/**
+ * Servlet holder for filters registered through the ext http service.
+ */
+public final class HttpServiceFilterHolder extends FilterHolder
+{
+ public HttpServiceFilterHolder(final ServletContext context,
+ final FilterInfo filterInfo,
+ final Filter filter)
+ {
+ super(context, filterInfo);
+ this.setFilter(filter);
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/ServletHolder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/ServletHolder.java
index ee2ab6f..a939298 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/ServletHolder.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/ServletHolder.java
@@ -150,4 +150,11 @@
}
return false;
}
+
+ public boolean dispose()
+ {
+ // fully destroy the servlet
+ this.useCount = 1;
+ return this.destroy();
+ }
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/WhiteboardFilterHolder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/WhiteboardFilterHolder.java
new file mode 100644
index 0000000..c1d2d2f
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/WhiteboardFilterHolder.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.handler.holder;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletContext;
+
+import org.apache.felix.http.base.internal.runtime.FilterInfo;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceObjects;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Filter holder for filters registered through the http whiteboard.
+ */
+public final class WhiteboardFilterHolder extends FilterHolder
+{
+ private final BundleContext bundleContext;
+
+ public WhiteboardFilterHolder(final ServletContext context,
+ final FilterInfo filterInfo,
+ final BundleContext bundleContext)
+ {
+ super(context, filterInfo);
+ this.bundleContext = bundleContext;
+ }
+
+ @Override
+ public int init()
+ {
+ if ( this.useCount > 0 )
+ {
+ return -1;
+ }
+
+ final ServiceReference<Filter> serviceReference = getFilterInfo().getServiceReference();
+ final ServiceObjects<Filter> so = this.bundleContext.getServiceObjects(serviceReference);
+
+ this.setFilter((so == null ? null : so.getService()));
+
+ final int reason = super.init();
+ if ( reason != -1 )
+ {
+ so.ungetService(this.getFilter());
+ this.setFilter(null);
+ }
+ return -reason;
+ }
+
+ @Override
+ public boolean destroy()
+ {
+ final Filter s = this.getFilter();
+ if ( s != null )
+ {
+ if ( super.destroy() )
+ {
+
+ final ServiceObjects<Filter> so = this.bundleContext.getServiceObjects(getFilterInfo().getServiceReference());
+ if (so != null)
+ {
+ so.ungetService(s);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PerContextHandlerRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PerContextHandlerRegistry.java
index 8d175ae..8c55c2e 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PerContextHandlerRegistry.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PerContextHandlerRegistry.java
@@ -16,19 +16,11 @@
*/
package org.apache.felix.http.base.internal.registry;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Pattern;
-
import javax.annotation.Nonnull;
import org.apache.felix.http.base.internal.handler.holder.ServletHolder;
import org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo;
-import org.apache.felix.http.base.internal.util.PatternUtil;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
/**
* This registry keeps track of all processing components per context:
@@ -50,9 +42,7 @@
/** The context prefix. */
private final String prefix;
- private Map<String, ServletHandler> activateServletMappings = new ConcurrentHashMap<String, ServletHandler>();
-
- private Map<String, List<ServletHolder>> inactivateServletMappings = new HashMap<String, List<ServletHolder>>();
+ private final ServletRegistry servletRegistry = new ServletRegistry();
/**
* Default http service registry
@@ -126,18 +116,7 @@
public PathResolution resolve(final String relativeRequestURI)
{
- int len = -1;
- PathResolution candidate = null;
- for(final Map.Entry<String, ServletHandler> entry : this.activateServletMappings.entrySet())
- {
- final PathResolution pr = entry.getValue().resolve(relativeRequestURI);
- if ( pr != null && entry.getKey().length() > len )
- {
- candidate = pr;
- len = entry.getKey().length();
- }
- }
- return candidate;
+ return this.servletRegistry.resolve(relativeRequestURI);
}
/**
@@ -147,70 +126,15 @@
*/
public void addServlet(@Nonnull final ServletHolder holder)
{
- // we have to check for every pattern in the info
- // Can be null in case of error-handling servlets...
- final String[] patternStrings = holder.getServletInfo().getPatterns();
- if ( patternStrings != null )
- {
- final int length = patternStrings.length;
- for (int i = 0; i < length; i++)
- {
- final String pattern = patternStrings[i];
-
- final ServletHandler regHandler = this.activateServletMappings.get(pattern);
- if ( regHandler != null )
- {
- if ( regHandler.getServletHolder().getServletInfo().getServiceReference().compareTo(holder.getServletInfo().getServiceReference()) < 0 )
- {
- // replace if no error with new servlet
- if ( holder.init() == -1 )
- {
- final Pattern p = Pattern.compile(PatternUtil.convertToRegEx(pattern));
- final ServletHandler handler = new ServletHandler(holder, p);
- this.activateServletMappings.put(pattern, handler);
-
- regHandler.getServletHolder().destroy();
-
- this.addToInactiveList(pattern, regHandler.getServletHolder());
- }
- else
- {
- // TODO - add to failure
- }
- }
- else
- {
- // add to inactive
- this.addToInactiveList(pattern, holder);
- }
- }
- else
- {
- // add to active
- if ( holder.init() == -1 )
- {
- final Pattern p = Pattern.compile(PatternUtil.convertToRegEx(pattern));
- final ServletHandler handler = new ServletHandler(holder, p);
- this.activateServletMappings.put(pattern, handler);
- }
- else
- {
- // TODO - add to failure
- }
- }
- }
- }
+ this.servletRegistry.addServlet(holder);
}
- private void addToInactiveList(final String pattern, final ServletHolder holder)
+ /**
+ * Remove a servlet
+ * @param info The servlet info
+ */
+ public void removeServlet(@Nonnull final ServletInfo info)
{
- List<ServletHolder> inactiveList = this.inactivateServletMappings.get(pattern);
- if ( inactiveList == null )
- {
- inactiveList = new ArrayList<ServletHolder>(inactiveList);
- this.inactivateServletMappings.put(pattern, inactiveList);
- }
- inactiveList.add(holder);
- Collections.sort(inactiveList);
+ this.servletRegistry.removeServlet(info);
}
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ServletRegistry.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ServletRegistry.java
new file mode 100644
index 0000000..3c521fe
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ServletRegistry.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.registry;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+import javax.annotation.Nonnull;
+
+import org.apache.felix.http.base.internal.handler.holder.ServletHolder;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.apache.felix.http.base.internal.util.PatternUtil;
+import org.osgi.service.http.runtime.dto.DTOConstants;
+
+/**
+ * The servlet registry keeps the mappings for all servlets (by using their pattern)
+ * for a single servlet context.
+ */
+public final class ServletRegistry
+{
+ private final Map<String, ServletHandler> activateServletMapping = new ConcurrentHashMap<String, ServletHandler>();
+
+ private final Map<String, List<ServletHolder>> inactivateServletMapping = new HashMap<String, List<ServletHolder>>();
+
+ private final Map<ServletInfo, ServletRegistrationStatus> statusMapping = new ConcurrentHashMap<ServletInfo, ServletRegistry.ServletRegistrationStatus>();
+
+ public static final class ServletRegistrationStatus
+ {
+ public final Map<String, Integer> pathToStatus = new ConcurrentHashMap<String, Integer>();
+ }
+
+ public PathResolution resolve(final String relativeRequestURI)
+ {
+ int len = -1;
+ PathResolution candidate = null;
+ for(final Map.Entry<String, ServletHandler> entry : this.activateServletMapping.entrySet())
+ {
+ final PathResolution pr = entry.getValue().resolve(relativeRequestURI);
+ if ( pr != null && entry.getKey().length() > len )
+ {
+ candidate = pr;
+ len = entry.getKey().length();
+ }
+ }
+ return candidate;
+ }
+
+ /**
+ * Add a servlet
+ * @param holder The servlet holder
+ */
+ public void addServlet(@Nonnull final ServletHolder holder)
+ {
+ final ServletRegistrationStatus status = new ServletRegistrationStatus();
+
+ // we have to check for every pattern in the info
+ // Can be null in case of error-handling servlets...
+ if ( holder.getServletInfo().getPatterns() != null )
+ {
+ for(final String pattern : holder.getServletInfo().getPatterns())
+ {
+ final ServletHandler regHandler = this.activateServletMapping.get(pattern);
+ if ( regHandler != null )
+ {
+ if ( regHandler.getServletHolder().getServletInfo().getServiceReference().compareTo(holder.getServletInfo().getServiceReference()) < 0 )
+ {
+ // replace if no error with new servlet
+ if ( this.tryToActivate(pattern, holder, status) )
+ {
+ regHandler.getServletHolder().destroy();
+
+ this.addToInactiveList(pattern, regHandler.getServletHolder(), this.statusMapping.get(regHandler.getServletHolder().getServletInfo()));
+ }
+ }
+ else
+ {
+ // add to inactive
+ this.addToInactiveList(pattern, holder, status);
+ }
+ }
+ else
+ {
+ // add to active
+ this.tryToActivate(pattern, holder, status);
+ }
+ }
+ this.statusMapping.put(holder.getServletInfo(), status);
+ }
+ }
+
+ /**
+ * Remove a servlet
+ * @param info The servlet info
+ */
+ public void removeServlet(@Nonnull final ServletInfo info)
+ {
+ if ( info.getPatterns() != null )
+ {
+ this.statusMapping.remove(info);
+ ServletHolder cleanupHolder = null;
+
+ for(final String pattern : info.getPatterns())
+ {
+
+ final ServletHandler regHandler = this.activateServletMapping.get(pattern);
+ if ( regHandler != null && regHandler.getServletHolder().getServletInfo().equals(info) )
+ {
+ cleanupHolder = regHandler.getServletHolder();
+ final List<ServletHolder> inactiveList = this.inactivateServletMapping.get(pattern);
+ if ( inactiveList == null )
+ {
+ this.activateServletMapping.remove(pattern);
+ }
+ else
+ {
+ boolean done = false;
+ while ( !done )
+ {
+ final ServletHolder h = inactiveList.remove(0);
+ done = this.tryToActivate(pattern, h, this.statusMapping.get(h.getServletInfo()));
+ if ( !done )
+ {
+ done = inactiveList.isEmpty();
+ }
+ }
+ if ( inactiveList.isEmpty() )
+ {
+ this.inactivateServletMapping.remove(pattern);
+ }
+ }
+ }
+ else
+ {
+ final List<ServletHolder> inactiveList = this.inactivateServletMapping.get(pattern);
+ if ( inactiveList != null )
+ {
+ final Iterator<ServletHolder> i = inactiveList.iterator();
+ while ( i.hasNext() )
+ {
+ final ServletHolder h = i.next();
+ if ( h.getServletInfo().equals(info) )
+ {
+ i.remove();
+ cleanupHolder = h;
+ break;
+ }
+ }
+ if ( inactiveList.isEmpty() )
+ {
+ this.inactivateServletMapping.remove(pattern);
+ }
+ }
+ }
+ }
+
+ if ( cleanupHolder != null )
+ {
+ cleanupHolder.dispose();
+ }
+ }
+ }
+
+ private void addToInactiveList(final String pattern, final ServletHolder holder, final ServletRegistrationStatus status)
+ {
+ List<ServletHolder> inactiveList = this.inactivateServletMapping.get(pattern);
+ if ( inactiveList == null )
+ {
+ inactiveList = new ArrayList<ServletHolder>(inactiveList);
+ this.inactivateServletMapping.put(pattern, inactiveList);
+ }
+ inactiveList.add(holder);
+ Collections.sort(inactiveList);
+ status.pathToStatus.put(pattern, DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
+ }
+
+ private boolean tryToActivate(final String pattern, final ServletHolder holder, final ServletRegistrationStatus status)
+ {
+ // add to active
+ final int result = holder.init();
+ if ( result == -1 )
+ {
+ final Pattern p = Pattern.compile(PatternUtil.convertToRegEx(pattern));
+ final ServletHandler handler = new ServletHandler(holder, p);
+ this.activateServletMapping.put(pattern, handler);
+
+ // add ok
+ status.pathToStatus.put(pattern, result);
+ return true;
+ }
+ else
+ {
+ // add to failure
+ status.pathToStatus.put(pattern, result);
+ return false;
+ }
+ }
+
+ public Map<ServletInfo, ServletRegistrationStatus> getServletStatusMapping()
+ {
+ return this.statusMapping;
+ }
+}
diff --git a/http/base/src/test/java/org/apache/felix/http/base/internal/registry/ServletRegistryTest.java b/http/base/src/test/java/org/apache/felix/http/base/internal/registry/ServletRegistryTest.java
new file mode 100644
index 0000000..d440cc1
--- /dev/null
+++ b/http/base/src/test/java/org/apache/felix/http/base/internal/registry/ServletRegistryTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.http.base.internal.registry;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+
+import org.apache.felix.http.base.internal.handler.holder.HttpServiceServletHolder;
+import org.apache.felix.http.base.internal.handler.holder.ServletHolder;
+import org.apache.felix.http.base.internal.runtime.ServletInfo;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
+
+public class ServletRegistryTest {
+
+ private final ServletRegistry reg = new ServletRegistry();
+
+ @Test public void testSingleServlet() throws InvalidSyntaxException
+ {
+ final Map<ServletInfo, ServletRegistry.ServletRegistrationStatus> status = reg.getServletStatusMapping();
+ // empty reg
+ assertEquals(0, status.size());
+
+ // register servlet
+ final ServletHolder h1 = createServletHolder(1L, 0, "/foo");
+ reg.addServlet(h1);
+
+ // one entry in reg
+ assertEquals(1, status.size());
+ assertNotNull(status.get(h1.getServletInfo()));
+ assertNotNull(status.get(h1.getServletInfo()).pathToStatus.get("/foo"));
+ final int code = status.get(h1.getServletInfo()).pathToStatus.get("/foo");
+ assertEquals(-1, code);
+
+ // remove servlet
+ reg.removeServlet(h1.getServletInfo());
+
+ // empty again
+ assertEquals(0, status.size());
+ }
+
+ private static ServletInfo createServletInfo(final long id, final int ranking, final String... paths) throws InvalidSyntaxException
+ {
+ final BundleContext bCtx = mock(BundleContext.class);
+ when(bCtx.createFilter(Matchers.anyString())).thenReturn(null);
+ final Bundle bundle = mock(Bundle.class);
+ when(bundle.getBundleContext()).thenReturn(bCtx);
+
+ final ServiceReference<Servlet> ref = mock(ServiceReference.class);
+ when(ref.getBundle()).thenReturn(bundle);
+ when(ref.getProperty(Constants.SERVICE_ID)).thenReturn(id);
+ when(ref.getProperty(Constants.SERVICE_RANKING)).thenReturn(ranking);
+ when(ref.getProperty(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN)).thenReturn(paths);
+ when(ref.getPropertyKeys()).thenReturn(new String[0]);
+ final ServletInfo si = new ServletInfo(ref);
+
+ return si;
+ }
+
+ private static ServletHolder createServletHolder(final long id, final int ranking, final String... paths) throws InvalidSyntaxException
+ {
+ final ServletInfo si = createServletInfo(id, ranking, paths);
+ final ServletContext ctx = mock(ServletContext.class);
+ final Servlet servlet = mock(Servlet.class);
+
+ return new HttpServiceServletHolder(ctx, si, servlet);
+ }
+}