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
--