Bug #5800
closedSaxon-HE:9.5.5 and higher causes NullpointerException in GatherUsePackageDeclaration method when using xsl:include and applying custom URIResolver
100%
Description
Saxon-HE:9.5.5 and higher causes NullpointerException in GatherUsePackageDeclaration method when using xsl:include and applying custom URIResolver
Hello, first of all I would like to thank you for the great work your team is doing.
I am facing the following problem, which occurs from version Saxon-HE:9.5.5 and above. The last stable version is Saxon-HE:9.5.1-4. I am currently using the latest version of saxon 11.4. I was able to isolate and repeat the problem. I am convinced that the error is due to our custom URI resolver, but I am not clear why. I also know the code line that, when commented out, makes everything work, but I don't know why.
This is the exception:
java.lang.NullPointerException at net.sf.saxon.style.PrincipalStylesheetModule.gatherUsePackageDeclarations(PrincipalStylesheetModule.java:522) at net.sf.saxon.style.PrincipalStylesheetModule.gatherUsePackageDeclarations(PrincipalStylesheetModule.java:523) at net.sf.saxon.style.PrincipalStylesheetModule.spliceUsePackages(PrincipalStylesheetModule.java:487) at net.sf.saxon.style.PrincipalStylesheetModule.preprocess(PrincipalStylesheetModule.java:330) at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:290) at net.sf.saxon.style.StylesheetModule.loadStylesheet(StylesheetModule.java:249) at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:113) at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:936) at net.sf.saxon.jaxp.SaxonTransformerFactory.newTemplates(SaxonTransformerFactory.java:155) at net.sf.saxon.jaxp.SaxonTransformerFactory.newTransformer(SaxonTransformerFactory.java:112) at com.netledger.dcs.NLComponentXSLTransformationTest.tempTest(NLComponentXSLTransformationTest.java:180) at com.netledger.test.NLTestBaseRunner$NLFailOnTimeout$1.run(NLTestBaseRunner.java:458)
This is the test:
final TransformerFactory transformerFactory = TransformerFactory.newInstance();
File base = new File("/webdev/tempTest/oramw/apache"); // Directory, that contains other directories with xslt templates
File[] dependencyLocations = { new File("/webdev/tempTest2/_dist/cmd/shared/rule") }; // Directory, that contains potential directories with xslt templates - its for fallback
transformerFactory.setURIResolver(new RelativePathUriResolver(base, dependencyLocations)); // Setting our custom URI resolver
final Transformer transformer = transformerFactory.newTransformer(new StreamSource("/webdev/tempTest/oramw/apache/conf-templates/create-rota-dedicated-baseline.xslt")); // The transformer is not created and an exception occurs
Somethink about the test data: There is a "main" template, location: "/webdev/tempTest/oramw/apache/conf-templates/create-rota-dedicated-baseline.xslt". This template has an include element: <xsl:include href="./conf-templates/base-defs.xslt"/>. The base-defs.xslt has two include elements <xsl:include href="../../../config/shared/rule/output-content.xslt"/> and <xsl:include href=".../../../config/shared/rule/functions.xslt"/>. For simplycity, thats all. I do not see anything wrong with this.
What have i found: I found out that if I comment out the resolvedSource.setSystemId(resolvedFile.getAbsolutePath()); line in the custom URIResolver (provided down below), suddenly everything works. But if I don't do that and uncomment the row, when method gatherUsePackageDeclarations is called and line TreeInfo includedTree = compilation.getStylesheetModules().get(key); is executed, the problem occurs, because stylesheetModules Hashmap contains three key-value pairs with keys: [/webdev/tempTest/config/shared/rule/output-content.xslt] and [/webdev/tempTest/config/shared/rule/functions.xslt] and [file:/webdev/tempTest/oramw/apache/conf-templates/conf/templates/base-defs.xslt]. DocumentKey, which is used for searching the value is [file:/../config/shared/rule/output-content.xslt] and this caused the NullpointerException. To be honest, i dont know why the systemID has an impact to this, but i am really curious about it... I think it has something to do with the systemID, the calculation of the DocumentKey and using the element
This is our custom URIREsolver:
/**
* Implementation of javax.xml.transform.URIResolver that allows the user to explicitly specify the
* base directory against which relative hrefs are resolved when processing stylesheets, and a list of
* dependency locations to fall back to for resolution.
*
* The href is resolved against the following locations, returning the first match:
* - The location of the file containing the relative href, if available
* - The resolution base, if non-null
* - Each dependency directory, in the order in which they are listed
*/
public class RelativePathUriResolver implements URIResolver
{
private final File resolutionBase;
private final File[] dependencyDirectories;
public RelativePathUriResolver(@Nullable File resolutionBase, File[] dependencyDirectories)
{
this.resolutionBase = resolutionBase;
this.dependencyDirectories = dependencyDirectories;
}
@Override
public Source resolve(String href, String base) throws TransformerException
{
if (Strings.isNullOrEmpty(href))
throw new TransformerException("Passed HREF was empty.");
// When a non-null base is passed to this method, it is typically the path to a file in a resolution chain.
// If this is the case, attempt to resolve against the parent directory of base rather than base itself.
File baseFile = Strings.isNullOrEmpty(base) ? null : new File(base);
if (baseFile != null && baseFile.isFile())
baseFile = baseFile.getParentFile();
List<File> potentialRoots = Lists.asList(baseFile, resolutionBase, dependencyDirectories);
File resolvedFile = findFile(href, potentialRoots);
if (resolvedFile == null)
{
// Some of our config template reference includes using relative paths as though they were in a source tree, e.g.
// <xsl:include href="../../../config/shared/rule/functions.xslt"/>
// These should be updated, but until they are, we will retain the filename-only lookup as a fallback.
resolvedFile = findFile(NLIOUtil.getNameOnlyGivenPath(href), potentialRoots);
}
if (resolvedFile == null)
{
String potentialRootsString = potentialRoots
.stream()
.filter(Objects::nonNull)
.map(File::getAbsolutePath)
.collect(Collectors.joining("\n\t"));
throw new TransformerException(String.format("Unable to resolve HREF [ %s ] for any of the potential base directories:\n"
+ "\t%s\n", href, potentialRootsString));
}
try (InputStream fileStream = new FileInputStream(resolvedFile))
{
Source resolvedSource = new JDOMSource(NLJdomUtils.buildDocument(fileStream));
resolvedSource.setSystemId(resolvedFile.getAbsolutePath()); // IF I COMMENT OUT THIS LINE, EVERYTHING WORKS FINE
return resolvedSource;
}
catch (JDOMException je)
{
throw new TransformerException("An exception occured while converting requested object [ " +
resolvedFile.getAbsolutePath() + " ] for HREF [ " + href + " ] to a Source object.", je);
}
catch (IOException ioe)
{
throw new TransformerException(
String.format("Problem accessing file [ %s ] for HREF [ %s ].", resolvedFile.getAbsolutePath(), href), ioe);
}
}
private File findFile(String href, List<File> potentialRoots)
{
return potentialRoots.stream()
.filter(Objects::nonNull)
.filter(File::isDirectory)
.map(file -> file.toPath().resolve(href).toFile())
.filter(File::isFile)
.findFirst()
.orElse(null);
}
}
Files
Please register to edit this issue