[xsl] RE: URI Resolution for document('') Reference Is Broken?

Subject: [xsl] RE: URI Resolution for document('') Reference Is Broken?
From: "Roger L. Cauvin" <roger@xxxxxxxxxx>
Date: Wed, 6 Jan 2010 18:08:27 -0600
Michael,

To what value should I set the system ID?

The document('') resolution still fails if I set the SystemId to the
directory of the stylesheet in the initial invocation:

      TransformerFactory transformerFactory =
TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl",
Thread.currentThread().getContextClassLoader());
      //TransformerFactory transformerFactory =
TransformerFactory.newInstance();

      URIResolver uriResolver = new ServletContextURIResolver(context);
      transformerFactory.setURIResolver(uriResolver);

      InputStream xslInputStream =
context.getResourceAsStream("/data/GetMonthLengths.xsl");
      Source xslSource = new StreamSource(xslInputStream);
      String realPath = context.getRealPath("/data");
      xslSource.setSystemId(realPath);

      Transformer transformer =
transformerFactory.newTransformer(xslSource);
      transformer.setURIResolver(uriResolver);

      InputStream xmlStream =
context.getResourceAsStream("/data/Months.xml");
      Reader xmlReader = new InputStreamReader(xmlStream);
      Source xmlSource = new StreamSource(xmlReader);
      StringWriter outputWriter = new StringWriter();
      Result output = new StreamResult(outputWriter);
      transformer.transform(xmlSource, output);
      outputWriter.flush();

It even fails if I also set the SystemID on the Source object returned by my
URIResolver:

  public Source resolve(
    String href,
    String basePath)
    {
    Source source = null;

    System.out.println("href: " + href);
    System.out.println("basePath: " + basePath);

    if (href != null)
      {
      boolean isEmptyHRef = href.equals("");
      if (!isEmptyHRef)
        {
        ServletContext servletContext = getServletContext();
        InputStream inputStream = servletContext.getResourceAsStream(href);
        if (inputStream != null)
          {
          source = new StreamSource(inputStream);

          String realPath = servletContext.getRealPath("/data");
          source.setSystemId(realPath);
          }
        }
      }

    System.out.println("source: " + source);

    return source;
    }

The output is:

href: /data/date.get-days-in-month.template.xsl
basePath: file:///C:/Morandum/source/http/servlet/war/data
source: javax.xml.transform.stream.StreamSource@c01e99
href:
basePath: file:///C:/Morandum/source/http/servlet/war/data
source: null
Error on line 1 column 1 of data:
  SXXP0003: Error reported by XML parser: Content is not allowed in prolog.
outputText: <month-lengths>
Recoverable error
   <month-length/>
  FODC0002: org.xml.sax.SAXParseException: Content is not allowed in prolog.
Recoverable error
  FODC0005: Document has been marked not available:
file:///C:/Morandum/source/http/servlet/war/data
   <month-length/>
Recoverable error
   <month-length/>
  FODC0005: Document has been marked not available:
file:///C:/Morandum/source/http/servlet/war/data
</month-lengths>

Roger

-----Original Message-----
From: "Michael Kay"
Sent: 1/6/2010 8:43:00 PM
To: xsl-list@xxxx
Subject: [xsl] URI Resolution for document('') Reference Is Broken?

>
> Thanks for the prompt response. Here is the servlet code that
> sets up and invokes the transformation:
>
>       Source xslSource = new StreamSource(xslInputStream);
>

Just as I guessed, you are not setting the systemID on the Source object. So
its base URI is unknown. document('') retrieves the resource whose URI is
the base URI of the element containing the call to document(''), which in
this case is unknown.

Regards,

Michael Kay
http://www.saxonica.com/
http://twitter.com/michaelhkay

-----Original Message-----
From: Roger L. Cauvin [mailto:roger@xxxxxxxxxx]
Sent: Wednesday, January 06, 2010 3:38 PM
To: 'xsl-list@xxxxxxxxxxxxxxxxxxxxxx'
Subject: RE: URI Resolution for document('') Reference Is Broken?

Michael,

Thanks for the prompt response. Here is the servlet code that sets up and
invokes the transformation:

  public void doGet(
    HttpServletRequest request,
    HttpServletResponse response) throws ServletException, IOException
    {
    try
      {
      HttpSession session = request.getSession();
      ServletContext context = session.getServletContext();

      TransformerFactory transformerFactory =
TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl",
Thread.currentThread().getContextClassLoader());
      //TransformerFactory transformerFactory =
TransformerFactory.newInstance();

      URIResolver uriResolver = new ServletContextURIResolver(context);
      transformerFactory.setURIResolver(uriResolver);

      InputStream xslInputStream =
context.getResourceAsStream("/data/GetMonthLengths.xsl");
      Source xslSource = new StreamSource(xslInputStream);

      Transformer transformer =
transformerFactory.newTransformer(xslSource);
      transformer.setURIResolver(uriResolver);

      InputStream xmlStream =
context.getResourceAsStream("/data/Months.xml");
      Reader xmlReader = new InputStreamReader(xmlStream);
      Source xmlSource = new StreamSource(xmlReader);
      StringWriter outputWriter = new StringWriter();
      Result output= new StreamResult(outputWriter);
      transformer.transform(xmlSource, output);
      outputWriter.flush();

      String outputText = outputWriter.toString();
      System.out.println("outputText: " + outputText);
      }
    catch (TransformerConfigurationException tce)
      {
      tce.printStackTrace();
      throw new ServletException(tce);
      }
    catch (TransformerException te)
      {
      te.printStackTrace();
      throw new ServletException(te);
      }
    }

As you can see, I did not set the SystemID property.  Do I need to do so?
If so, to what should I set it?

Roger

-----Original Message-----
From: "Michael Kay"
Sent: 1/6/2010 8:43:00 PM
To: xsl-list@xxxx
Subject: [xsl] URI Resolution for document('') Reference Is Broken?

It seems likely that the Base URI of the stylesheet isn't known, or has been
supplied incorrectly.

You don't show how you set it up. Generally it will be the SystemID property
of the JAXP Source object containing the stylesheet.

Regards,

Michael Kay
http://www.saxonica.com/
http://twitter.com/michaelhkay

-----Original Message-----
From: Roger L. Cauvin [mailto:roger@xxxx]
Sent: 06 January 2010 19:31
To: xsl-list@xxxx
Subject: [xsl] URI Resolution for document('') Reference Is Broken?

I have web application (servlet) that performs XSLT transformations and uses
a URIResolver to help resolve paths.

The web applications directory structure is as follows:

war
  data
  META-INF
  WEB-INF

The 'data' directory contains the following files:

Months.xml
GetMonthLengths.xsl
date.get-days-in-month.template

Months.xml contains:

<?xml version="1.0"?>
<months>
  <month>1</month>
  <month>2</month>
  <month>3</month>
</months>

GetMonthLengths.xsl contains:

<?xml version="1.0"?>

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:date="http://exslt.org/dates-and-times";
extension-element-prefixes="date">
  <xsl:import href="/data/date.get-days-in-month.template.xsl"/>
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <xsl:template match="/">
    <xsl:element name="month-lengths">
      <xsl:apply-templates select="months/month"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="month">
    <xsl:element name="month-length">
      <xsl:call-template name="date:get-days-in-month">
        <xsl:with-param name="month" select="."/>
      </xsl:call-template>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

date.get-days-in-month.template contains:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl=http://www.w3.org/1999/XSL/Transform
                xmlns:date=http://exslt.org/dates-and-times
                extension-element-prefixes="date">

<date:month-lengths>
   <date:month>31</date:month>
   <date:month>28</date:month>
   <date:month>31</date:month>
   <date:month>30</date:month>
   <date:month>31</date:month>
   <date:month>30</date:month>
   <date:month>31</date:month>
   <date:month>31</date:month>
   <date:month>30</date:month>
   <date:month>31</date:month>
   <date:month>30</date:month>
   <date:month>31</date:month>
</date:month-lengths>

<xsl:template name="date:get-days-in-month">
   <xsl:param name="month"/>
   <xsl:variable name="month-days"
select="document('')/*/date:month-lengths/date:month" />
   <xsl:value-of select="$month-days[number($month)]" />
</xsl:template>

</xsl:stylesheet>

My URIResolver is as follows:

  public Source resolve(
    String href,
    String basePath)
    {
    Source source = null;

    System.out.println("href: " + href);
    System.out.println("basePath: " + basePath);

    if (href != null)
      {
      boolean isEmptyHRef = href.equals("");
      if (!isEmptyHRef)
        {
        ServletContext servletContext = getServletContext();
        InputStream inputStream = servletContext.getResourceAsStream(href);
        if (inputStream != null)
          source = new StreamSource(inputStream);
        }
      }

    System.out.println("source: " + source);

    return source;
    }

The output is:

href: /data/date.get-days-in-month.template.xsl
basePath:
source: javax.xml.transform.stream.StreamSource@1d439fe
href:
basePath:
source: null
Error on line 1 column 1 of file:/c:/morandum/source/http/servlet/war/:
  SXXP0003: Error reported by XML parser: Content is not allowed in prolog.
outputText: <month-lengths>
   <month-length/>
   <month-length/>
Recoverable error
   <month-length/>
  FODC0002: org.xml.sax.SAXParseException: Content is not allowed in prolog.
</month-lengths>
Recoverable error
  FODC0005: Document has been marked not available:
Recoverable error
  FODC0005: Document has been marked not available:

As you can see, URI resolution works fine for resolving <xsl:import
href="/data/date.get-days-in-month.template.xsl"/>).  But it does NOT work
properly (or according to spec, as far as I can tell) for resolving
<xsl:variable name="month-days"
select="document('')/*/date:month-lengths/date:month" />.

This problem occurs whether I use Resin or Google App Engine as my web
server, and whether I use Saxon 9.2 or the default XML parser for
transformation.  Things work flawlessly if I run the transformation using an
ordinary Java program from the command line (without a URIResolver).

My understanding is that, if the URIResolver returns null, resolution will
be handled in the default manner.  And the default manner for document()
is to refer to the current document.  It is not doing so.

Who is to blame here?  The servlet containers, the XML parsers, or me?

--
Roger L. Cauvin
@rcauvin (Twitter)
cauvin.blogspot.com (blog)

Current Thread