Data Management Blog: URIResolver with XSLT2 using Saxon on Tomcat and JSTL

 

Data Management Blog
Data management, Cloud, Transformation and anything else...









URIResolver with XSLT2 using Saxon on Tomcat and JSTL

Ran into a rather maddening problem last week.  I was working on a front end to a tool and was planning on using JSP within a Tomcat environment.  I'd downloaded the latest Tomcat (8.0.9 to be exact).  It installed ok.  Well most of my app is xml based and I needed to use XSLT 2.  So grabbed Saxon 9 (9.1.0.2J - later tried 9.5.1.7J and same behaviour), and added to my lib directory and with an environment variable update, presto - I was able to perform transformations.  (Just needed a property set in the JSP)
<%
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
%>
So far a happy story right?  The issue came up around relative and absolute paths.  The collection() function was throwing errors if I tried to use a relative path.  It was annoying but not the end of the world as I could supply the full path behind the scenes.  Maddeningly, the doc() function was throwing an error if I used an absolute path.  So I had 2 functions that were each doing their own thing at different places in the tool.  But one required full path and one relative.  No exceptions.  I could work around this but it seemed silly to have to do this.

I wasn't sure if the problem was my code, java, tomcat, or saxon (can you guess which?).  I found that it wasn't anything to do with encoding, so I could rule that out.  I started doing research and found some interesting (though dated) discussions here , here , here .  The issue was apparently around the URIResolver. Potential work arounds/solutions here , here , here , here .  

I also did some document-uri(.) functions on loaded documents.  It reflected this problem, as it was returning a path that started with "jstl:/../" instead of  "file:///c:/" or even "http://localhost".  So the resolver was definitely the problem.

Just as I was about to contemplate writing a custon URIResolver, I did some more digging in my JSTL tagging.  And it hit me that I might have outsmarted myself.  Turns out that @xsltSystemId not only provides the path for the XSLT, but also serves as the basis for all relative URLs used in the XSLT. So things like imports, doc(), collection(), etc.  They all are based on that.  So my solution was a humbling, simple attribute on my JSTL:

<x:transform xml="${thexml}" xslt="${xslt}"
    xsltSystemId="${basepath}select-schema.xslt"/>

****postscript****

Here is more info on the errors I'd gotten:

When a known and correct relative path was used, the collection() function resulted in this error (snipped for brevity):
HTTP Status 500 - javax.servlet.ServletException: javax.servlet.jsp.JspException: net.sf.saxon.trans.XPathException: Cannot resolve relative URI: Invalid base URI: Expected scheme-specific part at index 5: jstl:: jstl:
Meanwhile when the full path is given, and the collection() function works correctly, later on in the process, the same full path in doc() function returns this error (also snipped):
HTTP Status 500 - java.lang.IllegalArgumentException: Expected scheme-specific part at index 5: jstl:
 

java.lang.IllegalArgumentException: Expected scheme-specific part at index 5: jstl:
    java.net.URI.create(Unknown Source)
    java.net.URI.resolve(Unknown Source)
    net.sf.saxon.functions.ResolveURI.tryToExpand(ResolveURI.java:115)
    net.sf.saxon.StandardURIResolver.resolve(StandardURIResolver.java:165)
    




© Copyright Paul Kiel.