SITE
  Documentation
  Download
  License
  Performance
  Mailing List

FACADE
  Documentation
  Download
  License
  Mailing List

STORE
  Overview
  Tools
  Download
  License
  Mailing List

JUNIT EXTENSIONS
  Documentation
  Download
  License

AMANDA CD-RW
TAPER

  Overview
  Installation & Usage
  Internals

SiTE - Simple Template Engine

1. Building SiTE

If you're using the binary distribution you don't have to build anything. Simply include the "site.jar" in your classpath.

The source distribution contains several directories, some of which have build.xml scripts that can be used with ANT. Just run

        ant compile

in the top-level directory of the distribution to compile the API classes and create the "site.jar". JDK 1.2 or higher is required for SiTE.

To run the unit tests in the "test" subdirectory you'll need the JUnit module for NetBeans.

For the "ULoseXMLC" demo class (which we used to compare SiTE to XMLC) you need the Enhydra Multiserver.

Please note that you don't need either JUnit or Enhydra to use SiTE - these were only used to run some tests. SiTE itself was designed to run without any additional libraries!

2. Using SiTE

2.1 How does SiTE work?

SiTE parses a template and generates a document representation. The document is represented as a tree structure. The application then modifies the document tree. Finally, the document tree is converted back into text.

SiTE generates a SiTE node in the document tree for every area which has to be manipulated. Everything else is stored in BLOB nodes. In other words, BLOB nodes are containers for any kind of data between SiTE nodes. BLOB nodes are always leaf nodes.

Conceptionally, what you do with SiTE does not differ much from what you can do with a text editor. You mark an area in your template. You are then able to cut (i. e. remove) the area, you can copy it or replace it with anything you like. In SiTE, the marked area is represented as a node.

2.2 Standard template syntax

The SiTE template syntax is configurable. So if you do not like the standard syntax you are free to define another template syntax. You can do so by using and configuring the ConfigurableParser or ConfigurableHtmlParser class. There are four parser classes contained in the SiTE distribution. The SimpleTextParser is the fastest parser and provides no convenience template syntax for HTML. The ConfigurableHtmlParser is a parser with a configurable syntax and convenience support for HTML templates but is not as fast as the SimpleTextParser. (All parsers are a lot faster than JSP recompilation.)

2.2.1 SimpleTextParser and ConfigurableTextParser

Each area which has to be manipulated is marked with a <SITE SNAME="manipulateme"> tag at the beginning and a </SITE> at the end.

For instance an email template may look like this:

	Dear <SITE SNAME="customerName">example customer</SITE>,

	have you seen our new template engine? .......

SiTE then builds a document tree from the template. Take a look at the document tree for this template. Now, what would you do to replace the customer name? You will need to replace the customer name area which is represented as a node that contains "example customer" with a BLOB node which contains the current customer's name:

    // access the node named "customerName"
    customerNameNode = myMailTemplate.getNode("customerName");
    // replace all of its child nodes with a new blob node containing
    // "Jim Solaris"
    customerNameNode.replaceAll("Jim Solaris");

If you convert myMailTemplate to a string, the result will be:

	Dear Jim Solaris,

	have you seen our new template engine? .......

As you can see the SiTE template contains absolutely no application code. This is good because you can change your template in any way you can think of. As long as the template still contains the SiTE tags they will be replaced by the customer's name, no matter if your template is in HTML, XML, WML, LaTeX, plaintext or any other format. Plus your template designers don't have to care about any Java-code or custom scripting language code which messes up their templates and makes maintenance expensive.

With the ConfigurableTextParser it is possible to define your own template syntax. If you do not like the idea of including <SITE>-tags you can change the template syntax:

	Dear <!--SITE MYATTRIBUTE="customerName"-->example customer<!--/SITE-->,

	have you seen our new template engine? .......

2.2.2 SimpleHtmlParser and ConfigurableHtmlParser

These parsers support an extended syntax in order to make it more convenient to work with HTML. Let's say you have an anchor element in an HTML-template and want to modify the HREF attribute and the link's text. In this case you can use the SNAME-attribute:

	<a href="mylink.html" SNAME="link">my link text</a>

(Note that the attribute values must be enclosed in double quotes!)

Take a look at the document tree.

You can then modify the HREF and any other attribute of the anchor tag and the link text via the SiTE-API.

Note: If you mark a *ML tag using the SNAME-attribute which contains other *ML tags, you can only access the attributes of the marked *ML tag, not its content. In the following example you will only be able to modify the anchor tag's attributes:

	<a href="mylink.html" SNAME="link"><b>my link text</b></a>

Take a look at the document tree.

A solution for this is to provide additonal SITE tags

	<a href="mylink.html" SNAME="link"><b><SITE SNAME="text"
            >my link text</SITE></b></a>

another solution is to rearrange your HTML

	<b><a href="mylink.html" SNAME="link">my link text</a></b>

Take a look at the document tree.

2.3 An example

We have a HTML-page containing a list template. The list consists of two entry types. One for external links colored red and one for internal links colored green. The application's task is to fill the list with data provided from some data source.

2.3.1 Template

entry 1 link
entry 2 external link

The HTML code:

	<table border=2>
	<tr>
	<td bgcolor=#00ff00>entry 1</td>
	<td bgcolor=#00ff00><a 
				href=locallink.html>link</a
			></td>
	</tr>
	<tr>
	<td bgcolor=#ff0000>entry 2</td>
	<td bgcolor=#ff0000><a 
				href=http://www.tivano.de/>external link</a
			></td>
	</tr>
	</table>

Now let's get it working together with SiTE. We need to mark the table rows (we want to copy them for each entry) and the HTML-tags which need to be filled with data:

	<table border=2>
	<SITE SNAME="localRow">
	<tr>
	<td bgcolor="#00ff00" SNAME="rowEntry">entry 1</td>
	<td bgcolor=#00ff00><a 
				href="locallink.html" SNAME="link">link</a
			></td>
	</tr>
	</SITE>
	<SITE SNAME="extRow">
	<tr>
	<td bgcolor="#ff0000" SNAME="rowEntry">entry 2</td>
	<td bgcolor=#ff0000><a href="http://www.tivano.de/" 
				SNAME="link">external link</a
			></td>
	</tr>
	</SITE>
	</table>

The SITE template nested in this document:

entry 1 link
entry 2 external link

Take a look at the document tree.

2.3.2 Application

The processing of a template takes four steps:

  1. initialize the TemplateFactory
  2. fetch the template
  3. manipulate the template
  4. output the template

2.3.2.1 Initialize the TemplateFactory

The TemplateFactory manages all templates. Getting templates via a TemplateFactory is the preferred method, because the factory automatically handles the underlying storage system, parsing and caching. In most cases the TemplateFactory will be initialized with a properties object containing the template directory configuration:

	TemplateFactory gtf =
			TemplateFactory.getTemplateFactory(
                            "/my/properties/file.properties");

The TemplateFactory can be configured in many ways. For now just remember it needs to be initialized. The configuration will be explained in detail in the architecture and configuration section.

2.3.2.2 Fetch a template

The TemplateFactory returns a template-tree. You can access the template tree via its root node. The flowchart for getTemplate is here.

	Node rootNode = (Node) gtf.getTemplate("mytemplate.html");

2.3.2.3 Manipulate the template

Template manipulation is nothing but tree manipulation. SiTE builds a tree structure for every template.

The TemplateFactory returns the root node for a template. You can access any child node with a name (any node with SNAME="nodename") via the node.getNode("nodename") function. So you can access any named node of the template via the rootNode.

The code for filling the template from the example above looks like this: (The complete servlet source code is available in the distribution in the examples directory.)

       try {
            // get the template from the GenericTemplateFactory
            Node rootNode = (Node) gtf.getTemplate(this.template);        

            // you would normally call a business function here
            MyLink[] myData = this.getData();

            Node localLinkRow = rootNode.getNode("localRow");
            Node extLinkRow = rootNode.getNode("extRow");
            Node newNode;

            for (int i = 0; i < myData.length; i++) {

                    // choose the node to copy, depending on the link type
                    if (myData[i].isLocalLink()) {
                            newNode = localLinkRow.copy();
                    } else {
                            newNode = extLinkRow.copy();
                    }

                    // set the text for first column
                    Node rowEntry = newNode.getChild("rowEntry");
                    rowEntry.replaceAll(myData[i].getText());

                    // set the link's href 
                    Node link = newNode.getChild("link");
                    link.setAttribute("href", myData[i].getHref());

                    // set link text
                    link.replaceAll(myData[i].getLinkText());

                    // insert new node in template
                    rootNode.insertBefore(localLinkRow, newNode);

            }

            // remove unneeded nodes
            rootNode.removeNode(localLinkRow);
            rootNode.removeNode(extLinkRow);

            response.setContentType(contentType);
            outputTemplate(request, response, rootNode);

        } catch (RecompilationException e) {
            throw new ServletException(e);
        }
        

2.3.2.4 output the template

At the end you just need to convert the template to string and write it to your output stream:

	myStream.print(rootNode.toString());

2.3.2.5 code reuse

You can reuse the Java code above for any template matching the structure of the original template. Excellent examples for this are the url rewriting and the automatic form filling classes. The de.tivano.site.util.FormFiller class for example recognizes different form input elements and automatically fills in values provided by the HttpServletRequest or a Hashtable. This becomes a very powerful framework when used together with Facade, Tivano's web application framework. Using Facade you can associate handler classes with SiTE nodes.

2.4 Meta information

After parsing a template, the TemplateFactory can check if it fulfills certain constraints. The constraints could for instance require that a template contains a SITE tag with SNAME="customerName".

Meta information is useful if you want to check if your templates match a structure your application expects it to have (if it doesn't, the result of the application processing the template may be broken). This provides a simple form of automated quality assurance for your templates.

2.4.1 TAGSPEC and ATTRSPEC

ATTRSPECs define a set of allowed values for an attribute. It is possible to define global ATTRSPECs and ATTRSPECs that apply within certain tags only.

A TAGSPEC defines several things:

  • The template must contain a tag matching it.
  • Any tag matching it must contain other tags which match the TAGSPECs enclosed in it.
  • Any tag matching it must contain additional attributes as defined in the NAMELIST part.
  • Attributes of tags enclosed in a matching tag may only contain the values defined in the ATTRSPEC part.

2.4.2 Metainfo grammar

    ALPHA     := 'A' ... 'Z' 'a' ... 'z' '_'
    DIGIT     := '0' ... '9'
    ALPHANUM  := ALPHA | DIGIT | '-'
    SPECIAL   := ' ' | ':' | '.' | ',' | ';'
    WS        := ' ' | <tab> | <newline>
    OPTWS     := WS*
    SOMEWS    := WS OPTWS
    NAME      := ALPHA ALPHANUM*
    NAMELIST  := '(' OPTWS NAME (SOMEWS NAME)* OPTWS ')'
    VALUE     := '"' (ALPHANUM | SPECIAL)* '"'
    VALUELIST := '(' OPTWS VALUE (SOMEWS VALUE)* OPTWS ')'
    ATTRSPEC  := NAME OPTWS ':' OPTWS VALUELIST
    ATTRVALUE := NAME '=' VALUE
    TAGSELECT := '(' ATTRVALUE (SOMEWS ATTRVALUE)* ')'
    TAGSPEC   := TAGSELECT OPTWS ':' OPTWS NAMELIST OPTWS '(' METAINFO ')'
    METAINFO  := (ATTRSPEC | OPTWS) (SOMEWS ATTRSPEC)* (SOMEWS TAGSPEC)* OPTWS

2.4.3 Example

Consider the following meta information:

    ART: ("title" "something")
    (NAME="row") : () (
        (NAME="col1") : () ()
        (NAME="col2") : () ()
    )

The first row is an ATTRSPEC. All attributes ART must contain either the value "title" or "something".

The other rows form a TAGSPEC. They require a tag with NAME=row to be present in the template. The row tag must contain two tags, one named "col1" and another named "col2".

3. Installation and configuration

3.1 Add SiTE to your CLASSPATH

In order to use SiTE in your application, you will have to add the site.jar to your application CLASSPATH.

3.2 Configuration

SiTE is configured by Java properties. In order to understand the configuration, you will need to understand the basic architecture of SiTE.

3.2.1 SiTE componentes

The central object in SiTE is the TemplateFactory. The TemplateFactory uses four interfaces:

  • SourceRepository
  • A SourceRepository fetches template files from a data source (like the filesystem or a database) and delivers them to the TemplateFactory which then calls the TemplateParser. The default SourceRepository is the FilesystemSourceRepository which fetches template files from the file system.

  • MetaRepository (optional)
  • A MetaRepository stores constraints in the form of MetaInfo objects. See section 2.4 for a description of meta information.

  • TemplateParser
  • A TemplateParser parses template source code (as delivered by a SourceRepository) and builds a document tree which represents the template. SiTE comes with two parsers, a SimpleTextParser and a SimpleHtmlParser (see section 2.2).

  • TemplateCache (optional)
  • A TemplateCache stores document representations (as returned by the TemplateParser). The TemplateFactory tries to retreive a template from the TemplateCache before it calls the TemplateParser.

    You can provide your own implementations of these interfaces, or simply use some of the default implementations (which may not always suit your needs). See the following section on how to integrate your own implementations with the TemplateFactory.

    3.2.2 Basic configuration properties

    The TemplateFactory.getTemplateFactory() method by default creates a de.tivano.site.generic.GenericTemplateFactory and either takes a properties object or the filename of a properties file as an argument. The properties define which classes the TemplateFactory should use as its TemplateParser, SourceRepository, MetaRepository and TemplateCache:

        //linebreaks for better legibility
        de.tivano.site.generic.genericTemplateFactory.parser=
                de.tivano.site.generic.SimpleTextParser
        
        de.tivano.site.generic.genericTemplateFactory.cache=
            de.tivano.site.generic.ProbabilisticCache
    
        de.tivano.site.generic.genericTemplateFactory.SourceRepository=
            de.tivano.site.generic.FilesystemSourceRepository
    
        de.tivano.site.generic.genericTemplateFactory.MetaRepository=
            de.tivano.site.generic.FilesystemMetaRepository
    

    The FilesystemSourceRepository and FilesystemMetaRepository require additional parameters, namely the filesystem paths where to look for the files:

        //linebreaks for better legibility
        de.tivano.site.generic.FilesystemMetaRepository.BasePath=
            /path/to/metainfo
        
        de.tivano.site.generic.FileSystemSourceRepository.BasePath=
            /path/to/templates
    

    3.2.3 Optional configuration properties

    Be sure to take a look at the javadoc.

        // Defines the extension for meta-information files
        de.tivano.site.generic.FilesystemMetaRepository.Extension
    
        // Defines the base path to cached files if using
        // the FilesystemPersistentCache
        de.tivano.site.generic.FilesystemPersistentCache.BasePath
    
        // Defines the extension for persistent cache files
        de.tivano.site.generic.FilesystemPersistentCache.Extension
    
        // Defines the high water mark for the probailistic cache
        // Refer to javadoc of PropabilisticCache
        de.tivano.site.generic.ProbabilisticCache.HighWater
        
        // Defines the high water mark for the probabilistic cache
        // Refer to javadoc of PropabilisticCache
        de.tivano.site.generic.ProbabilisticCache.LowWater
    
        // Defines the class of an underlying cache.
        // You might want to use the FilesystemPersistentCache
        de.tivano.site.generic.ProbabilisticCache.SubCache
    
        // When set a SiTE node is generated for several HTML-tags
        // which use attributes like HREF, SRC or ACTION
        // This makes URL-rewriting easy.
        de.tivano.site.generic.SimpleHtmlParser.UseHrefAndSrc
    
        // You can configure the SimpleTextParser's attribute name here.
        // The default is SNAME
        de.tivano.site.generic.SimpleTextParser.NameAttribute
    
        // You can configure the SimpleTextParser's tag name here.
        // The default is SITE
        de.tivano.site.generic.SimpleTextParser.TagName
    
        // Configure the syntax of ConfigurableParser and 
        // ConfigurableHtmlParser. 
        de.tivano.site.generic.ConfigurableParser.OpeningTagStart
        de.tivano.site.generic.ConfigurableParser.OpeningTagEnd
        de.tivano.site.generic.ConfigurableParser.ClosingTagStart
        de.tivano.site.generic.ConfigurableParser.ClosingTagEnd
    
        // When set a SiTE node is generated for several HTML-tags
        // which use attributes like HREF, SRC or ACTION
        // This makes URL-rewriting easy.
        de.tivano.site.generic.ConfigurableHtmlParser.UseHrefAndSrc
    
      
    


    [ t]ivano software gmbh
    www.tivano.de