Skip to content

Instantly share code, notes, and snippets.

@moravcik
Created October 9, 2012 14:12
Show Gist options
  • Save moravcik/3859062 to your computer and use it in GitHub Desktop.
Save moravcik/3859062 to your computer and use it in GitHub Desktop.
Freemarker template example with Spring expressions
<@spel expression="We say '{@greetingBean.printGreeting(name)}'"/> from Freemarker
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.19</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.adaptiweb.utils</groupId>
<artifactId>spel-evaluator</artifactId>
<version>1.5-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
public class SpelFreemarkerTemplateDirective implements TemplateDirectiveModel {
SpelEvaluator spelEvaluator;
@Required
public void setSpelEvaluator(SpelEvaluator spelEvaluator) {
this.spelEvaluator = spelEvaluator;
}
@Override
public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException {
@SuppressWarnings("unchecked")
String spelResult = spelEvaluator
// set all freemarker variables as root object - map
.setRootObject(new FreemarkerVariables(env, env.getKnownVariableNames()))
// resolve expression either from parameter or from body
.setExpression(resolveSpelExpression(params, body))
.evaluate(String.class);
env.getOut().write(spelResult);
spelEvaluator.dispose();
}
private String resolveSpelExpression(Map<String, Object> params, TemplateDirectiveBody body) throws TemplateException, IOException {
if (params.containsKey("expression")) {
TemplateModel expressionModel = (TemplateModel) params.get("expression");
return (String) DeepUnwrap.permissiveUnwrap(expressionModel);
} else { // expression in body
StringWriter expressionWriter = new StringWriter();
body.render(expressionWriter);
return expressionWriter.toString();
}
}
static class FreemarkerVariables implements Map<String, Object> {
final Environment env;
final Set<String> variableNames;
FreemarkerVariables(Environment env, Set<String> variableNames) {
this.env = env;
this.variableNames = variableNames;
}
@Override
public int size() {
return variableNames.size();
}
@Override
public boolean isEmpty() {
return variableNames.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return variableNames.contains(key);
}
@Override
public Object get(Object key) {
try {
TemplateModel model = env.getVariable((String) key);
return DeepUnwrap.permissiveUnwrap(model);
} catch (TemplateModelException e) {
return null;
}
}
@Override
public Set<String> keySet() {
return variableNames;
}
@Override public boolean containsValue(Object value) { throw new RuntimeException("Unsupported operation"); }
@Override public Object put(String key, Object value) { throw new RuntimeException("Unsupported operation"); }
@Override public Object remove(Object key) { throw new RuntimeException("Unsupported operation"); }
@Override public void putAll(Map<? extends String, ? extends Object> m) { throw new RuntimeException("Unsupported operation"); }
@Override public void clear() { throw new RuntimeException("Unsupported operation"); }
@Override public Collection<Object> values() { throw new RuntimeException("Unsupported operation"); }
@Override public Set<java.util.Map.Entry<String, Object>> entrySet() { throw new RuntimeException("Unsupported operation"); }
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- FreeMarker configuration -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
p:configuration-ref="freemarkerConfiguration"/>
<bean id="freemarkerConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean"
p:templateLoaderPath="/" p:preferFileSystemAccess="false">
<property name="freemarkerVariables">
<map>
<entry key="spel" value-ref="spelDirective"/>
</map>
</property>
</bean>
<bean id="spelDirective" class="com.adaptiweb.blog.example.SpelFreemarkerTemplateDirective"
p:spelEvaluator-ref="spelEvaluator"/>
<bean id="spelEvaluator" class="com.adaptiweb.utils.spel.SpelEvaluatorFactoryBean"
p:useBeanResolver="true" p:useBeanAccessor="true" p:useMapAccessor="true"
p:templatePrefix="{" p:templateSuffix="}"/>
<bean id="greetingBean" class="com.adaptiweb.blog.example.SpelFreemarkerTest.GreetingBean"
p:greeting="Hello"/>
</beans>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpelFreemarkerTest {
@Autowired GreetingBean greetingBean;
@Autowired Configuration freemarkerConfig;
@Test
public void testFreemarkerSpel() throws IOException, TemplateException {
Map<String, String> model = new HashMap<String, String>();
model.put("name", "Felix");
Template template = freemarkerConfig.getTemplate("greeting-template.ftl");
String output = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
System.out.println(output);
String expected = "We say '" + greetingBean.printGreeting(model.get("name")) + "' from Freemarker";
assertEquals(expected, output);
}
public static class GreetingBean {
private String greeting;
@Required
public void setGreeting(String greeting) {
this.greeting = greeting;
}
public String printGreeting(String name) {
return greeting + " " + name;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment