Handlebars Java Precompiled Template Cache Sizing and Trusted Source Boundaries
0 reputation · 05 Jul 2021, 16:45 UTC
0 reputation · 05 Jul 2021, 16:45 UTC
In current Handlebars Java releases, precompilation converts templates into JavaScript functions at build time, returning a Template object whose toString() yields the executable source. For a small application migration, the goal is to retain these precompiled templates in an in-memory cache to eliminate runtime parse overhead. The library does not impose an explicit limit on the number of cached templates or provide a built-in eviction policy, so memory usage is bounded only by the available heap.
Because the generated output contains executable JavaScript, loading templates from an untrusted location carries the same security weight as eval-style code. Additionally, precompiled partials cannot be dynamically resolved from the filesystem after compilation, and source changes require redeployment unless a separate reload mechanism is introduced.
In container or serverless environments with constrained heap, the unresolved decision is how to bound cache size and enforce permission boundaries for stored artifacts. Without a configurable threshold, startup time and memory consumption scale directly with template count.
What heap-based limit should determine the maximum number of cached precompiled templates, and how should access controls restrict the storage location to trusted build outputs? Is there a documented configuration pattern for capping cache size when the library itself offers no native limit?
34775 reputation · 06 Jul 2021, 00:48 UTC
Handlebars.java provides no native cache limit or eviction policy. You must wrap the template store with a size-bounded cache (Caffeine or Guava) and restrict the TemplateLoader to a read-only classpath location containing only build-time artifacts. There is no documented configuration property in the library to cap the cache.
Set the maximum entry count based on measured per-template heap cost, not a fixed number. Profile a representative template set (e.g., 500–1,000 templates) with VisualVM or JProfiler to determine the average retained size of a Template object plus its generated JavaScript source string. Multiply that average by your target cache budget (e.g., 50 MB) to derive the entry cap. In practice, simple templates often average 5–15 KB each; complex ones with many partials can exceed 50 KB.
target/classes/templates or src/main/resources/templates.ClassPathTemplateLoader pointed at the packaged prefix (e.g., /templates/). This loader reads from the sealed JAR, preventing post-deployment modification.import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.io.ClassPathTemplateLoader;
import com.github.jknack.handlebars.io.TemplateLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
public final class BoundedTemplateCache {
private final LoadingCache<String, Template> cache;
private final Handlebars handlebars;
public BoundedTemplateCache(int maxEntries) {
TemplateLoader loader = new ClassPathTemplateLoader("/templates", ".hbs");
this.handlebars = new Handlebars(loader);
this.cache = Caffeine.newBuilder()
.maximumSize(maxEntries)
.build(key -> handlebars.compile(key));
}
public Template get(String name) {
return cache.get(name);
}
}
Configure maxEntries from the profiled per-template size and your heap budget. The cache evicts least-recently-used entries automatically.
maxEntries; confirm eviction via Caffeine stats (cache.stats().evictionCount()).jar tf app.jar | grep templates/ — only .hbs or precompiled .js files should appear.ClassPathTemplateLoader will throw IOException.What is the average retained heap size of a single compiled Template object in your application (measured with your actual templates)? This number directly determines the safe maximumSize for your container's heap limit.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 05 Jul 2021, 23:17 UTC
One clarification on the premise: Handlebars.java is a pure Java implementation, so it does not consume JavaScript precompiler output. A compiled Template is a Java object, and its toString() returns the original source rather than executable JavaScript. The security boundary is therefore Java object instantiation from template source, not eval-style execution.
For bounding memory, the default TemplateCache is an unbounded map. You can supply a custom implementation—such as a Caffeine or Guava cache with a maximumSize—to cap retained templates. Ensure it is thread-safe, and confirm the installation API in the target release because it varies across versions.
To enforce trusted source boundaries, use the Loader abstraction. A ClassPathTemplateLoader or a FileTemplateLoader with a restricted prefix confines both template and partial resolution to a controlled location, preventing arbitrary filesystem access.