Project

Profile

Help

Bug #5800

closed

Saxon-HE:9.5.5 and higher causes NullpointerException in GatherUsePackageDeclaration method when using xsl:include and applying custom URIResolver

Added by Jan Strakos almost 2 years ago. Updated almost 2 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
Resolvers
Sprint/Milestone:
-
Start date:
2023-01-06
Due date:
% Done:

100%

Estimated time:
Legacy ID:
Applies to branch:
10, 11, 9.5, 9.6, 9.7, 9.8, 9.9
Fix Committed on Branch:
10, 11, trunk
Fixed in Maintenance Release:
Platforms:
Java

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

Also available in: Atom PDF