[PAX WEB] Fail to register Servlet at the Context Path Root Url Pattern

Skip to first unread message

Miroslav Beranič

unread,
Feb 8, 2020, 7:13:32 AM2/8/20
to op...@googlegroups.com
Hi all,

as an example of usage, presented by published sample at:

If one would like to service HTTP request at the root of the context path /bar, it is not possible to do so.

If one registers Servlet with initial parameter: ExtenderConstants.PROPERTY_URL_PATTERNS set to "", it will get marked as not-valid ( valid == false ) inside constructor of class:
org.ops4j.pax.web.extender.whiteboard.internal.element.ServletWebElement

this is because urlpattern when equals to "" is not picked up inside code:
org.ops4j.pax.web.extender.whiteboard.internal.tracker.ServletTracker#createWebElement

where it looks like this:
String[] urlPatterns = null;
if (urlPatternsProp != null) {
if (urlPatternsProp instanceof String && ((String) urlPatternsProp).trim().length() != 0) {
urlPatterns = new String[] { (String) urlPatternsProp };
} else if (urlPatternsProp instanceof String[]) {
urlPatterns = (String[]) urlPatternsProp;
}
}

so if urlPattern == "", it fails on condition: ((String) urlPatternsProp).trim().length() != 0 ; 
and urlPatterns is not set to "", but is set to null, because of this ServletWebElement marks it not-valid and gets rejected later on.

If one sets urlPattern to "/" it works. Servlet will serve the requested path, for example "/bar/", but will also serve back "/bar/invalid/path" also. Why? This is because when Request mapping is created - during the Servlet registration, Jetty code is called, that is in class:

org.eclipse.jetty.http.pathmap.ServletPathSpec#ServletPathSpec where it will check and set according:
else if ("/".equals(servletPathSpec)) {
   super.pathSpec = "/";
   super.pathDepth = -1;
   this.specLength = 1;
   this.group = PathSpecGroup.DEFAULT;
}

Because this.group = PathSpecGroup.DEFAULT, it will serve any URL later on. So this is not was it was requested. If it would be requested, user would/should register urlPattern like "/*", or register with alias ( that will auto-generate wild char pattern ).

To solve this, 
org.ops4j.pax.web.extender.whiteboard.internal.tracker.ServletTracker#createWebElement should be changed to not check for null or empty ( remove && ((String) urlPatternsProp).trim().length() != 0 ):

String[] urlPatterns = null;
if (urlPatternsProp != null) {
if (urlPatternsProp instanceof String) {
urlPatterns = new String[] { (String) urlPatternsProp };
} else if (urlPatternsProp instanceof String[]) {
urlPatterns = (String[]) urlPatternsProp;
}
}

This will pass on the "" url pattern to the ServletModel instance that is created. Additionally there is one more check/validation/normalization that not inline with this.

This is in class:
org.ops4j.pax.web.service.spi.model.ServletModel#ServletModel(org.ops4j.pax.web.service.spi.model.ContextModel, java.lang.Class<? extends javax.servlet.Servlet>, javax.servlet.Servlet, java.lang.String, java.lang.String[], java.lang.String, java.util.Dictionary<java.lang.String,?>, java.lang.Integer, java.lang.Boolean, javax.servlet.MultipartConfigElement)

Where it says:
this.urlPatterns = Path.normalizePatterns(urlPatterns);

That calls this:
org.ops4j.pax.web.service.spi.util.Path#normalizePattern
if (pattern == null || "".equals(pattern.trim())) {
return "/";
}

and this code will bring back "/" url pattern, that will make Jetty think this is "default" used for all servlets. If this check is removed altogether it will pass on the "" url pattern to the ServerModel.

This will in turn get created as it should. With this changes I've not noticed any side-effects to existing/other examples and also make Jetty serve only exact url located at /bar/, this also works if context path is empty, for example: / , full url: http://localhost:8181/, http://localhost:8181/bar/

If you think this would be useful, I would be glad to share my code as a pull request.

Kind Regards,
Miroslav



--

Grzegorz Grzybek

unread,
Feb 8, 2020, 8:22:58 AM2/8/20
to op...@googlegroups.com
Hello!

Nice analysis! The problem with URL Mapping is not trivial and Pax Web has to do many tricks to do it right (though I believe it still doesn't do it so...)

I've just recently created this issue: https://github.com/eclipse-ee4j/servlet-api/issues/300 where I check different mappings according to servlet API specification. OSGi Http Service mapping (and Whiteboard mapping) is based on Servlet API, with few clarifications (or rather making it a bit more confusing).

Also, the concept of "alias" is misleading - it HAS to be treated also as name sometimes. And in case of resources, special "name" == "default" is converted by Pax Web to "default" servlet... I'd be more than happy trying to provide integration tests showing all these mappings for all the containers in Pax Web. I'm quite progressed now with the review of Pax Web 8 and I believe I've managed to clarify (in Javadocs) what are the differences between:
 - name
 - alias
 - URL mapping
 - context path
 - HttpContext / ServletContextHelper
 - javax.servlet.ServletContext
 - default servlet from container
 - default (404) servlet
 - default resource servlet

In each change I focus on consistency between the above.

regards
Grzegorz Grzybek

--
--
------------------
OPS4J - http://www.ops4j.org - op...@googlegroups.com

---
You received this message because you are subscribed to the Google Groups "OPS4J" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ops4j+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ops4j/CA%2B3Fds5nT%2BruKm1abvgAikhhwhk1cmmmyBQvVi7eTymUXUKSmQ%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages