Project

Profile

Help

Saxon 11.1 URI Resolvers

Added by Vladimir Nesterovsky about 2 years ago

Hello,

Can you please direct me on how to migrate use of StandardURIResolver in Saxon 11.1?

We relied on "classpath:" scheme to resolve stylesheets inside jars. Now, it seems URIResolver is replaced with ResourceResolver, which doesn't seem to implement the feature.

Thanks


Replies (12)

Please register to reply

Saxon 11.1 - Added by Norm Tovey-Walsh about 2 years ago

Saxonica Developer Community writes:

Can you please direct me on how to migrate use of StandardURIResolver
in Saxon 11.1?

We relied on "classpath:" scheme to resolve stylesheets inside jars.
Now, it seems URIResolver is replaced with ResourceResolver, which
doesn't seem to implement the feature.

The ResourceResolver should resolve classpath: URIs. Can you provide an
example where it isn’t working?

Be seeing you,
norm

--
Norm Tovey-Walsh
Saxonica

RE: Saxon 11.1 - Added by Vladimir Nesterovsky about 2 years ago

It did not work out of the box, so I need to debug. It might be related to something with my setup.

Thanks

RE: Saxon 11.1 - Added by Norm Tovey-Walsh about 2 years ago

It did not work out of the box, so I need to debug. It might be
related to something with my setup.

I’d certainly like to understand why and help you fix it, so please let
me know what you find and don’t hesitate to ask for help!

Be seeing you,
norm

--
Norm Tovey-Walsh
Saxonica

RE: Saxon 11.1 URI Resolvers - Added by Michael Kay about 2 years ago

More specifically, it's supposed to work like this, though the detail depends on what kind of resource you're trying to resolve. Let's suppose it's a call to document() in XSLT.

  • The code goes first to see if the XsltTransformer has a ResourceResolver. It won't have one unless the user has supplied one. If there isn't one, go to the next step. If it has one, call it. If it returns a Source, use it; if not, go to the next step.

  • Then we go to the ResourceResolver registered at Configuration level. By default this invokes the catalog-based resolver, which also handles classpath and data URIs. But you can set your own resolver at this level to override this.

  • If the Configuration-level ResourceResolver returns null, the final fallback is to a DirectResourceResolver which resolves URIs "natively" e.g. via URL.openConnection().

Complicating this is what happens with legacy APIs that are still supported, such as setURIResolver() on the XsltTransformer or the JAXP TransformerFactory. Generally these are equivalent to a call on setResourceResolver() at the same level, for example setURIResolver() at TransformerFactory results in a call on Configuration.setResourceResolver(), which means the catalog-based resolver (with its classpath and data functionality) isn't invoked. If you want your URIResolver to fall back to that functionality, then either (a) define it at local level, or (b) set the Configuration-level resolver to a composite resolver that first does your local logic, then does the standard logic.

There's a class ChainedResourceResolver that allows you to chain together two (or more) ResourceResolvers in this way. There are also classes like ResourceResolverWrappingURIResolver that might help the transition; and the StandardURIResolver is still present in the product if you want to use it, though its name is now a misnomer.

RE: Saxon 11.1 - Added by Vladimir Nesterovsky about 2 years ago

Ok, I figured out what has happened. In my pipeline I'm using my custom URIResolver. My goal was to "fix" "classpath:" resolution.

Consider two snapshots comparing how it works in Saxon and in custom resolvers:

  @Test
  public void testSaxonResolver()
    throws Exception
  {
    String base = "classpath:my/class/path/";
    String relativeURI = "my-style.xslt";
    URI absoluteURI = null;
   
    // Based on Saxon's ResolveURI.java
    if (base.startsWith("classpath:"))
    {
      absoluteURI = new URI(relativeURI);
      
      if (!absoluteURI.isAbsolute()) 
      {
        absoluteURI = new URI("classpath:" + relativeURI);
      }
    }

    // This produces "classpath:my-style.xslt"
    assertNotNull(absoluteURI);
  }

  @Test
  public void testCustomResolver()
    throws Exception
  {
    String base = "classpath:my/class/path/";
    String relativeURI = "my-style.xslt";
    String absoluteURI = null;
   
    if (base.startsWith("classpath:"))
    {
      URI uri = new URI(relativeURI);
      
      if (!uri.isAbsolute())
      {
        absoluteURI = "classpath:" + 
          new URI(base.substring("classpath:".length())).
          resolve(uri).
          toString();
      }
    }

    // This produces "classpath:my/class/path/my-style.xslt"
    assertNotNull(absoluteURI);
  }

Saxon resolver produced "classpath:my-style.xslt" for "my-style.xslt" against "classpath:my/class/path/". Custom resolver produced "classpath:my/class/path/my-style.xslt" for "my-style.xslt" against "classpath:my/class/path/".

Custom resolver works better for my setup.

It was like this even in the past but I was able to replace resolver. But now I am not sure where should I plug my patch.

So, where may I implement such "fix"?

RE: Saxon 11.1 URI Resolvers - Added by Vladimir Nesterovsky about 2 years ago

Complicating this is what happens with legacy APIs that are still supported, such as setURIResolver() on the XsltTransformer or the JAXP TransformerFactory

I'll try to figure out how to set it.

For some reason code I have always used Configuration and set resolver there. In current version configuration.setURIResolver() does not exists any more.

Thanks.

RE: Saxon 11.1 - Added by Norm Tovey-Walsh about 2 years ago

Saxon resolver produced "classpath:my-style.xslt" for "my-style.xslt"
against "classpath:my/class/path/". Custom resolver produced
"classpath:my/class/path/my-style.xslt" for "my-style.xslt" against
"classpath:my/class/path/".

Custom resolver works better for my setup.

That looks like a bug to me, https://saxonica.plan.io/issues/5271

It was like this even in the past but I was able to replace resolver.
But now I am not sure where should I plug my patch.

So, where may I implement such "fix"?

You can still provide your own resolver.

Be seeing you,
norm

--
Norm Tovey-Walsh
Saxonica

RE: Saxon 11.1 URI Resolvers - Added by Vladimir Nesterovsky about 2 years ago

I've switched off any custom resolvers and made sure that absolute paths are used for "classpath:" protocol, and now I'm certain that it does not work in Saxon 11.1, and that it worked in Saxon 10.x.

I get following error once processor tries to resolve <xsl:include href="classpath:...":

     [java] Error on line 1 column 149 of META-INF:
     [java]   XTSE0165  I/O error reported by XML parser processing
     [java]   classpath:META-INF/stylesheets/postprocess/postprocess.xslt: unknown protocol: classpath.
     [java]   Caused by java.net.MalformedURLException: unknown protocol: classpath
     [java] [1]:javax.xml.transform.TransformerConfigurationException: net.sf.saxon.s9api.SaxonApiException: I/O error reported by XML parser processing classpath:META-INF/stylesheets/postprocess/postprocess.xslt: unknown protocol: classpath
     [java] [1] 	at net.sf.saxon.jaxp.SaxonTransformerFactory.newTemplates(SaxonTransformerFactory.java:187)
     [java] [1] 	... 3 more
     [java] [1] Caused by: net.sf.saxon.s9api.SaxonApiException: I/O error reported by XML parser processing classpath:META-INF/stylesheets/postprocess/postprocess.xslt: unknown protocol: classpath
     [java] [1] 	at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:937)
     [java] [1] 	at net.sf.saxon.jaxp.SaxonTransformerFactory.newTemplates(SaxonTransformerFactory.java:185)
     [java] [1] 	... 3 more
     [java] [1] Caused by: net.sf.saxon.trans.XPathException: I/O error reported by XML parser processing classpath:META-INF/stylesheets/postprocess/postprocess.xslt: unknown protocol: classpath
     [java] [1] 	at net.sf.saxon.resource.ActiveSAXSource.deliver(ActiveSAXSource.java:229)
     [java] [1] 	at net.sf.saxon.event.Sender.send(Sender.java:104)
     [java] [1] 	at net.sf.saxon.style.StylesheetModule.sendStylesheetSource(StylesheetModule.java:152)
     [java] [1] 	at net.sf.saxon.style.StylesheetModule.loadStylesheetModule(StylesheetModule.java:112)
     [java] [1] 	at net.sf.saxon.style.UseWhenFilter.processIncludeImport(UseWhenFilter.java:324)
     [java] [1] 	at net.sf.saxon.style.UseWhenFilter.startElement(UseWhenFilter.java:250)
     [java] [1] 	at net.sf.saxon.event.Stripper.startElement(Stripper.java:105)
     [java] [1] 	at net.sf.saxon.event.CommentStripper.startElement(CommentStripper.java:51)
     [java] [1] 	at net.sf.saxon.event.ProxyReceiver.startElement(ProxyReceiver.java:140)
     [java] [1] 	at net.sf.saxon.event.Valve.startElement(Valve.java:63)
     [java] [1] 	at net.sf.saxon.event.ReceivingContentHandler.startElement(ReceivingContentHandler.java:377)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:518)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(AbstractXMLDocumentParser.java:183)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:351)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2725)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:541)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1224)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
     [java] [1] 	at net.sf.saxon.resource.ActiveSAXSource.deliver(ActiveSAXSource.java:188)
     [java] [1] 	at net.sf.saxon.resource.ActiveStreamSource.deliver(ActiveStreamSource.java:65)
     [java] [1] 	at net.sf.saxon.event.Sender.send(Sender.java:104)
     [java] [1] 	at net.sf.saxon.style.StylesheetModule.sendStylesheetSource(StylesheetModule.java:156)
     [java] [1] 	at net.sf.saxon.style.StylesheetModule.loadStylesheet(StylesheetModule.java:226)
     [java] [1] 	at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:113)
     [java] [1] 	at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:932)
     [java] [1] 	... 4 more
     [java] [1] Caused by: java.net.MalformedURLException: unknown protocol: classpath
     [java] [1] 	at java.base/java.net.URL.<init>(URL.java:679)
     [java] [1] 	at java.base/java.net.URL.<init>(URL.java:568)
     [java] [1] 	at java.base/java.net.URL.<init>(URL.java:515)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:649)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(XMLVersionDetector.java:150)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:860)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1224)
     [java] [1] 	at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
     [java] [1] 	at net.sf.saxon.resource.ActiveSAXSource.deliver(ActiveSAXSource.java:188)
     [java] [1] 	... 33 more

I seems processor assumes java.net.URL should handle the protocol by itself, while java.net.URL does not agree with it. Nor I register any special protocol handlers.

Note: for thing to start to work at all, I have to add maven dependency:

    <dependency>
      <groupId>org.xmlresolver</groupId>
      <artifactId>xmlresolver</artifactId>
      <version>4.1.2</version>
    </dependency>

RE: Saxon 11.1 URI Resolvers - Added by Martin Honnen about 2 years ago

As for the Maven dependency missing, I think Norm has pushed Saxon 11.1.1 to Maven to fix that.

RE: Saxon 11.1 - Added by Norm Tovey-Walsh about 2 years ago

Hello Vladimir,

I’ve been investigating the classpath: issue. I’m not sure I really
understand your test:

String base = "classpath:my/class/path/";
String relativeURI = "my-style.xslt";
URI absoluteURI = null;

if (base.startsWith("classpath:"))

This will always be true.

{
absoluteURI = new URI(relativeURI);

This will always produce “my-style.xslt” as a URI.

if (!absoluteURI.isAbsolute())

That will never be absolute.

{
absoluteURI = new URI("classpath:" + relativeURI);

This will always produce “classpath:my-style.xslt”.

}
}

I rewrote your test as follows:

String base = "classpath:my/class/path/";
String relativeURI = "my-style.xslt";
URI absoluteURI, resolvedURI;

absoluteURI = new URI(base);

Now absoluteURI is “classpath:my/class/path/” as a URI.

resolvedURI = absoluteURI.resolve(relativeURI);

And here we find the heart of the problem. The Java URI class resolves
this incorrectly. :-(

There’s a utility class in XML Resolver that does the right thing, so
maybe I noticed this at one point and worked around it:

resolvedURI = URIUtils.resolve(absoluteURI, relativeURI);

I’ll have to investigate some more.

Saxonica Developer Community writes:

[[PGP Signed Part:Undecided]]

RE: Saxon 11.1 URI Resolvers - Added by Norm Tovey-Walsh about 2 years ago

Saxonica Developer Community writes:

[[PGP Signed Part:Undecided]]

RE: Saxon 11.1 URI Resolvers - Added by Vladimir Nesterovsky about 2 years ago

My test was for exposition only.

What I want to tell is that, with Saxon 11.1, I could not create xslt stored inside class path and refer relatively to other xslt, also stored in class path. In my case I have structure:

  • META-INF/stylesheets/lib1/a.xslt
  • META-INF/stylesheets/lib2/b.xslt

where a.xslt includes relatively b.xslt. In my case xslts might be stored in different jars.

In fact my latest tests show that even absolute "classpath:..." references do not work in latest version, as it expects that there is "classpath:" protocol handler registered.

    (1-12/12)

    Please register to reply