Introduction

JSTP is basically a subset of JSP, and a build time translator of it.

The nice thing about JSP is everybody knows it, and all modern IDE support it. The bad thing is it's heavy weight, you can only use it in a servlet container, inside a HTTP request/response cycle. That severely limits its availability as a template solution in many other situations.

JSTP discards things in JSP that depend on Servlet. The remaining part is pure template solution. A JSTP page looks very much like a JSP page; it is translated into a simple java class at build time, and used at runtime as a simple class without any dependencies.

Note However JSTP was not set out to "fix" JSP. I wanted a simple, intuitive, lightweight, statically typed template language. There were some already, unfortunately they don't have IDE supports at the level that JSP is supported in modern IDE, like find usages, refactoring, code completion etc. In my work environment we perform constant refactoring, without IDE support it's pretty tedious. I guess it's not yet easy for IDEs to understand a new language, the solution left is to adopt a subset of JSP syntax so that IDEs can be fooled to treat JSTP as if it's JSP.

Hello World

Download jstp-1.0.0.jar, save it in your 'classpath', or into Ant lib directory. Create a JSTP file HelloWorld.jstp:

    Hello World!  
Translate it by command line
    java net.sf.jstp.JstpTranslator HelloWorld.jstp 
or by Ant task
    <taskdef name="jstp" classname="net.sf.jstp.JstpAntTask"/>
    <jstp><fileset dir="." includes="**/*.jstp"/></jstp> 
either way a HelloWorld.java file is created
    public class HelloWorld                                     
    {                                                          
        public void render(java.io.PrintWriter out)            
        {                                                       
            out.print("Hello World!\r\n");                      
        }                                                       
    }

Syntax

Bottom line, just start writing JSTP pages like JSP and don't worry about it, you'll be fine mostly. Don't forget to configure your IDE to treat .jstp files as JSP files.

Following JSP-like tags are supported

    <%-- comment    --%>
    <%!  declaration  %>
    <%   scriptlet    %>
    <%=  expression   %>
    <%@  directive    %> 

They look and behave like JSP counterparts, except that only 'page' directive is supported, any other directive like <%@include% ...> is a translation error. 'page' directive has different attributes than those in JSP.

Everything else is treated as template text, including other special elements in JSP, like <jsp:useBean/>, taglibs, ${EL} etc. Implicit JSP variables like 'session', 'request' don't make sense in JSTP and are not supported.

Package

The package of the generate java class can either be specified in page directive

    <%@ page package="com.foo" %>  
or automatically deduced by Ant task from directory structure. If neither is available, the class is in the 'default package'.

Imports

Imports are treated the same way in JSP, and your IDE should have taken care of it by adding imports automatically in page directive.

    <%@ page import="java.util.Date,
                     java.util.List"      
    %> 

Parameter Passing

You can declare some instance variables for parameter passing. For example, in Bar.jstp, 'name' is one of its parameter

    <%!                                        
        public String name;                    
    %>                                         
    Hello <%= name %> 
which is translated in Bar.java as
    public class Bar                                        
    {                                                       
        public String name;                                 
        public void render(java.io.PrintWriter out)         
        {                                                   
            out.print("Hello ");                            
            out.print(String.valueOf(name));                
            ...                                             
        }                                                   
    }  

The caller naturally pass a 'name' parameter by

    Bar bar = new Bar();                              
    bar.name = "John";                               
    bar.render(..);       

You can surely declare and use getter/setters if you like.

Super Class

The 'extends' attribute of 'page' directive is supported just as in JSP.

In JSP the super class doesn't make much sense and nobody really uses the 'extends' attribute, however in JSTP you may find it very useful in your type hierarchy. For example, you have XXXTemplate as abstract interface

    public class XXXTemplate                                     
    {                                                            
        public String name;                                      
        abstract public void render(java.io.PrintWriter out);    
    } 
and Bar.jstp is one of its implementation
    <%@ page extends="XXXTemplate" %>          
    Hello <%= name %> 
which results in Bar.java
    public class Bar extends XXXTemplate                    
    {                                                       
        public void render(java.io.PrintWriter out)         
        {                                                   
            out.print("Hello ");                            
            out.print(String.valueOf(name));                
            ...                                             
        }                                                   
    } 
You can use it like
    XXXTemplate t = new Bar();                        
    t.name = "John";                                  
    t.render(..); 

Inclusion

If you need to include another JSTP in one JSTP, remember it's just a normal java class, and you invoke it in the same way:

    blah blah   
    <% new Bar().render(out); //including Bar.jstp %>  
    blah 

Rendering Method

By default the rendering method signature is

    public void render(java.io.PrintWriter out) 
template texts are rendered by
    out.print(".."); 
and expressions are converted to String by
    String.valueOf(..) 

You can change those to anything you like in page directive:

    <%@ page                                                      
        render-method="public void genMessage(StringBuffer buf)"  
        text-wrapper ="buf.append"                                
        expr-wrapper ="Util.escape"                               
    %>                                                            
    Hello <%= name %>   
The result render method will look like
    public void genMessage(StringBuffer buf)      
    {                                             
        buf.append("Hello ");                     
        buf.append(Util.escape(name)); 
        ...
    } 

Whitespace Gobbling

In JSP all whitespaces in template text are reserved. In JSTP some whitespaces are gobbled, and JSTP source pages look much nicer. You can turn the feature off by

    <%@page ws-gobbling="false" %> 
to follow JSP's convention if you must.

If ws-gobbling='true' which is default, you don't have to worry about how exactly it works, just format the JSTP source by common sense; if some whitespaces are gobbled against your intention, you can simply repeat the gobbled whitespaces at the same place. For example in

    Hello                    
    <% out.flush(); %>       
    World! 
The newline after the %> is gobbled, so the output will be
    Hello                    
    World!  
If you don't want that newline to be gobbled, just add a newline
    Hello                    
    <% out.flush(); %>       
                             
    World! 

The exact gobbling policy is

  • if on the same line there are only blanks(space or tab) before a JSTP start tag, those blanks are gobbled.
  • if a JSTP end tag is immediately followed by a newline, or with only blanks between, the newline and the blanks are gobbled.

(the rules don't apply to <%=%> tag)

(same idea by Christoph on Velocity whitespace gobbling)

For example, (. represents space or tab, represents newline)

Hello..
..<%..
....out.flush();..
..%>...                 
World!.. 
the two spaces before <%, the 3 spaces and one newline after %>, are not considered as template text; they are apparently only there to format the source, not the output.

'page' Directive Attributes

As a summary, following are attributes of page directive

AttributeDescriptiondefaultexample
packagethe package name of the generated java class. none

(however see autopackage option of Ant Task)
"com.foo"
extendsthe super class name of the generated java class none"com.foo.RegistrationEmailTemplate"
render-methodthe signature of render method"public void render(java.io.PrintWriter out)" "public void genMessage(StringBuffer buf)"
text-wrapperhow to render template texts"out.print""buf.append"
expr-wrapperhow to convert expression to String "String.valueOf" "Util.escape"
ws-gobblingwhitespace gobbling "true"
importcomma seperated import list none "java.util.Date, java.util.List"

command line translation

To translate a jstp by command line

    java net.sf.jstp.JstpTranslator jstpFile [javaFile] 

The feature is minimal; Ant task is recommended in development.

Ant Task

You must first define the jstp task in Ant,

    <taskdef name="jstp" classname="net.sf.jstp.JstpAntTask"/> 

Specify jstp files to be translated in a <fileset> child:

    <jstp>
        <fileset ... />
    </jstp> 

The task has some options, for example:

    <jstp destdir="tmp" encoding="UTF-8" autopackage="true" overwrite="false" verbose="false" failonerror="true">
        <fileset dir="demo" includes="**/*.jstp"/>
    </jstp> 
The only one really useful to you is probably 'destdir'.

JSTP Ant Task Options

OptionDescription
destdir the destination directory for generated java files. if not specified, java files will be generated in the same directories of source jstp files.
encoding the encoding of jstp and java files. if not specified default platform encoding is assumed.
autopackage 'true' by default. automatically determine package by directory structure. for example, if inputs are
    <fileset dir="demo" includes="**/*.jstp"/> 
the package of demo/doo/foo/Bar.jstp will be "doo.foo", unless Bar.jstp explicitly defines its package in <%@page package='..' %>
overwrite re-generate java file even if it's newer than the jstp file. by default the option is 'false'.
verbose 'false' by default
failonerror 'true' by default


temp counter
My Datanet
My Datanet