Skip to main content

Template Code Generator : Apache Velocity - JET - JET2

Posted by survivant on January 4, 2009 at 1:15 PM PST

I wanted to use a template as input for my Code Generator, but the problem was to find one that worked in a stand alone mode, not a web based one.
In my research a found few that do what I wanted. Apache Velocity and Eclipse JET/JET2.

I'll explain in details theses Template Code Generator with a little application. I'll create a web page as output.

Here the specs for the demo.

- The web page template should create a entry for each items in the list received in parameter
- The list could be created dynamically (so the number of items is not fixed)

Before starting to explain how to do it with theses frameworks, I'll use a class : MediaFile that contains the info on a media file. I'll create a html that display 2 items by row and just to kept the
demo simpler, I'll use a even number of items.

Take a look at the html that I would like to generate.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
  <head>
    <!-- include the required JavaScript file -->
  </head>

<body>
  <div align="center">
    <table width="80%">
        <tr>
            <td>
              <a href="name1" id="player1">name1
              </a>
            </td>
            <td>
              <a href="name2" id="player2">name2
              </a>
            </td>
        </tr>
        <tr>
            <td>
              <a href="name3" id="player3">name3
              </a>
            </td>
            <td>
              <a href="name4" id="player4">name4
              </a>
            </td>
        </tr>
        <tr>
            <td>
              <a href="name5" id="player5">name5
              </a>
            </td>
            <td>
              <a href="name6" id="player6">name6
              </a>
            </td>
        </tr>
    </table>
  </div>
  </body>
</html>

I didn't put and on the same line on purpose. The reason it because it's easier to show the format's problems with a code generator. I'll explain it in more details later.

The MediaFile's list will be populated like that :

public void init() {

    mediaFileList = new ArrayList();

    for (int i = 0; i < 5; i++) {
      MediaFile mediaFile = new MediaFile();
      mediaFile.setName("name" + i);
      mediaFile.setFps("29.997");
      mediaFile.setDuration("12:45");
      mediaFile.setCodec("H264");
      mediaFile.setWidth("320");
      mediaFile.setHeight("240");
      mediaFile.setStreamSize("2 Megs");

      mediaFileList.add(mediaFile);
    }

  }

Apache Velocity

I known the framework by name, but I never used before. I have to say that it wasn't too hard to get it to work.
Apache have a good documentation for Velocity API. If you are not familiar with this API, go take a look at the user guide
http://velocity.apache.org/engine/devel/user-guide.html

Apache Velocity use it's own language, but it's pretty simple. What I didn't like at first was the lack of nested loop.

I wanted to do something like that (using the iterator in a nested loop):

[prettify] for(Iterator it = list.iterator();it.hasNext();){    ...       for(int i=0;i&lt;2;i++){         Object element = iterator.next();         ...       }       ... } [/prettify]
I'm not a expert in Velocity, but after looking around on the web, I didn't find a simple answer, so I had to do it another way.
The problem with a template like below, is that it could become hard to read, because if you let some blank lines for the readiness of the code, theses lines will appear in the generated code.

Let's take a look at the template in Velocity that will give the desirable layout.

GalleryTemplateVelocity.vm
[prettify] &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt; &lt;html&gt;   &lt;head&gt;     &lt;!-- include the required JavaScript file --&gt;   &lt;/head&gt; &lt;body&gt;   &lt;div align="center"&gt;     &lt;table width="80%"&gt; #set( $maxItembyRow = 2) #set( $index = 0) #set( $newLine = true) #foreach( $mediaFile in $list ) #if( $newLine )     &lt;tr&gt; #set( $newLine = false) #end #if( $index&lt;$maxItembyRow )         &lt;td&gt;&lt;a href="$mediaFile.name" id="player$velocityCount"&gt;$mediaFile.name             &lt;/a&gt;         &lt;/td&gt; #set($index = $index+1) #else #set($index = 0) #set( $newLine = true) #end #end     &lt;/tr&gt;   &lt;/table&gt;   &lt;/div&gt;   &lt;/body&gt; &lt;/html&gt; [/prettify]
After that we need the main class that will call the template, and populate the list of MediaFile.
If you want to pass variables to Velocity template, you have to put them in the context, like you do for a HttpRequest.

SampleVelocity.java

  public void generate() {

    try {
      Velocity.init();

      VelocityContext context = new VelocityContext();
      context.put("list", mediaFileList);

      Template template = Velocity.getTemplate("./templates/GalleryTemplateVelocity.vm");

      BufferedWriter writer = writer = new BufferedWriter(new OutputStreamWriter(System.out));

      if (template != null)
        template.merge(context, writer);

      /*
       * flush and cleanup
       */

      writer.flush();
      writer.close();
     
    } catch (Exception e) {
      s_logger.error("generate", e);
    }

  }

Eclipse JET

JET can be used as a template code generator. It's was made to run within Eclipse, but you can include two Eclipse jar and that will do the trick.
JET is based on the JSP syntax. If you have done scriplet in JSP, you won't find that hard to use.

The main differences between JEt and Velocity, is that JET only take 1 parameter in the template.
Yes, only one... You can pass that by creating a java class that will contains all your variables. Yes it's ugly, but when you know it, it's not hard to use.

The second big difference is that JET framework compile the template into a java class, and you use this class in your application.

Take a look at the JET template (look like a JSP with the 2 first lines that you related to JET.

GalleryTemplateJet.jet

<%@ jet package="ca.sebastiendionne.gallery.jet" class="GalleryHtmlJET" imports="java.util.* ca.sebastiendionne.gallery.model.*" %>
<% List<MediaFile> mediaFileList = (List<MediaFile>) argument; %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
  <head>
    <!-- include the required JavaScript file -->
  </head>

<body>
  <div align="center">
    <table width="80%">
      <%
     
      int maxItembyRow = 2;
      int index = 0;
     
      for (Iterator<MediaFile> iterator=mediaFileList.iterator();iterator.hasNext();) {%>
        <tr>
        <%
        for(int j=0;j<maxItembyRow;j++){
          MediaFile element = iterator.next();
          %>
            <td>
            <a href="<%=element.getName()%>" id="player<%=index%>"><%=element.getName()%>
          </a>
           
            <%=element%>
           
            </td>
          <%
          index++;
        }%>
        </tr>
        <%
      }
      %>
    </table>
  </div>
  </body>
</html>

That will generate the java class : ca.sebastiendionne.gallery.jet.GalleryHtmlJET

and you use it like that :

  public void generate(){
   
    GalleryHtmlJET galleryJET = new GalleryHtmlJET();
   
    String html = galleryJET.generate(mediaFileList);  // the argument is the list
   
    System.out.println(html);
  }

The method generate() is simpler and smaller than Velocity, but in other hands... The formatting is more tricky with JSP syntax. Try for fun to look at a html page generated with scriptlet. It's will works, but
there will be lot of blank lines and it won't have a nice indentation. That is important when you want to generate java class. You don't want to pass a formatter after the generating job.

I had used JET few months ago for my application IbatisGen. I think that it took more time to get the indentation right that coding the rest of the application.

JET have some limitations like the one argument, but that JET2 fixed that.

Eclipse JET2

JET2 differs than JET1 in different ways. JET1 can be used outside Eclipse, but not JET2.

JET2 can be easily use in Eclipse if you have a xml file as input, but if you don't have a fixed input like this demo. It's more complicated.
You will have to create a Eclipse Plugin that will be able to handle dynamic input.

So, if you don't want to create a Eclipse Plugin, stop here, take JET or Velocity.

I won't cover how to create the JET2 Plugin, you can find a good sample here http://www.eclipse.org/articles/Article-JET2/jet_tutorial2.html, but I'll show you how to use a xml input file.

The JET2 templates can you JSP and JSLT syntax like.

sample.xml

<app class="Car">
  <property name="model" type="String" initial="Hybrid" />
  <property name="horsepower" type="int" initial="140" />
  <property name="spareTires" type="boolean" initial="true" />
</app>

The main template (like the java class that contains the main() )

main.jet

<%@taglib prefix="ws" id="org.eclipse.jet.workspaceTags" %>
<c:if test="isVariableDefined('org.eclipse.jet.resource.project.name')">
    <ws:file template="templates/testCar.jet" path="{$org.eclipse.jet.resource.project.name}/dump2.xml"/>
</c:if>

testCar.jet (the template that will generate the code from sample.xml) (We are creating a getter/setter class)

<%@taglib prefix="ws" id="org.eclipse.jet.workspaceTags" %>
class <c:get select="/app/@class" /> {
<c:iterate select="/app/property" var="p" >
  private <c:get select="$p/@type" /> <c:get select="$p/@name" />;
</c:iterate>

  public <c:get select="/app/@class" />() {
  <c:iterate select="/app/property" var="p" >
    this.<c:get select="$p/@name" /> = <c:choose select="$p/@type" >
    <c:when test="'String'">"<c:get select="$p/@initial" />"</c:when>
    <c:otherwise><c:get select="$p/@initial" /></c:otherwise>
    </c:choose>
;
  </c:iterate>
  }

<c:iterate select="/app/property" var="p" >
  public void set<c:get select="camelCase($p/@name)" />(<c:get select="$p/@type" /> <c:get select="$p/@name" />) {
    System.out.println("In set<c:get select="camelCase($p/@name)" />()");
    this.<c:get select="$p/@name" /> = <c:get select="$p/@name" />;
  }
 
  public <c:get select="$p/@type" /> get<c:get select="camelCase($p/@name)" />() {
    System.out.println("In get<c:get select="camelCase($p/@name)" />()");
    return <c:get select="$p/@name" />;
  }
 
</c:iterate>
}

To run all that you need to "Run as ... JET Transformation" in Eclipse.

I won't go into details, but here what the generate() method should look like if you want to handle a dynamic input.

public void generate(){
   
    GalleryHtmlJET galleryJET = new GalleryHtmlJET();
   
    JET2Context context = new JET2Context(null);
    context.setVariable("list", mediaFileList);
   
    JET2Writer out = new BodyContentWriter();
   
    context.setTagFactory(new TagFactoryImpl(context));
   
    ... the line for the Plugin
      galleryJET.generate(context, out);
    ... end of the Plugin stuff
    System.out.println(out);
   
  }

It's look really like Apache Velocity at this point.

If I had to choose between them, I don't know if I would choose Velocity or JET. Both of them could work in a plugin (Eclipse or Netbean), support Ant task.

What do you think ? Do you know some framework that you want to share with us ?

You can download the source code here : Velocity/JET and JET2.

Comments

I try FreeMarker. At first it's not easy to configure. Velocity is easier. I try that in FreeMarker try { Configuration cfg = new Configuration(); Template tpl = cfg.getTemplate("./templates/GalleryTemplateFreeMarker.ftl"); OutputStreamWriter output = new OutputStreamWriter(System.out); SimpleHash root = new SimpleHash(); TemplateModelListSequence list = new TemplateModelListSequence(mediaFileList); root.put("list", list); tpl.process(root, output); } catch (Exception e) { s_logger.error("generate", e); } and I got this when I run the template : java.lang.ClassCastException: ca.sebastiendionne.gallery.model.MediaFile cannot be cast to freemarker.template.TemplateModel if I create MediaFileFreemarker that implements TemplateModel.. I get a new exception : freemarker.template.TemplateException: Expected hash. mediaFile evaluated instead to ca.sebastiendionne.gallery.model.MediaFileFreeMarker on line 13, column 81 in templates/GalleryTemplateFreeMarker.ftl. and there isn't lot of documentation for FreeMarker. Can someone help me with that ?

Java in JET

I have searched and searched and searched, yet I have not found any examples to match my curiosity. So many pages show the way for java to generate code. However is there a way to embed java into jet files so when INput for jet transformation is ran, it runs the java?
basically:
I have jet files that builds code. I want to put a call to a already defined java object inside the jet files so that when it is run, it will go out, run the java code, return what is returned and put it on the code that is generated?
example:
*--------------------------------------------------------------*
* LINKAGE *
*--------------------------------------------------------------*
LINKAGE SECTION.

01 DFHCOMMAREA.
03 DFH-SQLCODE PIC S9(09).
<%!
public String runThisFunction() {
String str = com.package.dao.JetSYSIBMAccess.retrieveFunction();
return str;
}
%>
<%=runThisFunction() %>

and the function returns a string "05 DFH-3RD-LVL PIC X(32)." any ideas?

Good question. Never try.

Good question. Never try. JET should be use to generate something.. but I don't think it have to execute option. If you compare to Jasper, it compile the JSP and run it after that..

I like StringTemplate http://www.stringtemplate.org/. It has a nice syntax and a clear model/view separation. One of my favorite things is that you don't have to write loops, you just choose a template to apply to all the elements in a collection. Your two column table could be down with an alternating templates. http://www.antlr.org/wiki/display/ST/StringTemplate+Documentation

I will definitely try FreeMarker. I'll add that in another version of this post. I tough FreeMarker was a JSP remplacement, I didn't know it could be used in a standalone. I learn something new today :) thanks

What I don't like about Groovy, Ruby.. and others frameworks like that, is that is not Java. They could be powerful, but I prefer using a framework that is pure Java. It's easier to find auto-completion, syntax validation in all IDE. I did some work on Groovy long time ago.. maybe I should give it a try again.

you can try also freemarker http://freemarker.sourceforge.net/ I have use both of them jet,freemarker,velocity, freemarker is very light when you compare it to jet framework and more featured than velocity have fun

Have you checked Groovy templates (http://groovy.codehaus.org/Groovy+Templates)