Random musings from my awakening dementia...
06.12.2004  
Web Publishing with Ant
 

I'm quite interested in the concept of software components and how those ideas can be applied to Java code. Thoughts or ideas I have on this subject get dropped here for the benefit of humanity and my own hubris.

© 2004-2005, Howard Abrams



Except where otherwise noted, all original content is licensed under a Creative Commons License.
See details.

I’ve noticed that with a lot of the dynamic web sites that I make, could find a significant speed improvement with a cache. For instance, I like separating the content (the text body) from the layout (the graphics and whatnot), and then combining the two to form a page using a template engine.

But this process of combining doesn’t need to be done every time someone visits the web site. I might as well do it beforehand and then just serve up the static results… a pre-cache if you will.

Download my WebTemplate Ant Task I often build applications using Ant, and one of Ant’s strengths is its ability to extend it with new tasks… so I create a new task to create a web site from data and templates. Let me describe this program by way of an example…

Suppose that you had a number of web pages that were devoid of any layout or navigational links. It just contained the body. Let’s start with the following three pages:

  • htmldata/index.htm
  • htmldata/services.htm
  • htmldata/solutions.htm

Suppose that the index.htm file had the following contents:

<title>Simple Example</title>
<body>
      <p>
        This is just a simple <em>example</em> of simple
        data-only content. It will be inserted into a
        prettier HTML page.
      </p>
</body>

Pretty simple to maintain. Let’s now make the templates that will take this content and turn it into a better looking (and more navigable) web page.

  • htmldata/toptemplate.st
  • htmldata/footer.st
  • htmldata/header.st

The first decision is what template engine (and its attendant syntax) we are going to use. I’ve talked about my perspectives on this topic before, but in this example, we’ll use my new favorite StringTemplate. So we’ll make toptemplate.st look like the following:

<html>
   <head>
        <title>My Site: $title$</title>
        <link rel="stylesheet" href="/styles-site.css"/>
   </head>
   <body>
        $header()$
        $content$
        $footer()$
   </body>
</html>

I’m keeping it pretty simple for illustrative purposes, but you’ll notice some funny tags, like $title$ which will get replaced by the text in the original document’s <title>...</title> section and $content$ gets replaced by the content within the <body>...</body> tags.

The tag, $header()$ takes the contents of the header.st template file and inserts them (substituting any $...$ tags it encounters). The header.st file contains just this:

<h1 align="center">$title$</h1>

And the footer.st file contains this:

     <div align="center">
       &copy; Copyright $year$, $author$ <br/>
       Latest update: $filedate$
      </div>

The big advantage to this approach is that if I want to change the look-and-feel of the web site, I just modify the templates and every resulting web page gets converted. After going through my “process” I will end up with the following destination files:

  • html/index.html
  • html/services.html
  • html/solutions.html

The index.html file will, after this build process, look like the following:

<html>
  <head>
    <title>My Site: Simple Example</title>
    <link rel="stylesheet" href="/styles-site.css"/>
  </head>
  <body>
     <h1 align="center">Simple Example</h1>
    <p>  
      This is just a simple <em>example</em> of simple
      data-only content. It will be inserted into a
      prettier HTML page.  
    </p>  
     <div align="center">
       &copy; Copyright 2004, Howard Abrams <br/>
       Latest update: 12 June 2004
      </div>
  </body>
</html>

That was a long introduction to my build process. Like I said, I extended Ant with a new “webtemplate” task, so my Ant build.xml file looks like this:

<webtemplate src="htmldata" dest="html"
       template="toptemplate" engine="StringTemplate"
       includes="**/*.htm"  ext=".html"
       propertyFile="htmldata/default.properties">
    author=Howard Abrams
</webtemplate>

Let me step through the parameters here, as it isn’t that difficult. The src parameter specifies the directory to look in to get the source files and template files and the dest parameter specifies where the resulting web pages should end up.

The template parameter specifies the primary template file to use when processing. However, that template file may ask for others (like our header.st file), but you only need to state the first one.

The includes parameter tells what files in the src should be processed, and the ext parameter states what the extension to the resulting web pages should contain. This could contain things like php or even jsp if you expect even further processing on the server.

Before I address the propertyFile parameter, let’s talk about that whole author=Howard business. You remember in our footer.st file, it had a tag called $author$, and so we can specify the value of any extra tags between the opening and closing <webtemplate> tags.

What about the $year$ tag that was also in the footer.st file. Glad you brought that up as that will segue nicely into our discussion of the propertyFile parameter. Elsewhere in my build.xml file, let’s include the following:

<propertyfile file="htmldata/default.properties">
    <entry key="year" type="date" operation="=" 
                     value="now" pattern="yyyy"/>
    <entry key="today" type="date" operation="=" 
                     value="now" pattern="dd MMMM yyyy"/>
</propertyfile>

This <propertyfile> is an Ant task that builds a property file that the webtemplate task can read… but it can build these property files dynamically and do various calculations (like inserting the current date) into it.

The only thing left to do is tell the Ant process about my new WebTemplate task extension, so add this to your build.xml file:

<taskdef name="webtemplate" 
          classname="org.howardism.ant.WebTemplate"/>

You might have noticed another tag in the footer.st file that I didn’t explain: $filedate$. There are a number of tags associated with each content file like index.htm that we have. They are:

  • $content$ … The complete guts of the <body>...</body> tags.
  • $title$ … The contents of the <title>...</title> tag.
  • $filedate$ … The date from the system of the last modification of the file.
  • $description$ … The value associated with the header meta file: <meta name="Description" content="..."/>
  • $keywords$ … The value associated with the header meta file: <meta name="Keywords" content="..."/>

Update: The current version (1.2) now supports multiple engines. You can now specify either Velocity or FreeMarker, as in:

<webtemplate src="htmldata" dest="html"
       template="toptemplate.vm" engine="Velocity"
       … />

The biggest difference between using the Velocity or FreeMarker engines as opposed to StringTemplate, is that StringTemplate automatically appends a .st to the end of the template name, while Velocity and FreeMarker require you to specify the full filename. (You may be interested in reading about how you too can add more engines.)


TODO: I would like the Ant build script to be able to gather up even more information to have available to the template engine. For instance, values from an SQL query might be pretty slick. I suppose I could use the <sql>...</sql> Ant task…

A comment to this from the Author

Just expand on this by adding another Ant task to the archive, Text2HTML, which does a very simplistic job of converting a document formatted as a text file, into a simple HTML file. The syntax is very similar:

<text2html src="textdata" dest="htmldata"
    engine="Default" includes="**/*.txt"  ext=".htm" />

In this example, all of the files ending in “txt” in the “textdata” directory will be converted into HTML files and dropped into the “htmldata” directory with an ending of “htm”.

While doing this sort of conversion is mildly interesting, the goal here is to be able to create a complete web site with all of the content stored in easily edited text files, and an Ant build process that first converts the text files to simple HTML, and then merges those HTML files with a rich template to produce an entire site. Interesting idea, no?

Why yes, that is indeed a “engine” parameter that allows me (or anyone else) to extend the task with a simple plugin, for I would like to substitute my simple formatting for something more interesting, like Markdown or Textile (perhaps using the Textile4J project).

Comment posted on Saturday, 19 June 2004
A comment to this from the Author

True to my word, I have expanded my <text2html> Ant task to include other engines. To take convert Textile-formatted files to HTML, you would do something like this:

<text2html src="${src.docs}" dest="${stg.html}"
    engine="Textile"
    includes="**/*.textile, **/*.text"  ext=".htm" />

Pretty simple, eh? Since there isn’t a Java-based version of the Markdown syntax that I could find, I figured that I would just send the text through the Perl script, but after struggling with the Apply task, I finally just decided to expand my “Default” engine to allow a script to be specified:

<text2html src="${src.docs}" dest="${stg.html}"
    engine="Default" script="perl support/Markdown.pl"
    includes="**/*.txt"  ext=".htm" />

Yes, this does mean that you can use it for any sort of text filtering.

Comment posted on Friday, 2 July 2004
Another web page that references this entry...
Loosely Coupled Components, Part 1½
Excerpt:Continuing my discussion of integrating components by using the "Plugin Pattern".
Tracked:June 12, 2004 05:33 PM