VIVO-11 Add delimiters to Freemarker templates

If enabled by an option in runtime.properties
This commit is contained in:
j2blake 2013-01-25 16:29:17 -05:00
parent bb6b2fa970
commit f350c28d24
4 changed files with 169 additions and 8 deletions

View file

@ -0,0 +1,126 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.controller.freemarker;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import freemarker.cache.TemplateLoader;
/**
* Wrap a TemplateLoader, so each time a template is read, delimiters will be
* inserted at the beginning and end. This makes it easier for a developer can
* see what lines of HTML come from which templates.
*
* TemplateLoader returns a token object when finding a template, and then
* recognizes that object when it is used as an argument to getLastModified() or
* getReader(). In order to keep track of the template name, we wrap the token
* object and the name in a token of our own.
*
* Taking the easy way out and reading in the entire template into a string.
* This limits the template size to less than 2^31 characters (~2 GBytes). That
* seems adequate.
*/
public class DelimitingTemplateLoader implements TemplateLoader {
private static final Log log = LogFactory
.getLog(DelimitingTemplateLoader.class);
private final TemplateLoader innerLoader;
public DelimitingTemplateLoader(TemplateLoader innerLoader) {
this.innerLoader = innerLoader;
}
@Override
public Object findTemplateSource(String name) throws IOException {
Object innerTS = innerLoader.findTemplateSource(name);
if (innerTS == null) {
return null;
} else {
return new DelimitingTemplateSource(name, innerTS);
}
}
@Override
public long getLastModified(Object templateSource) {
DelimitingTemplateSource dts = (DelimitingTemplateSource) templateSource;
return innerLoader.getLastModified(dts.ts);
}
@Override
public Reader getReader(Object templateSource, String encoding)
throws IOException {
DelimitingTemplateSource dts = (DelimitingTemplateSource) templateSource;
StringBuilder sb = new StringBuilder();
sb.append("<!-- FM_BEGIN ").append(dts.name).append(" -->");
sb.append(readTemplateSource(encoding, dts.ts));
sb.append("<!-- FM_END ").append(dts.name).append(" -->\n");
return new StringReader(sb.toString());
}
private StringBuilder readTemplateSource(String encoding, Object ts)
throws IOException {
StringBuilder sb = new StringBuilder();
Reader reader = innerLoader.getReader(ts, encoding);
char[] buffer = new char[8192];
int howmany;
while (-1 != (howmany = reader.read(buffer))) {
sb.append(buffer, 0, howmany);
}
return sb;
}
@Override
public void closeTemplateSource(Object templateSource) throws IOException {
DelimitingTemplateSource dts = (DelimitingTemplateSource) templateSource;
innerLoader.closeTemplateSource(dts.ts);
}
// ----------------------------------------------------------------------
// Helper classes
// ----------------------------------------------------------------------
/**
* Data object, wrapping the template name and the templateSource object
* from the inner TemplateLoader.
*/
private static class DelimitingTemplateSource {
public final String name;
public final Object ts;
public DelimitingTemplateSource(String name, Object ts) {
if (name == null) {
throw new NullPointerException("name may not be null.");
}
if (ts == null) {
throw new NullPointerException("ts may not be null.");
}
this.name = name;
this.ts = ts;
}
@Override
public int hashCode() {
return name.hashCode() ^ ts.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o == null) {
return false;
}
if (!o.getClass().equals(this.getClass())) {
return false;
}
DelimitingTemplateSource that = (DelimitingTemplateSource) o;
return this.name.equals(that.name) && this.ts.equals(that.ts);
}
}
}

View file

@ -50,19 +50,21 @@ public class FreemarkerConfiguration extends Configuration {
private static final Log log = LogFactory.getLog(FreemarkerConfiguration.class);
private static final String PROPERTY_DEVELOPER_DEFEAT_CACHE = "developer.defeatFreemarkerCache";
private static final String PROPERTY_DEVELOPER_INSERT_DELIMITERS = "developer.insertFreemarkerDelimiters";
private final String themeDir;
private final ServletContext context;
private final ApplicationBean appBean;
private final ConfigurationProperties props;
FreemarkerConfiguration(String themeDir, ApplicationBean appBean, ServletContext context) {
this.themeDir = themeDir;
this.context = context;
this.appBean = appBean;
String flag = ConfigurationProperties.getBean(context).getProperty(
PROPERTY_DEVELOPER_DEFEAT_CACHE, "false");
this.props = ConfigurationProperties.getBean(context);
String flag = props.getProperty(PROPERTY_DEVELOPER_DEFEAT_CACHE, "false");
if (Boolean.valueOf(flag.trim())) {
log.debug("Disabling Freemarker template caching in development build.");
setTemplateUpdateDelay(0); // no template caching in development
@ -211,7 +213,13 @@ public class FreemarkerConfiguration extends Configuration {
log.error("Error creating template loaders");
}
return mtl;
// Add the ability to add delimiters to the templates, based on
// settings.
if (Boolean.valueOf(props.getProperty(PROPERTY_DEVELOPER_INSERT_DELIMITERS))) {
return new DelimitingTemplateLoader(mtl);
} else {
return mtl;
}
}
/**