flyingsaucerproject / flyingsaucer

XML/XHTML and CSS 2.1 renderer in pure Java
Other
1.96k stars 549 forks source link

Use URLStreamHandler for classpath protocol if available #250

Closed pbrant closed 6 months ago

pbrant commented 6 months ago

No handler for the classpath protocol will be available unless the JRE is configured to make one available.

See e.g. https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#URL-java.lang.String-java.lang.String-int-java.lang.String- for more information.

Based on Andrzej's description, Spring Boot (or maybe Spring itself) provides a handler for the classpath protocol.

This change provides backwards compatibility for those environments (Spring or otherwise) while falling back to the new implementation.

As an aside, I strongly suspect the Spring implementation is more defensive than the FS code. The context class loader cannot always be relied upon to return something useful (e.g. it could be null or set to something unhelpful). The FS code should be useful in many situations though.

asolntsev commented 6 months ago

@pbrant Thank you for the explanation. Now I understand why URLStreamHandler might work. But I still don't understand why searching of this file in classpath didn't work. It should always work if the file is located in classpath.

asolntsev commented 6 months ago

@pbrant Juhuu, I managed to reproduce the issue in a Spring project. Given a Spring boot project

It works with all FS versions: 9.3.0, 9.4.0, 9.4.1. The generated PDF contains the image.

NB! The above mentioned error happens ONLY if I add a leading slash to image url: <img src="classpath:/templates/selenide-logo-big.png"/>. Such URL fails with FS 9.4.0.

But I think it's just an invalid url. Users just need to remove the leading slash.

pbrant commented 6 months ago

Hey, nice detective work!

My reading of RFC 3986 is that both variants are perfectly legal URIs.

In any case, the larger point is that, as a general rule, we should be quite tentative about breaking existing, working input (even if I'm sure it wasn't intentional in this case).

The new FS code is definitely not a drop-in replacement for the equivalent Spring code in more ways that just path handling. For a trivial example, take a look at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/ClassUtils.html#getDefaultClassLoader() which handles the case when the context class loader is null, but it wouldn't surprise me if there are others.