Using FreeMarker with servlets

In a fundamental sense, using FreeMarker in the web application space is no different from anywhere else; FreeMarker writes its output to a Writer that you pass to the Template.process method, and it does not care if that Writer prints to the console or to a file or to the output stream of HttpServletResponse. FreeMarker knows nothing about servlets and Web; it just merges Java object with template files and generates text output from them. From here, it is up to you how to build a Web application around this.

But, probably you want to user FreeMarker with some already existing Web application framework. Many frameworks rely on the ``Model 2'' architecture, where JSP pages handle presentation. If you use such a framework (for example, Apache Struts), then read on. For other frameworks please refer to the documentation of the framework.

Using FreeMarker for ``Model 2''

Many frameworks follow the strategy that the HTTP request is dispatched to user-defined ``action'' classes that put data into ServletContext, HttpSession and HttpServletRequest objects as attributes, and then the request is forwarded by the framework to a JSP page (the view) that will generate the HTML page using the data sent with the attributes. This is often referred as Model 2.

figure

With these frameworks you can simply use FTL files instead of JSP files. But, since your servlet container (Web application server), unlike with JSP files, does not know out-of-the-box what to do with FTL files, a little extra configuring is needed for your Web application:

  1. Copy freemarker.jar (from the lib directory of the FreeMarker distribution) into the WEB-INF/lib directory of your Web application.

  2. Insert the following section to the WEB-INF/web.xml file of your Web application (and adjust it if required):

<servlet>
  <servlet-name>freemarker</servlet-name>
  <servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
    
  <!-- FreemarkerServlet settings: -->
  <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-param>
    <param-name>ContentType</param-name>
    <param-value>text/html</param-value>
  </init-param>
    
  <!-- FreeMarker settings: -->
  <init-param>
    <param-name>template_update_delay</param-name>
    <param-value>0</param-value> <!-- 0 is for development only! Use higher value otherwise. -->
  </init-param>
  <init-param>
    <param-name>default_encoding</param-name>
    <param-value>ISO-8859-1</param-value>
  </init-param>
  <init-param>
    <param-name>number_format</param-name>
    <param-value>0.##########</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>  

That's all. After this, you can use FTL files (*.ftl) in the same manner as JSP (*.jsp) files. (Of course you can choose another extension besides ftl; it is just the convention)

Note

How does it work? Let's examine how JSP-s work. Many servlet container handles JSP-s with a servlet that is mapped to the *.jsp request URL pattern. That servlet will receive all requests where the request URL ends with .jsp, find the JSP file based on the request URL, and internally compiles it to a Servlet, and then call the generated servlet to generate the page. The FreemarkerServlet mapped here to the *.ftl URL pattern does the same, except that FTL files are not compiled to Servlet-s, but to Template objects, and then the process method of Template will be called to generate the page.

For example, instead of this JSP file (note that it heavily uses Struts tag-libs to save designers from embedded Java monsters):

<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html>
<head><title>Acmee Products International</title>
<body>
  <h1>Hello <bean:write name="user"/>!</h1>
  <p>These are our latest offers:
  <ul>
    <logic:iterate name="latestProducts" id="prod">
      <li><bean:write name="prod" property="name"/>
        for <bean:write name="prod" property="price"/> Credits.
    </logic:iterate>
  </ul>
</body>
</html>  

you can use this FTL file (use ftl file extension instead of jsp):

<html>
<head><title>Acmee Products International</title>
<body>
  <h1>Hello ${user}!</h1>
  <p>These are our latest offers:
  <ul>
    <#list latestProducts as prod>
      <li>${prod.name} for ${prod.price} Credits.
    </#list>
  </ul>
</body>
</html>  

Warning!

In FreeMarker <html:form action="/query">...</html:form> is just static text, so it is printed to the output as is, like any other XML or HTML markup. JSP tags are just FreeMarker directives, nothing special, so you use FreeMarker syntax for calling them, not JSP syntax: <@html.form action="/query">...</@html.form>. Note that in the FreeMarker syntax you don't use ${...} in parameters as in JSP, and you don't quote the parameter values. So this is WRONG:

<#-- WRONG: -->
<@my.jspTag color="${aVarialbe}" name="aStringLiteral"
            width="100" height=${a+b} />  

and this is good:

<#-- Good: -->
<@my.jspTag color=theColor name="aStringLiteral"
            width=100 height=a+b />  

In both templates, when you refer to user and latestProduct, it will first try to find a variable with that name that was created in the template (like prod; if you master JSP: a page scope attribute). If that fails, it will try to look up an attribute with that name in the HttpServletRequest, and if it is not there then in the HttpSession, and if it still doesn't find it then in the ServletContext. In the case of FTL this works because FreemarkerServlet builds the data-model from the attributes of the mentioned 3 objects. That is, in this case the root hash is not a java.util.Map (as it was in some example codes in this manual), but ServletContext+HttpSession+HttpServletRequest; FreeMarker is pretty flexible about what the data-model is. So if you want to put variable "name" into the data-model, then you call servletRequest.setAttribute("name", "Fred"); this is the logic of Model 2, and FreeMarker adapts oneself to it.

FreemarkerServlet also puts 3 hashes into the data-model, by which you can access the attributes of the 3 objects directly. The hash variables are: Request, Session, Application (corresponds to ServletContext).

FreemarkerServlet has various init-params. It can be set up to load templates from an arbitrary directory, from the classpath, or relative to the Web application directory. You can set the charset used for templates. You can set up what object wrapper do you want to use. Etc.

FreemarkerServlet is easily tailored to special needs through subclassing. Say, if you need to have additional variables available in your data-model for all templates, subclass the servlet and override the preTemplateProcess() method to shove any additional data you need into the model before the template gets processed. Or subclass the servlet, and set these globally available variables as shared variables in the Configuration.

For more information please read the Java API documentation of the class.

Using JSP custom tags in FTL

FreemarkerServlet puts a hash JspTaglibs into the data-model, what you can use to access JSP taglibs. The JSP custom tags will be accessible as plain user-defined directives. For example, this is a JSP file that uses some Struts tags:

<%@page contentType="text/html;charset=ISO-8859-2" language="java"%>
<%@taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%>

<html>
  <body>
    <h1><bean:message key="welcome.title"/></h1>
    <html:errors/>
    <html:form action="/query">
      Keyword: <html:text property="keyword"/><br>
      Exclude: <html:text property="exclude"/><br>
      <html:submit value="Send"/>
    </html:form>
  </body>
</html>  

And this is the (near) equivalent FTL:

<#assign html=JspTaglibs["/WEB-INF/struts-html.tld"]>
<#assign bean=JspTaglibs["/WEB-INF/struts-bean.tld"]>

<html>
  <body>
    <h1><@bean.message key="welcome.title"/></h1>
    <@html.errors/>
    <@html.form action="/query">
      Keyword: <@html.text property="keyword"/><br>
      Exclude: <@html.text property="exclude"/><br>
      <@html.submit value="Send"/>
    </@html.form>
  </body>
</html>  

Since JSP custom tags are written to operate in JSP environment, they assume that variables (often referred as ``beans'' in JSP world) are stored in 4 scopes: page scope, request scope, session scope and application scope. FTL has no such notation (the 4 scopes), but FreemarkerServlet provides emulated JSP environment for the custom JSP tags, which maintains correspondence between the ``beans'' of JSP scopes and FTL variables. For the custom JSP tags, the request, session and application scopes are exactly the same as with real JSP: the attributes of the javax.servlet.ServletContext, HttpSession and ServerRequest objects. From the FTL side you see these 3 scopes together as the data-model, as it was explained earlier. The page scope corresponds to the FTL global variables (see the global directive). That is, if you create a variable with the global directive, it will be visible for the custom tags as page scope variable through the emulated JSP environment. Also, if a JSP-tag creates a new page scope variable, the result will be the same as if you create a variable with the global directive. Note that the variables in the data-model are not visible as page-scope attributes for the JSP tags, despite that they are globally visible, since the data-model corresponds to the request, session and application scopes, not the page-scope.

On JSP pages you quote all attribute values, it does not mater if the type of the parameter is string or boolean or number. But since custom tags are accessible in FTL templates as user-defined FTL directives, you have to use the FTL syntax rules inside the custom tags, not the JSP rules. So when you specify the value of an ``attribute'', then on the right side of the = there is an FTL expression. Thus, you must not quote boolean and numerical parameter values (e.g. <@tiles.insert page="/layout.ftl" flush=true/>), or they are interpreted as string values, and this will cause a type mismatch error when FreeMarker tries to pass the value to the custom tag that expects non-string value. Also note, that naturally, you can use any FTL expression as attribute value, such as variables, calculated values, etc. (e.g. <@tiles.insert page=layoutName flush=foo && bar/>).

FreeMarker does not rely on the JSP support of the servlet container in which it is run when it uses JSP taglibs since it implements its own lightweight JSP runtime environment. There is only one small detail to pay attention to: to enable the FreeMarker JSP runtime environment to dispatch events to JSP taglibs that register event listeners in their TLD files, you should add this to the WEB-INF/web.xml of your Web application:

<listener>
  <listener-class>freemarker.ext.jsp.EventForwarding</listener-class>
</listener>  

Note that you can use JSP taglibs with FreeMarker even if the servlet container has no native JSP support, just make sure that the javax.servlet.jsp.* packages for JSP 1.2 (or later) are available to your Web application. If your servlet container comes with JSP 1.1, then you have to obtain the following six classes (for example you can extract them from the jar-s of Tomcat 5.x or Tomcat 4.x), and copy them into your webapp's WEB-INF/classes/... directory: javax.servlet.jsp.tagext.IterationTag, javax.servlet.jsp.tagext.TryCatchFinally, javax.servlet.ServletContextListener, javax.servlet.ServletContextAttributeListener, javax.servlet.http.HttpSessionAttributeListener, javax.servlet.http.HttpSessionListener. But beware, since containers that come with JSP 1.1 usually use earlier Serlvet versions than 2.3, event listeners will not be supported, and thus JSP 1.2 taglibs that register event listeners will not work properly.

As of this writing, JSP features up to JSP 2.1 are implemented, except the "tag files" feature of JSP 2 (i.e., custom JSP tags implemented in JSP language). The tag files had to be compiled to Java classes to be usable under FreeMarker.

Embed FTL into JSP pages

There is a taglib that allows you to put FTL fragments into JSP pages. The embedded FTL fragment can access the attributes (Beans) of the 4 JSP scopes. You can find a working example and the taglib in the FreeMarker distribution.


Page generated: 2008-08-31 23:11:23 GMT FreeMarker Manual -- For FreeMarker 2.3.14