Details
-
Bug
-
Status: Closed
-
Normal
-
Resolution: Fixed
-
5.2.0
-
None
-
None
Description
FreeMarker's template loading invokes TemplateLoader#findTemplateSource(String) before trying to invoke TemplateLoader#getReader(Object, String).
Implementation on #findTemplateSource() method must not throw an IOException if the source for the template doesn't exist.
The reason why it must not throw IOException when the template source is not found, is basically because there are options in FreeMarker to ignoreMissing. See JavaDocs in Configuration#getTemplate(...) or Environment#getTemplate(...).
Especially, Environment#getTemplateForInclusion(...) explicitly uses true value in ignoreMissing parameter.
Now the problem in HST code is as follows:
- In both JcrTemplateLoader#findTemplateSource() and WebFileTemplateLoader#findTemplateSource(), at the moment, it always returns a kind of abstract RepositorySource instance through RepositorySource.notFound(absPath) if the node doesn't exist. See org.hippoecm.hst.freemarker.jcr.TemplateLoadingCache#getRepositoryTemplate(String).
- As a result, when Freemarker finally invokes TemplateLoader#getTemplate(), that org.hippoecm.hst.freemarker.jcr.AbstractTemplateLoader.getReader(Object, String) has to throw an IOException as the kind of abstract RepositorySource represents a not-found source.
This breaks the contact. It should have returned null in #findTemplateSource() if the node turned out to be non-existing at the path.
IOException should have thrown only when there's any javax.jcr.Repository exception for example while trying to read a node instead.
If a developer uses <#include > directive [1] with ignore_missing option, the whole template execution will be just blown away, instead of ignoring that missing included template.
Example stack trace:
Caused by: java.io.IOException: Repository templateSource '/webfiles/site/freemarker/myhippoproject/pagenotfound-main2.ftl' not found at org.hippoecm.hst.freemarker.jcr.AbstractTemplateLoader.getReader(AbstractTemplateLoader.java:54) ~[hst-client-5.2.0.jar:5.2.0] at freemarker.cache.MultiTemplateLoader$MultiSource.getReader(MultiTemplateLoader.java:142) ~[freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] at freemarker.cache.MultiTemplateLoader.getReader(MultiTemplateLoader.java:102) ~[freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] at freemarker.cache.TemplateCache.loadTemplate(TemplateCache.java:547) ~[freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] at freemarker.cache.TemplateCache.getTemplateInternal(TemplateCache.java:439) ~[freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] at freemarker.cache.TemplateCache.getTemplate(TemplateCache.java:292) ~[freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] at freemarker.template.Configuration.getTemplate(Configuration.java:2720) ~[freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] at freemarker.core.Environment.getTemplateForInclusion(Environment.java:2540) [freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] at freemarker.core.GetOptionalTemplateMethod.exec(GetOptionalTemplateMethod.java:138) ~[freemarker-gae-2.3.28-incubating-SNAPSHOT.jar:2.3.27.97] ... 110 more
If not found, it shouldn't be IOException.
In summary,
- Returning a non-null object from #findTemplateSource() simply violates what the API says:
(ref: https://freemarker.apache.org/docs/api/freemarker/cache/TemplateLoader.html#findTemplateSource-java.lang.String- )
Both for the return value and the throw exception it's explicitly noted what to do if the template is missing. - By the way, #getReader() must not return null in any case unless there's an system exception like IOException. See the latest the JavaDoc API as well. Returning null will just cause NPE in FreeMarker.
- Only findTemplate should return null on missing template.
[1] https://freemarker.apache.org/docs/ref_directive_include.html