Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 37 additions & 81 deletions src/main/java/de/neuland/pug4j/PugConfiguration.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package de.neuland.pug4j;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import de.neuland.pug4j.Pug4J.Mode;
import de.neuland.pug4j.exceptions.PugCompilerException;
import de.neuland.pug4j.exceptions.PugException;
Expand All @@ -11,14 +9,11 @@
import de.neuland.pug4j.filter.CssFilter;
import de.neuland.pug4j.filter.Filter;
import de.neuland.pug4j.filter.JsFilter;
import de.neuland.pug4j.parser.Parser;
import de.neuland.pug4j.parser.node.Node;
import de.neuland.pug4j.template.FileTemplateLoader;
import de.neuland.pug4j.template.PugTemplate;
import de.neuland.pug4j.template.TemplateLoader;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -59,6 +54,7 @@
* @see RenderContext
*/
@Deprecated(since = "3.0.0", forRemoval = true)
@SuppressWarnings("deprecation")
public class PugConfiguration {

private static final String FILTER_CDATA = "cdata";
Expand All @@ -73,11 +69,10 @@ public class PugConfiguration {
private Map<String, Object> sharedVariables = new HashMap<>();
private TemplateLoader templateLoader = new FileTemplateLoader();
private ExpressionHandler expressionHandler = new JexlExpressionHandler();
private PugEngine engine = null; // Cached engine instance
private volatile PugEngine engine = null; // Cached engine instance
protected static final long MAX_ENTRIES = 1000L;
private long maxCacheSize = MAX_ENTRIES;
private Cache<String, PugTemplate> cache =
Caffeine.newBuilder().maximumSize(maxCacheSize).build();
private int expressionCacheSize = 5000;

public PugConfiguration() {
setFilter(FILTER_CDATA, new CDATAFilter());
Expand All @@ -92,38 +87,28 @@ public PugConfiguration() {
* @return the PugEngine instance
*/
public PugEngine getOrCreateEngine() {
if (engine == null) {
engine = PugEngine.builder()
.templateLoader(templateLoader)
.expressionHandler(expressionHandler)
.caching(caching)
.filters(filters)
.build();
PugEngine result = engine;
if (result == null) {
synchronized (this) {
result = engine;
if (result == null) {
result = PugEngine.builder()
.templateLoader(templateLoader)
.expressionHandler(expressionHandler)
.caching(caching)
.maxCacheSize(maxCacheSize)
.expressionCacheSize(expressionCacheSize)
.filters(filters)
.build();
engine = result;
}
}
}
return engine;
return result;
}

public PugTemplate getTemplate(String name) throws IOException, PugException {

if (caching) {

long lastModified = templateLoader.getLastModified(name);
return cache.get(
getKeyValue(name, lastModified),
value -> {
try {
return createTemplate(name);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}

return createTemplate(name);
}

private String getKeyValue(String name, long lastModified) {
return name + "-" + lastModified;
return getOrCreateEngine().getTemplate(name);
}

public void renderTemplate(PugTemplate template, Map<String, Object> model, Writer writer)
Expand All @@ -148,14 +133,6 @@ public String renderTemplate(PugTemplate template, Map<String, Object> model)
return writer.toString();
}

private PugTemplate createTemplate(String name) throws PugException, IOException {

Parser parser = new Parser(name, templateLoader, expressionHandler);
Node root = parser.parse();
PugTemplate template = new PugTemplate(root, getMode());
return template;
}

public boolean isPrettyPrint() {
return prettyPrint;
}
Expand Down Expand Up @@ -218,8 +195,8 @@ public void setMode(Mode mode) {
}

public boolean templateExists(String url) {
try {
return templateLoader.getReader(url) != null;
try (var reader = templateLoader.getReader(url)) {
return reader != null;
} catch (IOException e) {
return false;
}
Expand All @@ -231,15 +208,17 @@ public boolean isCaching() {

public void setCaching(boolean cache) {
if (cache != this.caching) {
expressionHandler.setCache(cache);
this.caching = cache;
this.engine = null; // Invalidate cached engine
}
}

public void clearCache() {
expressionHandler.clearCache();
cache.invalidateAll();
public synchronized void clearCache() {
if (engine != null) {
engine.clearCache();
} else {
expressionHandler.clearCache();
}
}

/**
Expand All @@ -264,52 +243,29 @@ public void setMaxCacheSize(long maxCacheSize) {
}
if (this.maxCacheSize != maxCacheSize) {
this.maxCacheSize = maxCacheSize;
rebuildCache();
this.engine = null; // Invalidate cached engine
}
}

/**
* Rebuilds the template cache with the current maxCacheSize. This will clear all existing cached
* templates.
*/
private void rebuildCache() {
cache = Caffeine.newBuilder().maximumSize(maxCacheSize).build();
}

/**
* Sets the expression handler cache size. This method only works if the expression handler is a
* {@link JexlExpressionHandler}. If a different expression handler is used, this method will
* throw an {@link IllegalStateException}.
* Sets the expression handler cache size.
*
* @param cacheSize the cache size (0 to disable, positive value to enable with specific size)
* @throws IllegalStateException if the expression handler is not a JexlExpressionHandler
* @throws IllegalArgumentException if cacheSize is negative
*/
public void setExpressionCacheSize(int cacheSize) {
if (expressionHandler instanceof JexlExpressionHandler) {
((JexlExpressionHandler) expressionHandler).setCacheSize(cacheSize);
} else {
throw new IllegalStateException(
"setExpressionCacheSize() only works with JexlExpressionHandler, current handler is: "
+ expressionHandler.getClass().getName());
if (cacheSize < 0) {
throw new IllegalArgumentException("expressionCacheSize must be non-negative");
}
this.expressionCacheSize = cacheSize;
this.engine = null; // Invalidate cached engine
}

/**
* Gets the expression handler cache size. This method only works if the expression handler is a
* {@link JexlExpressionHandler}. If a different expression handler is used, this method will
* throw an {@link IllegalStateException}.
* Gets the expression handler cache size.
*
* @return the cache size
* @throws IllegalStateException if the expression handler is not a JexlExpressionHandler
*/
public int getExpressionCacheSize() {
if (expressionHandler instanceof JexlExpressionHandler) {
return ((JexlExpressionHandler) expressionHandler).getCacheSize();
} else {
throw new IllegalStateException(
"getExpressionCacheSize() only works with JexlExpressionHandler, current handler is: "
+ expressionHandler.getClass().getName());
}
return expressionCacheSize;
}
}
40 changes: 36 additions & 4 deletions src/main/java/de/neuland/pug4j/PugEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ private PugEngine(Builder builder) {

// Configure expression handler caching
if (builder.caching) {
if (expressionHandler instanceof JexlExpressionHandler) {
((JexlExpressionHandler) expressionHandler).setCacheSize(builder.expressionCacheSize);
if (expressionHandler instanceof JexlExpressionHandler jexlHandler) {
jexlHandler.setCacheSize(builder.expressionCacheSize);
} else {
expressionHandler.setCache(true);
}
Expand Down Expand Up @@ -125,8 +125,8 @@ public PugTemplate getTemplate(String name) throws IOException, PugException {
* @return true if the template exists, false otherwise
*/
public boolean templateExists(String name) {
try {
return templateLoader.getReader(name) != null;
try (var reader = templateLoader.getReader(name)) {
return reader != null;
} catch (IOException e) {
return false;
}
Expand Down Expand Up @@ -270,6 +270,38 @@ public String render(PugTemplate template, Map<String, Object> model)
return render(template, model, RenderContext.defaults());
}

/**
* Loads a template by name and renders it with the given model and render context.
*
* @param templateName the template name/path
* @param model the model data
* @param context the render context
* @return the rendered HTML as a String
* @throws IOException if the template cannot be loaded
* @throws PugCompilerException if rendering fails
* @since 3.0.0
*/
public String render(String templateName, Map<String, Object> model, RenderContext context)
throws IOException, PugCompilerException {
PugTemplate template = getTemplate(templateName);
return render(template, model, context);
}

/**
* Loads a template by name and renders it with the given model using default render settings.
*
* @param templateName the template name/path
* @param model the model data
* @return the rendered HTML as a String
* @throws IOException if the template cannot be loaded
* @throws PugCompilerException if rendering fails
* @since 3.0.0
*/
public String render(String templateName, Map<String, Object> model)
throws IOException, PugCompilerException {
return render(templateName, model, RenderContext.defaults());
}

private PugTemplate createTemplate(String name) throws PugException, IOException {
Parser parser = new Parser(name, templateLoader, expressionHandler);
Node root = parser.parse();
Expand Down
Loading