keycloak-uncached

Merge pull request #442 from stianst/master Cache for FreeMarker

5/30/2014 1:03:37 PM

Details

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 0400f75..ca77182 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 7a3f272..0e70002 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/ExtendingThemeManager.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ExtendingThemeManager.java
index 06991e2..51f4984 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<ThemeProvider> 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/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/FreeMarkerUtil.java
index 8b1c67c..ca8773d 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 <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
  */
 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<String, Template> cache;
+
+    public FreeMarkerUtil() {
+        if (Config.scope("theme").getBoolean("cacheTemplates", false)) {
+            cache = Collections.synchronizedMap(new HashMap<String, Template>());
+        }
+    }
 
+    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();
+    private Template getTemplate(String templateName, Theme theme) throws IOException {
+        Configuration cfg = new Configuration();
+        cfg.setTemplateLoader(new ThemeTemplateLoader(theme));
+        return cfg.getTemplate(templateName);
     }
 
-    public static class ThemeTemplateLoader extends URLTemplateLoader {
+    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 0ec1fcb..9a7d37c 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 b98d981..1caf677 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 1dfcd29..889ae3b 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<String, String> 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 6b11d19..0e5905b 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";
     }
 
+
+
 }

pom.xml 2(+1 -1)

diff --git a/pom.xml b/pom.xml
index 87b3dab..44916d4 100755
--- a/pom.xml
+++ b/pom.xml
@@ -39,7 +39,7 @@
         <google.zxing.version>2.2</google.zxing.version>
         <google.client.version>1.14.1-beta</google.client.version>
         <winzipaes.version>1.0.1</winzipaes.version>
-        <freemarker.version>2.3.19</freemarker.version>
+        <freemarker.version>2.3.20</freemarker.version>
         <twitter4j.version>3.0.5</twitter4j.version>
         <selenium.version>2.35.0</selenium.version>
 
diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json
index af4c79e..7bc7ffd 100755
--- a/server/src/main/resources/META-INF/keycloak-server.json
+++ b/server/src/main/resources/META-INF/keycloak-server.json
@@ -20,6 +20,8 @@
 
     "theme": {
         "default": "keycloak",
+        "staticMaxAge": 2592000,
+        "cacheTemplates": true,
         "folder": {
           "dir": "${jboss.server.config.dir}/themes"
         }
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 090a2df..9104ea9 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/services/src/main/java/org/keycloak/services/resources/ThemeResource.java b/services/src/main/java/org/keycloak/services/resources/ThemeResource.java
index 84413a3..55509d9 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/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
index 8fb61dc..8e4db51 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 13b70f8..bc3370f 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,8 @@
 
     "theme": {
         "default": "keycloak",
+        "staticMaxAge": 2592000,
+        "cacheTemplates": "${keycloak.theme.cacheTemplates:true}",
         "folder": {
             "dir": "${keycloak.theme.dir}"
         }