From 5dafad71ac172893aec7c2432c52628667c57928 Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 30 May 2014 16:01:01 +0100 Subject: [PATCH 1/2] Set cache-control on static resources from themes --- .../keycloak/freemarker/ExtendingThemeManager.java | 6 ++++++ .../src/main/resources/META-INF/keycloak-server.json | 1 + .../keycloak/services/resources/ThemeResource.java | 11 ++++++++--- .../services/resources/admin/AdminConsole.java | 9 +++++++-- .../src/main/resources/META-INF/keycloak-server.json | 1 + 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java index 06991e20cc..51f4984311 100644 --- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java +++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java @@ -22,6 +22,7 @@ public class ExtendingThemeManager implements ThemeProvider { private List providers; private String defaultTheme; + private int staticMaxAge; public ExtendingThemeManager(ProviderSession providerSession) { providers = new LinkedList(); @@ -40,6 +41,11 @@ public class ExtendingThemeManager implements ThemeProvider { }); this.defaultTheme = Config.scope("theme").get("default"); + this.staticMaxAge = Config.scope("theme").getInt("staticMaxAge"); + } + + public int getStaticMaxAge() { + return staticMaxAge; } @Override diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json index af4c79e2cf..f35857eb21 100755 --- a/server/src/main/resources/META-INF/keycloak-server.json +++ b/server/src/main/resources/META-INF/keycloak-server.json @@ -20,6 +20,7 @@ "theme": { "default": "keycloak", + "staticMaxAge": 2592000, "folder": { "dir": "${jboss.server.config.dir}/themes" } diff --git a/services/src/main/java/org/keycloak/services/resources/ThemeResource.java b/services/src/main/java/org/keycloak/services/resources/ThemeResource.java index 84413a3ce4..55509d923f 100755 --- a/services/src/main/java/org/keycloak/services/resources/ThemeResource.java +++ b/services/src/main/java/org/keycloak/services/resources/ThemeResource.java @@ -11,6 +11,7 @@ import javax.activation.MimetypesFileTypeMap; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; +import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import java.io.InputStream; @@ -39,14 +40,18 @@ public class ThemeResource { * @return */ @GET - @Path("/{themType}/{themeName}/{path:.*}") - public Response getResource(@PathParam("themType") String themType, @PathParam("themeName") String themeName, @PathParam("path") String path) { + @Path("/{themeType}/{themeName}/{path:.*}") + public Response getResource(@PathParam("themeType") String themType, @PathParam("themeName") String themeName, @PathParam("path") String path) { try { ExtendingThemeManager themeManager = new ExtendingThemeManager(providerSession); Theme theme = themeManager.createTheme(themeName, Theme.Type.valueOf(themType.toUpperCase())); InputStream resource = theme.getResourceAsStream(path); if (resource != null) { - return Response.ok(resource).type(mimeTypes.getContentType(path)).build(); + CacheControl cacheControl = new CacheControl(); + cacheControl.setNoTransform(false); + cacheControl.setMaxAge(themeManager.getStaticMaxAge()); + + return Response.ok(resource).type(mimeTypes.getContentType(path)).cacheControl(cacheControl).build(); } else { return Response.status(Response.Status.NOT_FOUND).build(); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java index 090a2dfa52..9104ea9cba 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java @@ -30,6 +30,7 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; @@ -314,13 +315,17 @@ public class AdminConsole { } try { - //logger.info("getting resource: " + path + " uri: " + uriInfo.getRequestUri().toString()); ExtendingThemeManager themeManager = new ExtendingThemeManager(providerSession); Theme theme = themeManager.createTheme(realm.getAdminTheme(), Theme.Type.ADMIN); InputStream resource = theme.getResourceAsStream(path); if (resource != null) { String contentType = mimeTypes.getContentType(path); - return Response.ok(resource).type(contentType).build(); + + CacheControl cacheControl = new CacheControl(); + cacheControl.setNoTransform(false); + cacheControl.setMaxAge(themeManager.getStaticMaxAge()); + + return Response.ok(resource).type(contentType).cacheControl(cacheControl).build(); } else { return Response.status(Response.Status.NOT_FOUND).build(); } 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 13b70f8878..9d71a169e3 100755 --- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json +++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json @@ -29,6 +29,7 @@ "theme": { "default": "keycloak", + "staticMaxAge": 2592000, "folder": { "dir": "${keycloak.theme.dir}" } From ea1f10361af5da902cb10549a7ae7dc2d2b576eb Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Fri, 30 May 2014 16:59:14 +0100 Subject: [PATCH 2/2] 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 0400f75698..ca77182f4b 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 7a3f272c7a..0e70002ce0 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 8b1c67c884..ca8773d639 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 0ec1fcbc93..9a7d37c1fe 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 b98d9811fd..1caf6774a7 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 1dfcd29827..889ae3bed6 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 6b11d19e23..0e5905b3d2 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 87b3dab3cf..44916d4ad7 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 f35857eb21..7bc7ffd591 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 8fb61dc41c..8e4db51c1b 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 9d71a169e3..bc3370f53e 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}" }