From ea1f10361af5da902cb10549a7ae7dc2d2b576eb Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 30 May 2014 16:59:14 +0100 Subject: [PATCH] Cache FreeMarker templates --- .../freemarker/FreeMarkerAccountProvider.java | 6 ++- .../FreeMarkerAccountProviderFactory.java | 7 +++- .../keycloak/freemarker/FreeMarkerUtil.java | 41 +++++++++++++++---- .../freemarker/FreeMarkerEmailProvider.java | 6 ++- .../FreeMarkerEmailProviderFactory.java | 7 +++- .../FreeMarkerLoginFormsProvider.java | 6 ++- .../FreeMarkerLoginFormsProviderFactory.java | 9 +++- pom.xml | 2 +- .../resources/META-INF/keycloak-server.json | 1 + .../keycloak/testutils/KeycloakServer.java | 4 ++ .../resources/META-INF/keycloak-server.json | 1 + 11 files changed, 71 insertions(+), 19 deletions(-) diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java index 0400f756988..ca77182f4b4 100755 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java @@ -51,6 +51,7 @@ public class FreeMarkerAccountProvider implements AccountProvider { private boolean audit; private boolean passwordUpdateSupported; private ProviderSession session; + private FreeMarkerUtil freeMarker; public static enum MessageType {SUCCESS, WARNING, ERROR} @@ -59,8 +60,9 @@ public class FreeMarkerAccountProvider implements AccountProvider { private String message; private MessageType messageType; - public FreeMarkerAccountProvider(ProviderSession session) { + public FreeMarkerAccountProvider(ProviderSession session, FreeMarkerUtil freeMarker) { this.session = session; + this.freeMarker = freeMarker; } public AccountProvider setUriInfo(UriInfo uriInfo) { @@ -134,7 +136,7 @@ public class FreeMarkerAccountProvider implements AccountProvider { } try { - String result = FreeMarkerUtil.processTemplate(attributes, Templates.getTemplate(page), theme); + String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme); return Response.status(status).type(MediaType.TEXT_HTML).entity(result).build(); } catch (FreeMarkerException e) { logger.error("Failed to process template", e); diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProviderFactory.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProviderFactory.java index 7a3f272c7a4..0e70002ce0f 100644 --- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProviderFactory.java +++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProviderFactory.java @@ -3,6 +3,7 @@ package org.keycloak.account.freemarker; import org.keycloak.Config; import org.keycloak.account.AccountProvider; import org.keycloak.account.AccountProviderFactory; +import org.keycloak.freemarker.FreeMarkerUtil; import org.keycloak.provider.ProviderSession; import javax.ws.rs.core.UriInfo; @@ -12,17 +13,21 @@ import javax.ws.rs.core.UriInfo; */ public class FreeMarkerAccountProviderFactory implements AccountProviderFactory { + private FreeMarkerUtil freeMarker; + @Override public AccountProvider create(ProviderSession providerSession) { - return new FreeMarkerAccountProvider(providerSession); + return new FreeMarkerAccountProvider(providerSession, freeMarker); } @Override public void init(Config.Scope config) { + freeMarker = new FreeMarkerUtil(); } @Override public void close() { + freeMarker = null; } @Override diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java index 8b1c67c8843..ca8773d639a 100644 --- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java +++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java @@ -3,35 +3,58 @@ package org.keycloak.freemarker; import freemarker.cache.URLTemplateLoader; import freemarker.template.Configuration; import freemarker.template.Template; -import freemarker.template.TemplateException; +import org.keycloak.Config; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * @author Stian Thorgersen */ public class FreeMarkerUtil { - public static String processTemplate(Object data, String templateName, Theme theme) throws FreeMarkerException { - Writer out = new StringWriter(); - Configuration cfg = new Configuration(); + private Map cache; + public FreeMarkerUtil() { + if (Config.scope("theme").getBoolean("cacheTemplates", false)) { + cache = Collections.synchronizedMap(new HashMap()); + } + } + + public String processTemplate(Object data, String templateName, Theme theme) throws FreeMarkerException { try { - cfg.setTemplateLoader(new ThemeTemplateLoader(theme)); - Template template = cfg.getTemplate(templateName); + Template template; + if (cache != null) { + String key = theme.getName() + "/" + templateName; + template = cache.get(key); + if (template == null) { + template = getTemplate(templateName, theme); + cache.put(key, template); + } + } else { + template = getTemplate(templateName, theme); + } + Writer out = new StringWriter(); template.process(data, out); + return out.toString(); } catch (Exception e) { throw new FreeMarkerException("Failed to process template " + templateName, e); } - - return out.toString(); } - public static class ThemeTemplateLoader extends URLTemplateLoader { + private Template getTemplate(String templateName, Theme theme) throws IOException { + Configuration cfg = new Configuration(); + cfg.setTemplateLoader(new ThemeTemplateLoader(theme)); + return cfg.getTemplate(templateName); + } + + class ThemeTemplateLoader extends URLTemplateLoader { private Theme theme; diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java index 0ec1fcbc939..9a7d37c1fe8 100644 --- a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java +++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java @@ -30,11 +30,13 @@ public class FreeMarkerEmailProvider implements EmailProvider { private static final Logger log = Logger.getLogger(FreeMarkerEmailProvider.class); private ProviderSession session; + private FreeMarkerUtil freeMarker; private RealmModel realm; private UserModel user; - public FreeMarkerEmailProvider(ProviderSession session) { + public FreeMarkerEmailProvider(ProviderSession session, FreeMarkerUtil freeMarker) { this.session = session; + this.freeMarker = freeMarker; } @Override @@ -81,7 +83,7 @@ public class FreeMarkerEmailProvider implements EmailProvider { Theme theme = themeManager.createTheme(realm.getEmailTheme(), Theme.Type.EMAIL); String subject = theme.getMessages().getProperty(subjectKey); - String body = FreeMarkerUtil.processTemplate(attributes, template, theme); + String body = freeMarker.processTemplate(attributes, template, theme); send(subject, body); } catch (Exception e) { diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProviderFactory.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProviderFactory.java index b98d9811fd7..1caf6774a79 100644 --- a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProviderFactory.java +++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProviderFactory.java @@ -3,6 +3,7 @@ package org.keycloak.email.freemarker; import org.keycloak.Config; import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailProviderFactory; +import org.keycloak.freemarker.FreeMarkerUtil; import org.keycloak.provider.ProviderSession; /** @@ -10,17 +11,21 @@ import org.keycloak.provider.ProviderSession; */ public class FreeMarkerEmailProviderFactory implements EmailProviderFactory { + private FreeMarkerUtil freeMarker; + @Override public EmailProvider create(ProviderSession providerSession) { - return new FreeMarkerEmailProvider(providerSession); + return new FreeMarkerEmailProvider(providerSession, freeMarker); } @Override public void init(Config.Scope config) { + freeMarker = new FreeMarkerUtil(); } @Override public void close() { + freeMarker = null; } @Override diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java index 1dfcd298277..889ae3bed69 100755 --- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java +++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java @@ -64,6 +64,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider { private MultivaluedMap formData; private ProviderSession session; + private FreeMarkerUtil freeMarker; private RealmModel realm; private UserModel user; @@ -72,8 +73,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider { private UriInfo uriInfo; - public FreeMarkerLoginFormsProvider(ProviderSession session) { + public FreeMarkerLoginFormsProvider(ProviderSession session, FreeMarkerUtil freeMarker) { this.session = session; + this.freeMarker = freeMarker; } public LoginFormsProvider setRealm(RealmModel realm) { @@ -210,7 +212,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider { } try { - String result = FreeMarkerUtil.processTemplate(attributes, Templates.getTemplate(page), theme); + String result = freeMarker.processTemplate(attributes, Templates.getTemplate(page), theme); return Response.status(status).type(MediaType.TEXT_HTML).entity(result).build(); } catch (FreeMarkerException e) { logger.error("Failed to process template", e); diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProviderFactory.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProviderFactory.java index 6b11d19e231..0e5905b3d22 100755 --- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProviderFactory.java +++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProviderFactory.java @@ -1,6 +1,7 @@ package org.keycloak.login.freemarker; import org.keycloak.Config; +import org.keycloak.freemarker.FreeMarkerUtil; import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProviderFactory; import org.keycloak.provider.ProviderSession; @@ -10,17 +11,21 @@ import org.keycloak.provider.ProviderSession; */ public class FreeMarkerLoginFormsProviderFactory implements LoginFormsProviderFactory { + private FreeMarkerUtil freeMarker; + @Override public LoginFormsProvider create(ProviderSession providerSession) { - return new FreeMarkerLoginFormsProvider(providerSession); + return new FreeMarkerLoginFormsProvider(providerSession, freeMarker); } @Override public void init(Config.Scope config) { + freeMarker = new FreeMarkerUtil(); } @Override public void close() { + freeMarker = null; } @Override @@ -28,4 +33,6 @@ public class FreeMarkerLoginFormsProviderFactory implements LoginFormsProviderFa return "freemarker"; } + + } diff --git a/pom.xml b/pom.xml index 87b3dab3cf0..44916d4ad74 100755 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ 2.2 1.14.1-beta 1.0.1 - 2.3.19 + 2.3.20 3.0.5 2.35.0 diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json index f35857eb213..7bc7ffd591d 100755 --- a/server/src/main/resources/META-INF/keycloak-server.json +++ b/server/src/main/resources/META-INF/keycloak-server.json @@ -21,6 +21,7 @@ "theme": { "default": "keycloak", "staticMaxAge": 2592000, + "cacheTemplates": true, "folder": { "dir": "${jboss.server.config.dir}/themes" } diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java index 8fb61dc41ce..8e4db51c1bc 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java @@ -131,6 +131,10 @@ public class KeycloakServer { System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath()); } + if (!System.getProperties().containsKey("keycloak.theme.cacheTemplates")) { + System.setProperty("keycloak.theme.cacheTemplates", "false"); + } + config.setResourcesHome(dir.getAbsolutePath()); } diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json index 9d71a169e3d..bc3370f53e2 100755 --- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json @@ -30,6 +30,7 @@ "theme": { "default": "keycloak", "staticMaxAge": 2592000, + "cacheTemplates": "${keycloak.theme.cacheTemplates:true}", "folder": { "dir": "${keycloak.theme.dir}" }