Cache FreeMarker templates

This commit is contained in:
Stian Thorgersen 2014-05-30 16:59:14 +01:00
parent 5dafad71ac
commit ea1f10361a
11 changed files with 71 additions and 19 deletions

View file

@ -51,6 +51,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
private boolean audit; private boolean audit;
private boolean passwordUpdateSupported; private boolean passwordUpdateSupported;
private ProviderSession session; private ProviderSession session;
private FreeMarkerUtil freeMarker;
public static enum MessageType {SUCCESS, WARNING, ERROR} public static enum MessageType {SUCCESS, WARNING, ERROR}
@ -59,8 +60,9 @@ public class FreeMarkerAccountProvider implements AccountProvider {
private String message; private String message;
private MessageType messageType; private MessageType messageType;
public FreeMarkerAccountProvider(ProviderSession session) { public FreeMarkerAccountProvider(ProviderSession session, FreeMarkerUtil freeMarker) {
this.session = session; this.session = session;
this.freeMarker = freeMarker;
} }
public AccountProvider setUriInfo(UriInfo uriInfo) { public AccountProvider setUriInfo(UriInfo uriInfo) {
@ -134,7 +136,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
} }
try { 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(); return Response.status(status).type(MediaType.TEXT_HTML).entity(result).build();
} catch (FreeMarkerException e) { } catch (FreeMarkerException e) {
logger.error("Failed to process template", e); logger.error("Failed to process template", e);

View file

@ -3,6 +3,7 @@ package org.keycloak.account.freemarker;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.account.AccountProvider; import org.keycloak.account.AccountProvider;
import org.keycloak.account.AccountProviderFactory; import org.keycloak.account.AccountProviderFactory;
import org.keycloak.freemarker.FreeMarkerUtil;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
@ -12,17 +13,21 @@ import javax.ws.rs.core.UriInfo;
*/ */
public class FreeMarkerAccountProviderFactory implements AccountProviderFactory { public class FreeMarkerAccountProviderFactory implements AccountProviderFactory {
private FreeMarkerUtil freeMarker;
@Override @Override
public AccountProvider create(ProviderSession providerSession) { public AccountProvider create(ProviderSession providerSession) {
return new FreeMarkerAccountProvider(providerSession); return new FreeMarkerAccountProvider(providerSession, freeMarker);
} }
@Override @Override
public void init(Config.Scope config) { public void init(Config.Scope config) {
freeMarker = new FreeMarkerUtil();
} }
@Override @Override
public void close() { public void close() {
freeMarker = null;
} }
@Override @Override

View file

@ -3,35 +3,58 @@ package org.keycloak.freemarker;
import freemarker.cache.URLTemplateLoader; import freemarker.cache.URLTemplateLoader;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateException; import org.keycloak.Config;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.net.URL; 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> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class FreeMarkerUtil { public class FreeMarkerUtil {
public static String processTemplate(Object data, String templateName, Theme theme) throws FreeMarkerException { private Map<String, Template> cache;
Writer out = new StringWriter();
Configuration cfg = new Configuration();
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 { try {
cfg.setTemplateLoader(new ThemeTemplateLoader(theme)); Template template;
Template template = cfg.getTemplate(templateName); 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); template.process(data, out);
return out.toString();
} catch (Exception e) { } catch (Exception e) {
throw new FreeMarkerException("Failed to process template " + templateName, 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; private Theme theme;

View file

@ -30,11 +30,13 @@ public class FreeMarkerEmailProvider implements EmailProvider {
private static final Logger log = Logger.getLogger(FreeMarkerEmailProvider.class); private static final Logger log = Logger.getLogger(FreeMarkerEmailProvider.class);
private ProviderSession session; private ProviderSession session;
private FreeMarkerUtil freeMarker;
private RealmModel realm; private RealmModel realm;
private UserModel user; private UserModel user;
public FreeMarkerEmailProvider(ProviderSession session) { public FreeMarkerEmailProvider(ProviderSession session, FreeMarkerUtil freeMarker) {
this.session = session; this.session = session;
this.freeMarker = freeMarker;
} }
@Override @Override
@ -81,7 +83,7 @@ public class FreeMarkerEmailProvider implements EmailProvider {
Theme theme = themeManager.createTheme(realm.getEmailTheme(), Theme.Type.EMAIL); Theme theme = themeManager.createTheme(realm.getEmailTheme(), Theme.Type.EMAIL);
String subject = theme.getMessages().getProperty(subjectKey); String subject = theme.getMessages().getProperty(subjectKey);
String body = FreeMarkerUtil.processTemplate(attributes, template, theme); String body = freeMarker.processTemplate(attributes, template, theme);
send(subject, body); send(subject, body);
} catch (Exception e) { } catch (Exception e) {

View file

@ -3,6 +3,7 @@ package org.keycloak.email.freemarker;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.email.EmailProvider; import org.keycloak.email.EmailProvider;
import org.keycloak.email.EmailProviderFactory; import org.keycloak.email.EmailProviderFactory;
import org.keycloak.freemarker.FreeMarkerUtil;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
/** /**
@ -10,17 +11,21 @@ import org.keycloak.provider.ProviderSession;
*/ */
public class FreeMarkerEmailProviderFactory implements EmailProviderFactory { public class FreeMarkerEmailProviderFactory implements EmailProviderFactory {
private FreeMarkerUtil freeMarker;
@Override @Override
public EmailProvider create(ProviderSession providerSession) { public EmailProvider create(ProviderSession providerSession) {
return new FreeMarkerEmailProvider(providerSession); return new FreeMarkerEmailProvider(providerSession, freeMarker);
} }
@Override @Override
public void init(Config.Scope config) { public void init(Config.Scope config) {
freeMarker = new FreeMarkerUtil();
} }
@Override @Override
public void close() { public void close() {
freeMarker = null;
} }
@Override @Override

View file

@ -64,6 +64,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
private MultivaluedMap<String, String> formData; private MultivaluedMap<String, String> formData;
private ProviderSession session; private ProviderSession session;
private FreeMarkerUtil freeMarker;
private RealmModel realm; private RealmModel realm;
private UserModel user; private UserModel user;
@ -72,8 +73,9 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
private UriInfo uriInfo; private UriInfo uriInfo;
public FreeMarkerLoginFormsProvider(ProviderSession session) { public FreeMarkerLoginFormsProvider(ProviderSession session, FreeMarkerUtil freeMarker) {
this.session = session; this.session = session;
this.freeMarker = freeMarker;
} }
public LoginFormsProvider setRealm(RealmModel realm) { public LoginFormsProvider setRealm(RealmModel realm) {
@ -210,7 +212,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
} }
try { 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(); return Response.status(status).type(MediaType.TEXT_HTML).entity(result).build();
} catch (FreeMarkerException e) { } catch (FreeMarkerException e) {
logger.error("Failed to process template", e); logger.error("Failed to process template", e);

View file

@ -1,6 +1,7 @@
package org.keycloak.login.freemarker; package org.keycloak.login.freemarker;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.freemarker.FreeMarkerUtil;
import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProvider;
import org.keycloak.login.LoginFormsProviderFactory; import org.keycloak.login.LoginFormsProviderFactory;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
@ -10,17 +11,21 @@ import org.keycloak.provider.ProviderSession;
*/ */
public class FreeMarkerLoginFormsProviderFactory implements LoginFormsProviderFactory { public class FreeMarkerLoginFormsProviderFactory implements LoginFormsProviderFactory {
private FreeMarkerUtil freeMarker;
@Override @Override
public LoginFormsProvider create(ProviderSession providerSession) { public LoginFormsProvider create(ProviderSession providerSession) {
return new FreeMarkerLoginFormsProvider(providerSession); return new FreeMarkerLoginFormsProvider(providerSession, freeMarker);
} }
@Override @Override
public void init(Config.Scope config) { public void init(Config.Scope config) {
freeMarker = new FreeMarkerUtil();
} }
@Override @Override
public void close() { public void close() {
freeMarker = null;
} }
@Override @Override
@ -28,4 +33,6 @@ public class FreeMarkerLoginFormsProviderFactory implements LoginFormsProviderFa
return "freemarker"; return "freemarker";
} }
} }

View file

@ -39,7 +39,7 @@
<google.zxing.version>2.2</google.zxing.version> <google.zxing.version>2.2</google.zxing.version>
<google.client.version>1.14.1-beta</google.client.version> <google.client.version>1.14.1-beta</google.client.version>
<winzipaes.version>1.0.1</winzipaes.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> <twitter4j.version>3.0.5</twitter4j.version>
<selenium.version>2.35.0</selenium.version> <selenium.version>2.35.0</selenium.version>

View file

@ -21,6 +21,7 @@
"theme": { "theme": {
"default": "keycloak", "default": "keycloak",
"staticMaxAge": 2592000, "staticMaxAge": 2592000,
"cacheTemplates": true,
"folder": { "folder": {
"dir": "${jboss.server.config.dir}/themes" "dir": "${jboss.server.config.dir}/themes"
} }

View file

@ -131,6 +131,10 @@ public class KeycloakServer {
System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath()); 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()); config.setResourcesHome(dir.getAbsolutePath());
} }

View file

@ -30,6 +30,7 @@
"theme": { "theme": {
"default": "keycloak", "default": "keycloak",
"staticMaxAge": 2592000, "staticMaxAge": 2592000,
"cacheTemplates": "${keycloak.theme.cacheTemplates:true}",
"folder": { "folder": {
"dir": "${keycloak.theme.dir}" "dir": "${keycloak.theme.dir}"
} }