FELIX-4860 : Revisit HandlerRegistry implementation. Improve servlet registry by sorting patterns, highest matching first
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1681578 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolver.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolver.java
index eb40f45..389de1d 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolver.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolver.java
@@ -28,4 +28,6 @@
int getRanking();
int getOrdering();
+
+ String getPattern();
}
diff --git a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolverFactory.java b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolverFactory.java
index da607f9..16b67c8 100644
--- a/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolverFactory.java
+++ b/http/base/src/main/java/org/apache/felix/http/base/internal/registry/PathResolverFactory.java
@@ -49,7 +49,7 @@
}
else if ( pattern.startsWith("*.") )
{
- return new ExtensionMatcher(handler, pattern.substring(1));
+ return new ExtensionMatcher(handler, pattern);
}
else if ( pattern.endsWith("/*") )
{
@@ -67,16 +67,20 @@
{
private final int ranking;
+ private final String pattern;
+
private final ServletHandler handler;
- public AbstractMatcher(final ServletHandler handler, final int ranking)
+ public AbstractMatcher(final ServletHandler handler, final String pattern, final int ranking)
{
this.handler = handler;
this.ranking = ranking;
+ this.pattern = pattern;
}
@Override
- public int compareTo(final PathResolver o) {
+ public int compareTo(final PathResolver o)
+ {
int result = o.getRanking() - this.ranking;
if ( result == 0 )
{
@@ -86,26 +90,55 @@
}
@Override
- public ServletHandler getServletHandler() {
+ public ServletHandler getServletHandler()
+ {
return this.handler;
}
@Override
- public int getRanking() {
+ public int getRanking()
+ {
return this.ranking;
}
@Override
- public int getOrdering() {
+ public int getOrdering()
+ {
return 0;
}
+
+ @Override
+ public String getPattern()
+ {
+ return this.pattern;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return pattern.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final AbstractMatcher other = (AbstractMatcher) obj;
+ return this.pattern.equals(other.pattern);
+ }
}
public static final class RootMatcher extends AbstractMatcher
{
public RootMatcher(final ServletHandler handler)
{
- super(handler, 2);
+ super(handler, "", 2);
}
@Override
@@ -128,7 +161,7 @@
{
public DefaultMatcher(final ServletHandler handler)
{
- super(handler, 1);
+ super(handler, "/", 1);
}
@Override
@@ -151,7 +184,7 @@
public ExactAndPathMatcher(final ServletHandler handler, final String pattern)
{
- super(handler, 4);
+ super(handler, pattern, 4);
this.path = pattern;
this.prefix = pattern.concat("/");
}
@@ -194,7 +227,7 @@
public PathMatcher(final ServletHandler handler, final String pattern)
{
- super(handler, 4);
+ super(handler, pattern, 4);
this.prefix = pattern.substring(0, pattern.length() - 1);
}
@@ -224,10 +257,10 @@
{
private final String extension;
- public ExtensionMatcher(final ServletHandler handler, final String extension)
+ public ExtensionMatcher(final ServletHandler handler, final String pattern)
{
- super(handler, 3);
- this.extension = extension;
+ super(handler, pattern, 3);
+ this.extension = pattern.substring(1);
}
@Override
@@ -258,7 +291,7 @@
public RegexMatcher(final String regex)
{
- super(null, 0);
+ super(null, regex, 0);
this.pattern = Pattern.compile(regex);
}
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
index ced9b7c..bd8fe42 100644
--- 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
@@ -43,13 +43,10 @@
/**
* The servlet registry keeps the mappings for all servlets (by using their pattern)
* for a single servlet context.
- *
- * TODO - sort active servlet mappings by pattern length, longest first (avoids looping over all)
- * TODO - we could collapse all three maps into a single map (activeServletMappings, inactiveServletMappings and statusMapping)
*/
public final class ServletRegistry
{
- private final Map<String, PathResolver> activeServletMappings = new ConcurrentHashMap<String, PathResolver>();
+ private volatile List<PathResolver> activeResolvers = Collections.emptyList();
private final Map<String, List<ServletHandler>> inactiveServletMappings = new HashMap<String, List<ServletHandler>>();
@@ -71,20 +68,30 @@
*/
public PathResolution resolve(@Nonnull final String relativeRequestURI)
{
- PathResolver resolver = null;
- PathResolution candidate = null;
- for(final Map.Entry<String, PathResolver> entry : this.activeServletMappings.entrySet())
+ final List<PathResolver> resolvers = this.activeResolvers;
+ for(final PathResolver entry : resolvers)
{
- final PathResolution pr = entry.getValue().resolve(relativeRequestURI);
- if ( pr != null && (resolver == null || entry.getValue().compareTo(resolver) < 0) )
+ final PathResolution pr = entry.resolve(relativeRequestURI);
+ if ( pr != null )
{
// TODO - we should have all patterns under which this servlet is actively registered
- pr.patterns = new String[] {entry.getKey()};
- candidate = pr;
- resolver = entry.getValue();
+ pr.patterns = new String[] {entry.getPattern()};
+ return pr;
}
}
- return candidate;
+ return null;
+ }
+
+ private PathResolver findResolver(final List<PathResolver> resolvers, final String pattern)
+ {
+ for(final PathResolver pr : resolvers)
+ {
+ if ( pr.getPattern().equals(pattern) )
+ {
+ return pr;
+ }
+ }
+ return null;
}
/**
@@ -98,6 +105,8 @@
// Can be null in case of error-handling servlets...
if ( handler.getServletInfo().getPatterns() != null )
{
+ final List<PathResolver> resolvers = new ArrayList<PathResolver>(this.activeResolvers);
+
final ServletRegistrationStatus status = new ServletRegistrationStatus();
status.handler = handler;
@@ -111,13 +120,13 @@
continue;
}
patterns.add(pattern);
- final PathResolver regHandler = this.activeServletMappings.get(pattern);
+ final PathResolver regHandler = findResolver(resolvers, pattern);
if ( regHandler != null )
{
if ( regHandler.getServletHandler().getServletInfo().compareTo(handler.getServletInfo()) > 0 )
{
// replace if no error with new servlet
- if ( this.tryToActivate(pattern, handler, status) )
+ if ( this.tryToActivate(resolvers, pattern, handler, status, regHandler) )
{
isActive = true;
final String oldName = regHandler.getServletHandler().getName();
@@ -140,7 +149,7 @@
else
{
// add to active
- if ( this.tryToActivate(pattern, handler, status) )
+ if ( this.tryToActivate(resolvers, pattern, handler, status, null) )
{
isActive = true;
}
@@ -151,6 +160,8 @@
{
addToNameMapping(handler);
}
+ Collections.sort(resolvers);
+ this.activeResolvers = resolvers;
}
}
@@ -213,6 +224,8 @@
{
if ( info.getPatterns() != null )
{
+ final List<PathResolver> resolvers = new ArrayList<PathResolver>(this.activeResolvers);
+
this.statusMapping.remove(info);
ServletHandler cleanupHandler = null;
@@ -225,7 +238,7 @@
continue;
}
patterns.add(pattern);
- final PathResolver regHandler = this.activeServletMappings.get(pattern);
+ final PathResolver regHandler = this.findResolver(resolvers, pattern);
if ( regHandler != null && regHandler.getServletHandler().getServletInfo().equals(info) )
{
cleanupHandler = regHandler.getServletHandler();
@@ -234,7 +247,7 @@
final List<ServletHandler> inactiveList = this.inactiveServletMappings.get(pattern);
if ( inactiveList == null )
{
- this.activeServletMappings.remove(pattern);
+ resolvers.remove(regHandler);
}
else
{
@@ -243,7 +256,7 @@
{
final ServletHandler h = inactiveList.remove(0);
boolean activate = h.getServlet() == null;
- done = this.tryToActivate(pattern, h, this.statusMapping.get(h.getServletInfo()));
+ done = this.tryToActivate(resolvers, pattern, h, this.statusMapping.get(h.getServletInfo()), regHandler);
if ( !done )
{
done = inactiveList.isEmpty();
@@ -286,6 +299,9 @@
}
}
+ Collections.sort(resolvers);
+ this.activeResolvers = resolvers;
+
if ( cleanupHandler != null )
{
cleanupHandler.dispose();
@@ -295,7 +311,7 @@
public synchronized void cleanup()
{
- this.activeServletMappings.clear();
+ this.activeResolvers = Collections.emptyList();
this.inactiveServletMappings.clear();
this.servletsByName.clear();
this.statusMapping.clear();
@@ -314,14 +330,22 @@
status.pathToStatus.put(pattern, DTOConstants.FAILURE_REASON_SHADOWED_BY_OTHER_SERVICE);
}
- private boolean tryToActivate(final String pattern, final ServletHandler handler, final ServletRegistrationStatus status)
+ private boolean tryToActivate(final List<PathResolver> resolvers,
+ final String pattern,
+ final ServletHandler handler,
+ final ServletRegistrationStatus status,
+ final PathResolver oldResolver)
{
// add to active
final int result = handler.init();
if ( result == -1 )
{
- final PathResolver reg = PathResolverFactory.createPatternMatcher(handler, pattern);
- this.activeServletMappings.put(pattern, reg);
+ if ( oldResolver != null )
+ {
+ resolvers.remove(oldResolver);
+ }
+ final PathResolver resolver = PathResolverFactory.createPatternMatcher(handler, pattern);
+ resolvers.add(resolver);
// add ok
status.pathToStatus.put(pattern, result);