jknack / handlebars.java

Logic-less and semantic Mustache templates with Java
http://jknack.github.io/handlebars.java
Other
1.47k stars 382 forks source link

Template cache should distinguish between templates compiled with different Handlebars settings (helpers etc.) #685

Open pvgoran opened 5 years ago

pvgoran commented 5 years ago

The cache system for templates, as it's implemented now, is subtly broken with certain use cases, when a cache object is shared between different Handlebars objects.

Specifically, a cache can remember a Template compiled by a Handlebars object with settings A (which includes at least helpers, MissingValueResolver, and EscapingStrategy), and then return it from a compile/compileInline call for a Handlebars object with settings B. In this situation, the caller expects the template to work according to settings B, but it will work according to settings A because a previously compiled, cached version is returned.

This problem can be triggered even without sharing a cache object between different Handlebars objects: just compile a template, change the Handlebars settings, and compile the same template again.

The good way of resolving this problem would be to make the caches take into account Handlebars settings when looking up the cached Template, and only deliver the cached Template when the applicable settings are the same (MissingValueResolver is the same, EscapingStrategy is the same, the set of helpers is the same, probably TemplateSource is the same, and maybe some other settings too).

The simple way of resolving this problem would be to disallow sharing of cache objects between different Handlebars objects, bind a cache object to a Handlebars object, and invalidate the cache whenever applicable settings of the Handlebars object are changed.

pvgoran commented 5 years ago

Here is my use case: I keep a single, shared ConcurrentMapTemplateCache instance to cache the templates, and a factory method that creates a Handlebars object and configures it to use that single cache instance.

When I need to compile and apply a template, I call the factory method to acquire a new Handlebars object, configure it with a MissingValueResolver and some helpers, then compile and apply the template. MissingValueResolver and the helpers may be different for different template invocations (even if the template text is the same), which triggers the problem.