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.
Download jstp-1.0.0.jar, save it in your 'classpath', or into Ant lib directory. Create a JSTP file HelloWorld.jstp:
Hello World!
java net.sf.jstp.JstpTranslator HelloWorld.jstp
<taskdef name="jstp" classname="net.sf.jstp.JstpAntTask"/> <jstp><fileset dir="." includes="**/*.jstp"/></jstp>
public class HelloWorld { public void render(java.io.PrintWriter out) { out.print("Hello World!\r\n"); } }
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.
The package of the generate java class can either be specified in page directive
<%@ page package="com.foo" %>
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" %>
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 %>
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.
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); }
<%@ page extends="XXXTemplate" %> Hello <%= name %>
public class Bar extends XXXTemplate { public void render(java.io.PrintWriter out) { out.print("Hello "); out.print(String.valueOf(name)); ... } }
XXXTemplate t = new Bar(); t.name = "John"; t.render(..);
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
By default the rendering method signature is
public void render(java.io.PrintWriter out)
out.print("..");
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 %>
public void genMessage(StringBuffer buf) { buf.append("Hello "); buf.append(Util.escape(name)); ... }
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" %>
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!
Hello World!
Hello <% out.flush(); %> World!
The exact gobbling policy is
(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.
As a summary, following are attributes of page directive
Attribute | Description | default | example |
---|---|---|---|
package | the package name of the generated java class. | none (however see autopackage option of Ant Task) | "com.foo" |
extends | the super class name of the generated java class | none | "com.foo.RegistrationEmailTemplate" |
render-method | the signature of render method | "public void render(java.io.PrintWriter out)" | "public void genMessage(StringBuffer buf)" |
text-wrapper | how to render template texts | "out.print" | "buf.append" |
expr-wrapper | how to convert expression to String | "String.valueOf" | "Util.escape" |
ws-gobbling | whitespace gobbling | "true" | |
import | comma seperated import list | none | "java.util.Date, java.util.List" |
To translate a jstp by command line
java net.sf.jstp.JstpTranslator jstpFile [javaFile]
The feature is minimal; Ant task is recommended in development.
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>
Option | Description |
---|---|
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"/> |
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 |