Part of the fun of shifting my Blog from Blogger to github has been building the static-site-generator tool which reads my blog content as Markdown and spits it out as html + css.

I'm using MarkdownJ to convert the Markdown syntax to HTML, and FreeMarker for templating.

FreeMarker for templating was an easy choice for me:

  • It is a decent templating engine that I've used recently for a dynamic website,
  • It is very easy to set up in Java for my one-hit templating needs,
  • I like its syntax compared to, say, Velocity

Yesterday I used FreeMarker to add two new productions to my site-generator: a sitemap, and an RSS feed, and thought I'd make some quick notes...

FreeMarker strives to be a pure templating language, and as such resists the temptation to ... shall we say, make certain things easy that you probably shouldn't do in a template.

This can make templating a little uncomfortable sometimes - all the more so if you try to use complex domain objects directly in your templates instead of producing model's specifically for output (YMMV depending on the complexity of your domain model of course).

For the simple needs of my Blog FreeMarker has been great, allowing me to whiz up the sitemap template in a couple of minutes, and not much longer for the RSS-2.0 template.

Setting up FreeMarker in Java is very very simple indeed. I chose to abstract FreeMarker away behind my own interface (TemplateProcessor), in case I want to use something different later:

import java.io.*;
import freemarker.template.*;

public class FreemarkerTemplateProcessor implements TemplateProcessor
{
    private Configuration cfg;

    public FreemarkerTemplateProcessor(File aTemplateDir) 
    throws IOException
    {
        cfg = new Configuration();
        cfg.setDirectoryForTemplateLoading(aTemplateDir);
        cfg.setObjectWrapper(new DefaultObjectWrapper());
    }

    @Override
    public void process(Object aModel, String aTemplate, Writer aWriter) 
    throws IOException, TemplateException
    {
        Template _tpl = cfg.getTemplate(aTemplate);
        _tpl.process(aModel, aWriter);
    }
}

The templating language is quite neat, and very easy to write - as I mentioned before, it helps if your model is a good fit for the view you are trying to render so you don't need to squeeze to many conditionals into the template.

Freemarker template for sitemap.xml

There are only 3 lines of FreeMarker markup in my sitemap template:

  • <#list posts as post> which iterates over an Iterable containing Post objects,
  • the closing </#list>, and
  • the line that reads the post's link <loc>${blog.url}/${post.link}</loc>

Here's the template in full:

<?xml version="1.0" encoding="UTF-8"?> 
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation=
        "http://www.sitemaps.org/schemas/sitemap/0.9 
        http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
  xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 

  <url>
    <loc>${blog.url}/index.html</loc>
    <changefreq>daily</changefreq>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>${blog.url}/about_me.html</loc>
    <changefreq>daily</changefreq>
    <priority>0.9</priority>
  </url>

  <#list posts as post>
    <url> 
      <loc>${blog.url}/${post.link}</loc>
      <changefreq>hourly</changefreq>
    </url>
  </#list>

</urlset>

Freemarker template for RSS 2.0

The RSS template is very similar to the sitemap template, as it deals with exactly the same model. Skipping the pre-amble, here's the part that actually involves any freemarker markup:

<#list posts as post>
  <item>
    <title><![CDATA[${post.title}]]></title>
    <link><![CDATA[http://${blog.url}/${post.link}]]></link>
    <guid isPermaLink="true">${blog.url}/${post.link}</guid>
    <description><![CDATA[${post.body}]]></description>
    <#list post.keywords as keyword>
      <category>${keyword}</category>
    </#list>
    <dc:creator><![CDATA[ Steve Liles ]]></dc:creator>
    <pubDate>${post.date?string("EEE, dd MMM yyyy HH:mm:ss Z")}</pubDate>
  </item>
</#list>

I like the date built-in here: ${post.date?string("EEE, dd MMM yyyy HH:mm:ss Z")} which keeps the date format in the template where it belongs.

Of course, here I've just written it directly into this template because I only use this format once in my whole site - if I were using it more I could define a re-usable FreeMarker macro instead to avoid repetition.

blog comments powered by Disqus