This is extended and translated into English version of my post
Spring MVC + FreeMarker + Tiles 2. I've found that many are interested about the joint work of those frameworks.
Well, this article shows how to configure the Spring MVC application with
Tiles 2 and
FreeMarker. You may don't use the
SpringMVC framework, and in this case you need to make by your hands all the configuration work that Spring MVC does for you.
As you may know, Freemarker is popular template engine write on Java and used by mostly by Java applications. Tiles 2 is also Java frameworks used to prepare document using one or more "tiles" - the parts that are defined separately but used to be generated at one document. For example, using tiles you can define site header/footer, news or post blocks at your site etc.
So, how did I get it working?
Simple!
In file
web.xml
add the freemarker configuration.
<servlet>
<servlet-name>pages</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>pages</servlet-name>
<url-pattern>*.page</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>NoCache</param-name>
<param-value>true</param-value>
</init-param>
<init-paramv
<param-name>ContentType</param-name>
<param-value>text/html</param-value>
</init-param>
....
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
And here is the important part *-servlet.xml configuration:
<!-- Tiles 2 configuration-->
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<!-- the tiles configuration files are declared here -->
<value>/WEB-INF/defs/general.xml</value>
<value>/WEB-INF/defs/secure.xml</value>
</list>
</property>
</bean>
<bean id="tilesViewResolver" class="com.javaua.spring.integration.ext.ExtUrlBasedViewResolver">
<property name="viewClass" value="com.javaua.spring.integration.ext.ExtTilesView"/>
<property name="exposeSpringMacroModel" value="true"/>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="cache" value="true"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
<!-- if you want to use the Spring FreeMarker macros, set this property to true -->
<property name="exposeSpringMacroHelpers" value="true"/>
</bean>
<!-- Controller that returns view = /layout/header -->
<bean name="/layout/header.page" class="com.javaua.HeaderController"/>
Also show here the part of general.xml file where Tiles definitions are set up.:
<definition name="home" extends="default">
<put-attribute name="title" value="Home Page"/>
<put-attribute name="body" value="/templates/home.ftl"/>
</definition>
<definition name="default" template="/templates/main.ftl">
<put-attribute name="header" value="/layout/header.page"/>
<put-attribute name="footer" value="/templates/layout/footer.ftl"/>
</definition>
<definition name="/layout/header" template="/templates/layout/header.ftl"/>
Source code of
com.javaua.spring.integration.ext.ExtUrlBasedViewResolver
class:
import org.springframework.core.Ordered;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.AbstractUrlBasedView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
public class ExtUrlBasedViewResolver extends UrlBasedViewResolver implements Ordered {
private boolean exposeSpringMacroModel = false;
public void setExposeSpringMacroModel(boolean exposeSpringMacroModel) {
this.exposeSpringMacroModel = exposeSpringMacroModel;
}
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
View viewObj = (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
if (viewObj instanceof ExtTilesView) {
ExtTilesView tilesView = (ExtTilesView) viewObj;
tilesView.setExposeSpringMacroModel(exposeSpringMacroModel);
}
return viewObj;
}
Source code of
com.javaua.spring.integration.ext.ExtTilesView
class:
import org.springframework.web.servlet.view.tiles2.TilesView;
import org.springframework.web.servlet.view.AbstractTemplateView;
import org.springframework.web.servlet.support.RequestContext;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletException;
import java.util.Map;
public class ExtTilesView extends TilesView {
private boolean exposeSpringMacroModel = false;
public void setExposeSpringMacroModel(boolean exposeSpringMacroModel) {
this.exposeSpringMacroModel = exposeSpringMacroModel;
}
@SuppressWarnings("unchecked")
protected final void renderMergedOutputModel(Map model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (exposeSpringMacroModel) {
if (model.containsKey(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE)) {
throw new ServletException(
"Cannot expose bind macro helper '" +
AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE +
"' because of an existing model object of the same name");
}
model.put(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE,
new RequestContext(request, getServletContext(), model));
}
super.renderMergedOutputModel(model, request, response);
}
}
Why we need those two classes? Simple enough. Class
ExtTilesView
exposes Spring macro model which is required when using standard macros used to support work with Freemarker and Velocity typelate frameworks. And the class
ExtUrlBasedViewResolver
helps us to prevent view-not-found-error. Because, standard
UrlBasedViewResolver
is used for getting tiles view, it will throw an error when view with specified name is not found. In my case, I have two view resolvers and each will throw an error if view can't be found.
And
FreeMarkerViewResolver
will request for the file, while tiles requests just for XML definition. That's why I've ran the tiles view resolver as first one.