FELIX-4888 : ServletHandler's are not sorted by longest matching path. Start new registry implementation
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1679748 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
index de8fef3..2f4eaac 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/ErrorsMapping.java
@@ -32,18 +32,18 @@
private final Map<Integer, ServletHandler> errorCodesMap;
private final Map<String, ServletHandler> exceptionsMap;
- ErrorsMapping()
+ public ErrorsMapping()
{
this(new HashMap<Integer, ServletHandler>(), new HashMap<String, ServletHandler>());
}
- ErrorsMapping(Map<Integer, ServletHandler> errorCodesMap, Map<String, ServletHandler> exceptionsMap)
+ public ErrorsMapping(Map<Integer, ServletHandler> errorCodesMap, Map<String, ServletHandler> exceptionsMap)
{
this.errorCodesMap = errorCodesMap;
this.exceptionsMap = exceptionsMap;
}
- ErrorsMapping update(Map<String, ServletHandler> add, Map<String, ServletHandler> remove) throws RegistrationFailureException
+ public ErrorsMapping update(Map<String, ServletHandler> add, Map<String, ServletHandler> remove) throws RegistrationFailureException
{
Map<Integer, ServletHandler> newErrorCodesMap = new HashMap<Integer, ServletHandler>(this.errorCodesMap);
Map<String, ServletHandler> newExceptionsMap = new HashMap<String, ServletHandler>(this.exceptionsMap);;
@@ -160,7 +160,7 @@
@SuppressWarnings("unchecked")
- Collection<ServletHandler> values()
+ public Collection<ServletHandler> values()
{
return sortedUnion(errorCodesMap.values(), exceptionsMap.values());
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
index 9a7210d..ee9066f 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerMapping.java
@@ -42,10 +42,10 @@
* 3.0 specification.
* <p>
* {@link HandlerMapping} instances are immutable.
- *
+ *
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-final class HandlerMapping<V extends AbstractHandler<V>>
+public final class HandlerMapping<V extends AbstractHandler<V>>
{
private final SortedMap<Pattern, Set<V>> exactMap;
private final SortedMap<Pattern, Set<V>> wildcardMap;
@@ -54,7 +54,7 @@
/**
* Creates a new, empty, {@link HandlerMapping} instance.
*/
- HandlerMapping()
+ public HandlerMapping()
{
this(Collections.<Pattern, Collection<V>>emptyMap());
}
@@ -108,7 +108,7 @@
* @return a new {@link HandlerMapping} instance with a mapping for the
* given handler.
*/
- HandlerMapping<V> add(V handler)
+ public HandlerMapping<V> add(V handler)
{
Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternComparator.INSTANCE);
for (Pattern pattern : handler.getPatterns())
@@ -133,7 +133,7 @@
* @return a new {@link HandlerMapping} instance without a mapping for the
* given handler.
*/
- HandlerMapping<V> remove(V handler)
+ public HandlerMapping<V> remove(V handler)
{
Map<Pattern, V> mappings = new TreeMap<Pattern, V>(PatternComparator.INSTANCE);
for (Pattern pattern : handler.getPatterns())
@@ -197,23 +197,23 @@
/**
* Returns all mapped handlers.
- *
+ *
* @return the handlers contained in this mapping. The returned
* <code>Collection</code> is unmodifiable and never
* <code>null</code>.
*/
- Collection<V> values()
+ public Collection<V> values()
{
return unmodifiableCollection(mappedHandlers);
}
/**
* Returns whether this mapping contains the specified handler.
- *
+ *
* @return <code>true</code> if the handlers contains the specified handler,
* <code>false</code> otherwise
*/
- boolean contains(V handler)
+ public boolean contains(V handler)
{
return mappedHandlers.contains(handler);
}
@@ -224,7 +224,7 @@
* @param path the path that should match, cannot be <code>null</code>.
* @return a {@link Collection} of all matching handlers, never <code>null</code>.
*/
- List<V> getAllMatches(String path)
+ public List<V> getAllMatches(String path)
{
return getAllMatches(path, false /* firstOnly */);
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimap.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimap.java
index e9a426a..70322cc 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimap.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/HandlerRankingMultimap.java
@@ -32,7 +32,7 @@
import java.util.TreeSet;
-final class HandlerRankingMultimap<K>
+public final class HandlerRankingMultimap<K>
{
private final Map<ServletHandler, Integer> useCounts = new TreeMap<ServletHandler, Integer>();
@@ -42,19 +42,19 @@
private int size = 0;
- HandlerRankingMultimap()
+ public HandlerRankingMultimap()
{
this.handlerMultimap = new HashMap<K, PriorityQueue<ServletHandler>>();
this.keyComparator = null;
this.handlerComparator = null;
}
- HandlerRankingMultimap(Comparator<K> keyComparator)
+ public HandlerRankingMultimap(Comparator<K> keyComparator)
{
this(keyComparator, null);
}
- HandlerRankingMultimap(Comparator<K> keyComparator, Comparator<ServletHandler> handlerComparator)
+ public HandlerRankingMultimap(Comparator<K> keyComparator, Comparator<ServletHandler> handlerComparator)
{
this.keyComparator = keyComparator;
this.handlerMultimap = new TreeMap<K, PriorityQueue<ServletHandler>>(keyComparator);
@@ -66,7 +66,7 @@
return useCounts.containsKey(handler);
}
- Update<K> add(K[] keys, ServletHandler handler)
+ public Update<K> add(K[] keys, ServletHandler handler)
{
return add(asList(keys), handler);
}
@@ -103,7 +103,7 @@
return Update.forAdd(activate, deactivate, destroy);
}
- Update<K> remove(K[] keys, ServletHandler handler)
+ public Update<K> remove(K[] keys, ServletHandler handler)
{
return remove(asList(keys), handler);
}
@@ -199,13 +199,13 @@
return newCount;
}
- void clear()
+ public void clear()
{
handlerMultimap.clear();
useCounts.clear();
}
- Collection<ServletHandler> getActiveValues()
+ public Collection<ServletHandler> getActiveValues()
{
TreeSet<ServletHandler> activeValues = new TreeSet<ServletHandler>();
for (PriorityQueue<ServletHandler> queue : handlerMultimap.values())
@@ -215,7 +215,7 @@
return activeValues;
}
- Collection<ServletHandler> getShadowedValues()
+ public Collection<ServletHandler> getShadowedValues()
{
TreeSet<ServletHandler> shadowedValues = new TreeSet<ServletHandler>();
for (PriorityQueue<ServletHandler> queue : handlerMultimap.values())
@@ -251,7 +251,7 @@
return keyComparator == null ? new HashMap<K, ServletHandler>() : new TreeMap<K, ServletHandler>(keyComparator);
}
- static final class Update<K>
+ public static final class Update<K>
{
private final Map<K, ServletHandler> activate;
private final Map<K, ServletHandler> deactivate;
@@ -299,22 +299,22 @@
return valueSet;
}
- Map<K, ServletHandler> getActivated()
+ public Map<K, ServletHandler> getActivated()
{
return activate;
}
- Map<K, ServletHandler> getDeactivated()
+ public Map<K, ServletHandler> getDeactivated()
{
return deactivate;
}
- Collection<ServletHandler> getInit()
+ public Collection<ServletHandler> getInit()
{
return init;
}
- Collection<ServletHandler> getDestroy()
+ public Collection<ServletHandler> getDestroy()
{
return destroy;
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/AbstractHolder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/AbstractHolder.java
deleted file mode 100644
index 59b30fb..0000000
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/AbstractHolder.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.felix.http.base.internal.handler.holder;
-
-import javax.servlet.ServletContext;
-
-import org.osgi.service.http.runtime.dto.DTOConstants;
-
-public abstract class AbstractHolder<T extends AbstractHolder<?>> implements Comparable<T>
-{
- private final ServletContext context;
-
- public AbstractHolder(final ServletContext context)
- {
- this.context = context;
- }
-
- public ServletContext getContext()
- {
- return this.context;
- }
-
- /**
- * Initialize the object
- * @return {code -1} on success, a failure reason according to {@link DTOConstants} otherwise.
- */
- public abstract int init();
-
- public abstract void destroy();
-}
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 e6d4a24..ee2ab6f 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
@@ -32,20 +32,34 @@
/**
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
-public abstract class ServletHolder extends AbstractHolder<ServletHolder>
+public abstract class ServletHolder implements Comparable<ServletHolder>
{
private final ServletInfo servletInfo;
+ private final ServletContext context;
+
private volatile Servlet servlet;
+ protected volatile int useCount;
+
public ServletHolder(final ServletContext context,
final ServletInfo servletInfo)
{
- super(context);
-
+ this.context = context;
this.servletInfo = servletInfo;
}
+ @Override
+ public int compareTo(final ServletHolder other)
+ {
+ return this.servletInfo.compareTo(other.servletInfo);
+ }
+
+ protected ServletContext getContext()
+ {
+ return this.context;
+ }
+
protected Servlet getServlet()
{
return servlet;
@@ -56,19 +70,13 @@
this.servlet = s;
}
- @Override
- public int compareTo(final ServletHolder other)
- {
- return this.servletInfo.compareTo(other.servletInfo);
- }
-
public void handle(final ServletRequest req, final ServletResponse res)
throws ServletException, IOException
{
this.servlet.service(req, res);
}
- protected ServletInfo getServletInfo()
+ public ServletInfo getServletInfo()
{
return this.servletInfo;
}
@@ -83,42 +91,63 @@
return name;
}
- @Override
+ /**
+ * 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.servlet == null)
{
return DTOConstants.FAILURE_REASON_SERVICE_NOT_GETTABLE;
}
- try {
+ try
+ {
servlet.init(new ServletConfigImpl(getName(), getContext(), getServletInfo().getInitParameters()));
- } catch (final ServletException e) {
+ }
+ catch (final ServletException e)
+ {
SystemLogger.error(this.getServletInfo().getServiceReference(),
"Error during calling init() on servlet " + this.servlet,
e);
return DTOConstants.FAILURE_REASON_EXCEPTION_ON_INIT;
}
+ this.useCount++;
return -1;
}
- @Override
- public void destroy()
+
+ public boolean destroy()
{
if (this.servlet == null)
{
- return;
+ return false;
}
- try {
- servlet.destroy();
- } catch ( final Exception ignore ) {
- // we ignore this
- SystemLogger.error(this.getServletInfo().getServiceReference(),
- "Error during calling destroy() on servlet " + this.servlet,
- ignore);
- }
+ this.useCount--;
+ if ( this.useCount == 0 )
+ {
+ try
+ {
+ servlet.destroy();
+ }
+ catch ( final Exception ignore )
+ {
+ // we ignore this
+ SystemLogger.error(this.getServletInfo().getServiceReference(),
+ "Error during calling destroy() on servlet " + this.servlet,
+ ignore);
+ }
- servlet = null;
+ servlet = null;
+ return true;
+ }
+ return false;
}
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/WhiteboardServletHolder.java b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/WhiteboardServletHolder.java
index 980f068..6758639 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/WhiteboardServletHolder.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/handler/holder/WhiteboardServletHolder.java
@@ -42,6 +42,11 @@
@Override
public int init()
{
+ if ( this.useCount > 0 )
+ {
+ return -1;
+ }
+
final ServiceReference<Servlet> serviceReference = getServletInfo().getServiceReference();
final ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(serviceReference);
@@ -57,18 +62,22 @@
}
@Override
- public void destroy()
+ public boolean destroy()
{
final Servlet s = this.getServlet();
if ( s != null )
{
- super.destroy();
-
- final ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(getServletInfo().getServiceReference());
- if (so != null)
+ if ( super.destroy() )
{
- so.ungetService(s);
+
+ final ServiceObjects<Servlet> so = this.bundleContext.getServiceObjects(getServletInfo().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/PathResolution.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolution.java
new file mode 100644
index 0000000..9810a78
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolution.java
@@ -0,0 +1,28 @@
+/*
+ * 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 org.apache.felix.http.base.internal.handler.holder.ServletHolder;
+
+public class PathResolution {
+
+ public String servletPath;
+
+ public String pathInfo;
+
+ public ServletHolder holder;
+}
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
new file mode 100644
index 0000000..8d175ae
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PerContextHandlerRegistry.java
@@ -0,0 +1,216 @@
+/*
+ * 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.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;
+
+/**
+ * This registry keeps track of all processing components per context:
+ * - servlets
+ * - filters
+ * - error pages
+ */
+public final class PerContextHandlerRegistry implements Comparable<PerContextHandlerRegistry>
+{
+ /** Service id of the context. */
+ private final long serviceId;
+
+ /** Ranking of the context. */
+ private final int ranking;
+
+ /** The context path. */
+ private final String path;
+
+ /** 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>>();
+
+ /**
+ * Default http service registry
+ */
+ public PerContextHandlerRegistry()
+ {
+ this.serviceId = 0;
+ this.ranking = Integer.MAX_VALUE;
+ this.path = "/";
+ this.prefix = null;
+ }
+
+ /**
+ * Registry for a servlet context helper (whiteboard support)
+ * @param info The servlet context helper info
+ */
+ public PerContextHandlerRegistry(@Nonnull final ServletContextHelperInfo info)
+ {
+ this.serviceId = info.getServiceId();
+ this.ranking = info.getRanking();
+ this.path = info.getPath();
+ if ( this.path.equals("/") )
+ {
+ this.prefix = null;
+ }
+ else
+ {
+ this.prefix = this.path + "/";
+ }
+ }
+
+ @Override
+ public int compareTo(@Nonnull final PerContextHandlerRegistry other)
+ {
+ // the context of the HttpService is the least element
+ if (this.serviceId == 0 ^ other.serviceId == 0)
+ {
+ return this.serviceId == 0 ? -1 : 1;
+ }
+
+ final int result = Integer.compare(other.path.length(), this.path.length());
+ if ( result == 0 ) {
+ if (this.ranking == other.ranking)
+ {
+ // Service id's can be negative. Negative id's follow the reverse natural ordering of integers.
+ int reverseOrder = ( this.serviceId <= 0 && other.serviceId <= 0 ) ? -1 : 1;
+ return reverseOrder * Long.compare(this.serviceId, other.serviceId);
+ }
+
+ return Integer.compare(other.ranking, this.ranking);
+ }
+ return result;
+ }
+
+ public String isMatching(final String requestURI)
+ {
+ if (requestURI.equals(this.path))
+ {
+ return "";
+ }
+ if (this.prefix == null)
+ {
+ return requestURI;
+ }
+ if (requestURI.startsWith(this.prefix))
+ {
+ return requestURI.substring(this.prefix.length() - 1);
+ }
+ return null;
+ }
+
+ 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;
+ }
+
+ /**
+ * Add a servlet
+ * @param holder The servlet holder
+ * @param info The servlet info
+ */
+ 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
+ }
+ }
+ }
+ }
+ }
+
+ private void addToInactiveList(final String pattern, final ServletHolder holder)
+ {
+ 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);
+ }
+}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ServletHandler.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ServletHandler.java
new file mode 100644
index 0000000..fbdbfcf
--- /dev/null
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/ServletHandler.java
@@ -0,0 +1,59 @@
+/*
+ * 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.regex.Matcher;
+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.util.UriUtils;
+
+/**
+ * Servlet handler is registered with a pattern and a servlet holder
+ */
+public class ServletHandler
+{
+ private final ServletHolder holder;
+ private final Pattern pattern;
+
+ public ServletHandler(@Nonnull final ServletHolder holder, @Nonnull final Pattern pattern)
+ {
+ this.holder = holder;
+ this.pattern = pattern;
+ }
+
+ public ServletHolder getServletHolder()
+ {
+ return this.holder;
+ }
+
+ public PathResolution resolve(@Nonnull final String requestURI)
+ {
+ final Matcher matcher = pattern.matcher(requestURI);
+ if (matcher.find(0))
+ {
+ final PathResolution pr = new PathResolution();
+ pr.servletPath = matcher.groupCount() > 0 ? matcher.group(1) : matcher.group();
+ pr.pathInfo = UriUtils.compactPath(UriUtils.relativePath(pr.servletPath, requestURI));
+ pr.holder = this.holder;
+ }
+
+ return null;
+ }
+}