Hi Joe,
Thanks for detailling the issues in this class. This URI resolver
lacks tests indeed.
Could you provide a SVN patch that would fix those issues? I would
like that to be fixed before releasing the 1.0.6 version next week.
Also, if you have time to provide a test case, we could add it to the
build process :)
Best regards,
Jerome
2007/10/24, Joe Nellis <[EMAIL PROTECTED]>:
> Greetings again,
>
> There is a bug in the TransformRepresentation.getTransformer() method
> involving the wrong setURIResolver method being called. The method in
> question:
>
> public Transformer getTransformer() throws IOException {
> if (this.transformer == null) {
> try {
> // Prepare the XSLT transformer documents
> StreamSource transformSheet = new StreamSource(
> getTransformSheet().getStream());
>
> // Create a new transformer as they are not thread safe
> this.transformer = TransformerFactory.newInstance()
> .newTransformer(transformSheet);
>
> // Set the URI resolver
> transformer.setURIResolver(getURIResolver());
> } catch (TransformerConfigurationException tce) {
> throw new IOException("Transformer configuration exception.
> "
> + tce.getMessage());
> } catch (TransformerFactoryConfigurationError tfce) {
> throw new IOException(
> "Transformer factory configuration exception. "
> + tfce.getMessage());
> }
> }
>
> return this.transformer;
> }
>
> The this.transformer assignment calls for an instance of the
> TransformerFactory, then calls for a new Transformer handing it the
> transformsheet in the process. At this point in execution, compiling of the
> transform sheet is done and any <xsl:include>,<xsl:import> or document()
> functions will use the default URIResolver. But in this case, the
> URIResolver that is intended to be used is set AFTER the stylesheet is
> compiled, which is after include/import tags need to be resolved.
>
> So what is being used as a URIResolver? Null is the value, and the XSLT
> spec says that
> if it's null then the base that the XSLT implementers should use is the
> address of the current stylesheet as the base reference. In some contexts
> of the Transformer, the stylesheet is passed as a file object so the
> transformer implementation has a chance to resolve include/imports when the
> URIResolver is null. In the TransformRepresentation case, it only sees a
> stream so there is a different default base address, which is usually the
> working address of the program that is creating Transformer objects. In the
> case of using the ServerServlet, for the most part this base address is
> going to be the application container address.. In the netbeans bundled
> tomcat it resolves to the tomcat bin folder. With Resin it resolves
> somewhat more correctly to the webapps/application root directory.
>
> The next problem is that Transformer.setURIResolver is for setting the
> resolver for ONLY document() function calls in a stylesheet, which are
> dynamic and resolved at runtime. Include/import tags are static and
> resolved at compile time. The TransformerFactory.setURIResolver is the
> correct method, it sets the resolver for include/import and document()
> function. So the modified code would look like this:
>
> @Override
> public Transformer getTransformer() throws IOException {
> /* There is no way to set the transformer in this override
> * since their is no protected setTransformer method.
> * A new Transformer is created for each call. */
> Transformer result = null;
> try {
> // Prepare the XSLT transformer documents
> StreamSource transformSheet = new StreamSource(
> getTransformSheet().getStream());
>
> // Get a transformer factory.
> TransformerFactory tfactory = TransformerFactory.newInstance();
> // Set the URI resolver before compiling the stylesheet
> // since all it ever sees is a stream, not a file/path object.
> tfactory.setURIResolver(getURIResolver());
> // Create a new transformer as they are not thread safe
> result = tfactory.newTransformer(transformSheet);
>
> } catch (TransformerConfigurationException tce) {
> throw new IOException("Transformer configuration exception. "
> + tce.getMessage());
> } catch (TransformerFactoryConfigurationError tfce) {
> throw new IOException(
> "Transformer factory configuration exception. "
> + tfce.getMessage());
> }
> return result;
> }
>
> Now the behavior actually invokes the default URIResolver for
> TransformRepresentation, which is the embedded member class ContextResolver.
> Line 184-185 there seems like an error in forgetting to resolve the base
> address:
>
> Response response = this.context.getDispatcher().get(
> targetRef.toString());
> should be:
> Response response = this.context.getDispatcher().get(
> targetRef.getTargetRef().toString());
>
> Otherwise the base address is never resolved. This base address String
> value that is passed into the URIResolver is, I think, exclusively the
> explicitly called base value that is encountered in xslt tags that contain
> xml:base="path to base" attributes. I'm not positive on that. This is about
> where I am right now. I think more test cases are needed for the
> ContextResolver class maybe. Some transformer implementations may pass the
> base parameter to the resolver as null or as an empty string. Right now
> there is no check for this but I would think they both mean the same thing.
> I'm currently unable to get line 184-185 to return a response with a
> non-null entity even with a valid reference anyway, so I'm still digging
> around.
>
> Right now I suggest using a custom URIResolver when you need
> includes/imports in your xslt files or you are running under the
> ServerServlet in a app container. Unfortunately, there is no
> TransformRepresentation.setURIResolver as it is locked into it's custom
> ContextResolver during construction. So until this is remedied as well, you
> need to subclass TransformRepresentation anyway and override the entire
> getTransformer() method to accept your custom resolver.
>
> Joe.
>
>