Skip to main content

A Dynamic MVC Development Approach Using Java 6 Scripting, Groovy, and WebLEAF

June 19, 2007

{cs.r.title}



A flexible and dynamic development environment is currently a
great concern, and even though scripting languages have shown they
can be of help, we still need to create applications that can be
maintained easily while giving us what we need during development.
Recognizing the usefulness of scripting languages, "http://java.sun.com/javase/6/docs/">Java SE 6 introduced the
new "http://java.sun.com/javase/6/docs/technotes/guides/scripting/">Java
Scripting API
: a language-independent framework that allows
developers to use scripting engines from Java code. With this new
API, we can take advantage of the characteristics of the scripting
languages where we need them most, while being able to use our
well-known Java bag of tools.

In this article, we show an example of new architectures made
possible with this API, developing a web application based on the
Model View Controller (MVC) architecture using "http://groovy.codehaus.org/">Groovy, a dynamic scripting
language based on the Java platform, to implement the business
logic (the "model"), and using different technologies to implement
the interface (the "view"). As the "controller" part of our
application, we use "https://webleaf.dev.java.net/">WebLEAF, an open source
framework to develop web applications based on the MVC
architecture. In order to make the example "self contained," we use
an embeddable database written in Java, "http://hsqldb.org/">HSQLDB, which allows us to create a sample
database with just two text files. To implement the view logic that
generates the HTML user interface, the technologies we use in the
article are XSLT, a
language for transforming XML documents into other XML documents
specified by the W3C, and "http://freemarker.sourceforge.net/">FreeMarker, a popular Java
template engine that can also handle XML as input.

The example we are going to develop consists of a page that
displays a list of the items of a database and allows us to select
one item so we can see its detailed information. It is a fairly
simple example, but enough to see the different pieces of the
architecture we want to demonstrate.

We are going to follow the example step by step, but you can
check the Resources section for the full
sample code.

Setting Up the Application Skeleton

The first step is to create the basic structure of a standard
web application and add the WebLEAF library version 3, as that is
the version that provides support for the new Java Scripting
API:

  1. We start by creating a directory called Test.
  2. We create a directory inside Test called
    WEB-INF.
  3. Inside WEB-INF, we create a directory called lib and
    another directory called classes.
  4. We download the WebLEAF 3 .jar file and put it in
    Test/WEB-INF/lib.
  5. We create the standard servlet descriptor file
    Test/WEB-INF/web.xml as displayed below:
[prettify]<?xml version="1.0"?>
<!DOCTYPE web-app
 PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
<web-app>
 <!--
  WebLEAF controller servlet configuration
 -->
 <servlet>
  <servlet-name>WebLEAFController</servlet-name>
  <servlet-class>org.leaf.LEAFManager</servlet-class>
  <load-on-startup>2</load-on-startup>
 </servlet>
 <!-- End controller servlet -->
</web-app>
[/prettify]

This configuration tells the servlet container to instantiate a
WebLEAF controller servlet and start it up when the context is
started. Pay attention that the <load-on-startup>
value is set to 2. We will see in a minute the reason behind this
not being a 1.

  1. To finish this part, we create the WebLEAF descriptor file,
    Test/WEB-INF/Test.leaf.conf, by copying this:
[prettify]&lt;?xml version="1.0" encoding="iso-8859-1"?&gt;
&lt;!DOCTYPE WADSET SYSTEM "http://www.uib.es/leaf/LEAFwad.dtd"&gt;
&lt;WADSET&gt;
  &lt;WAD
    NAME="Test"
    INIT_MODE="INITS_ON_START_UP"
    XML_ALLOW_SHOW="TRUE"
    XSLT_NO_CACHE="TRUE"
    &gt;
  &lt;/WAD&gt;
&lt;/WADSET&gt;
[/prettify]
That gives us a minimum configuration to start with. But don't
worry, we have just started!

Creating the Database

We are now going to add the database that will be used in our
tests.

  1. As mentioned previously, we will use HSQLDB, so the first steps
    are to download the latest "http://sourceforge.net/project/showfiles.php?group_id=23316&package_id=16653&release_id=339171">
    1.8.0 HSQLDB JDBC driver
    , extract it, and add
    hsqldb.jar to Test/WEB-INF/lib.
  2. After that, we need to create a small test database with some
    dummy data in it; so we add the directory Test/WEB-INF/db
    and put in it two files. The first is named
    Test.properties and has this content:
[prettify]#HSQL Database Engine
#Fri Apr 20 18:52:47 CEST 2007
hsqldb.script_format=0
runtime.gc_interval=0
sql.enforce_strict_size=false
hsqldb.cache_size_scale=10
readonly=false
hsqldb.nio_data_file=true
hsqldb.cache_scale=14
version=1.8.0
hsqldb.default_table_type=memory
hsqldb.cache_file_scale=1
sql.compare_in_locale=false
hsqldb.log_size=200
modified=no
hsqldb.cache_version=1.7.0
hsqldb.original_version=1.7.1
hsqldb.compatible_version=1.8.0
[/prettify]
The second file is called Test.script and has this
content:
[prettify]CREATE SCHEMA PUBLIC AUTHORIZATION DBA
CREATE MEMORY TABLE TTST_ITEM(ITE_CODE CHAR(32) NOT NULL PRIMARY KEY,ITE_NAME VARCHAR(100) NOT NULL,ITE_DESCRIPTION VARCHAR(300))
CREATE USER SA PASSWORD ""
GRANT DBA TO SA
SET WRITE_DELAY 60
SET SCHEMA PUBLIC
INSERT INTO TTST_ITEM VALUES('06e8da2682ce842b01a47de7823ec779','Parents','Links for parents')
INSERT INTO TTST_ITEM VALUES('1ab94df54312961a015749157fe05097','Web related','Item description')
INSERT INTO TTST_ITEM VALUES('1acc5e9d4312961a0157491547be91fa','Source control','That must be about source')
INSERT INTO TTST_ITEM VALUES('1ad80b204312961a015749159fe5205a','JBuilder Plugins','Plugin? What's that')
[/prettify]

As the HSQLDB driver requires a full path that is known only at
deploy time, we will make use of the AutoConfigurer servlet that
comes with WebLEAF to self-configure this path.

  1. To do that, we create the file called
    Test/WEB-INF/classes/webapp.properties with simply this
    line inside:
[prettify]db.location=REAL_PATH/WEB-INF/db
[/prettify]

  1. We then edit Test/WEB-INF/classes/web.xml to add the
    Autoconfigurer servlet, so it ends up as the sample
    below:
[prettify]&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
 &lt;!--
  Autoconfiguration servlet
 --&gt;
 &lt;servlet&gt;
  &lt;servlet-name&gt;AutoConfigurer&lt;/servlet-name&gt;
  &lt;servlet-class&gt;org.leaf.util.AutoConfigurer&lt;/servlet-class&gt;
  &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;FILE_NAME.1&lt;/param-name&gt;
   &lt;param-value&gt;WEB-INF/classes/webapp.properties&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;NODES_NAME.1&lt;/param-name&gt;
   &lt;param-value&gt;db.location&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;PATTERN.1&lt;/param-name&gt;
   &lt;param-value&gt;(.*)/WEB-INF/(.*)&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;FORMAT.1&lt;/param-name&gt;
   &lt;param-value&gt;{0}WEB-INF/{1}&lt;/param-value&gt;
  &lt;/init-param&gt;
 &lt;/servlet&gt;
 &lt;!-- End Autoconfiguration servlet --&gt;
 &lt;!--
  WebLEAF controller servlet configuration
 --&gt;
 &lt;servlet&gt;
  &lt;servlet-name&gt;WebLEAFController&lt;/servlet-name&gt;
  &lt;servlet-class&gt;org.leaf.LEAFManager&lt;/servlet-class&gt;
  &lt;load-on-startup&gt;2&lt;/load-on-startup&gt;
 &lt;/servlet&gt;
 &lt;!-- End controller servlet --&gt;
&lt;/web-app&gt;
[/prettify]

The configuration above tells the Autoconfigurer
servlet to check the webapp.properties file and correct
the db.location property if it does not point to the
proper location. Moreover, the Autoconfigurer servlet
is loaded first, before the WebLEAF controller servlet,
thanks to having a lower load-on-startup value. That
guarantees that the Autoconfigurer servlet will have
the chance to fix the db.location property before the WebLEAF
controller servlet (LEAFManager) tries to access the
database.

Implementing the Business Logic in Groovy

Now that we have the database, we can start implementing the
business logic. As mentioned previously, Java 6 adds support for
scripting languages directly at the JVM level, with "http://www.mozilla.org/rhino/">Mozilla's Rhino JavaScript
engine
included by default. WebLEAF takes advantage of these
new features and allows us to implement the business logic using
any supported scripting language. A Groovy engine is not shipped
directly with Java 6, so in order to use it we must turn to the
scripting java.net
project
, which hosts Script engines implementations for various
languages.

  1. We also need Groovy, the language, so we download the "http://groovy.codehaus.org/Download">latest release of Groovy,
    extract it and and add groovy-all-1.0.jar (not
    groovy-1.0.jar) to Test/WEB-INF/lib.
  2. We then download the "https://scripting.dev.java.net/servlets/ProjectDocumentList">jsr223-engines
    from the "https://scripting.dev.java.net/servlets/ProjectDocumentList">java.net scripting project,
    extract the content, locate the groovy-engine.jar file (it
    should be in the groovy/build directory inside the .jar) and add it
    to Test/WEB-INF/lib.
  3. Now that we have all the required libraries, we can add the
    code. We create the file Test/WEB-INF/scr/Item.groovy with
    the following code:
[prettify]import groovy.xml.MarkupBuilder
import groovy.sql.Sql
import java.util.*

def ShowAllItems(builder,sql)
{
 sql.eachRow(
  "select * from TTST_ITEM",
  {
   itemToXML(builder,it)
  }
 );
}

def showItem(builder,sql,codeItem)
{
 if(codeItem)
 {
  item =
    sql.firstRow("select * from TTST_ITEM where ITE_CODE = ?"
                 ,[codeItem])
  if(item)
  {
   builder.ITEM_QUERY(Type:'Selected'){
           itemToXML(builder,item)
           }
  }
 }
}

def itemToXML(builder, item)
{
 builder.ITEM( Code:item.ite_code,
               Name:item.ite_name,
               Description:item.ite_description
             )
}

def show(param)
{
 def bundle = ResourceBundle.getBundle("webapp");
 def sql = Sql.newInstance("jdbc:hsqldb:"
         + bundle.getString("db.location")
         + "/Test;shutdown=true;ifexists=true"
         , "sa","", "org.hsqldb.jdbcDriver")
 def writer = new StringWriter()
 def xml = new MarkupBuilder(writer)
 def groovy = xml.ITEM_QUERY(Type:'All'){
                  ShowAllItems(xml,sql)
                  }
 showItem(xml,sql,param.p_ite_code)
 // Not efficient but it's just to prevent
 // HSQLDB to block on the DB files
 // when the servlet context is restarted
 sql.execute("SHUTDOWN")
 return writer.toString()
}
[/prettify]

  1. To be able to use the Groovy script displayed above, we modify
    the WebLEAF configuration file
    (Test/WEB-INF/Test.leaf.conf) and add an
    XMLOPERATION that references the script as the
    business logic implementation. After adding an operation that will
    answer to requests of the form .../showItems.fm, the
    configuration file should now look like this:
[prettify]&lt;?xml version="1.0" encoding="iso-8859-1"?&gt;
&lt;!DOCTYPE WADSET SYSTEM "http://www.uib.es/leaf/LEAFwad.dtd"&gt;
&lt;WADSET&gt;
  &lt;WAD
    NAME="Test"
    INIT_MODE="INITS_ON_START_UP"
    XML_ALLOW_SHOW="TRUE"
    XSLT_NO_CACHE="TRUE"
  &gt;
   &lt;OPERATIONSET&gt;
    &lt;SUFFIX VALUE="fm"/&gt;
    &lt;XMLOPERATION
     NAME="showItems"
     DESCRIPTION="Shows all the items and the selected one"
     CLASSNAME="org.leaf.XMLOperation"
    &gt;
     &lt;SOURCES
      GLOBAL_TAG="XML_APP"
     &gt;
      &lt;SOURCE XML_SOURCE="xml/Labels.xml"/&gt;
      &lt;SOURCE XML_SOURCE="script://scr/Item.groovy/show?w_"/&gt;
     &lt;/SOURCES&gt;
    &lt;/XMLOPERATION&gt;
   &lt;/OPERATIONSET&gt;
  &lt;/WAD&gt;
&lt;/WADSET&gt;
[/prettify]

One of the important things to notice here is the URL to call
the Groovy file (script://scr/Item.groovy/show?w_).

  1. To have a more real implementation, we also add the labels that
    will be used later to implement the view. We do this by creating
    the file Test/WEB-INF/xml/Labels.xml with the following
    XML content:
[prettify]&lt;LABELS
 Title="Test application"
&gt;
 &lt;ITEM
  Title="Item Management"
  FieldSetLegend="Item data"
  SelectDefault="... select an item to display its data"
  Code="Code"
  Name="Name"
  Description="Description"
 /&gt;
&lt;/LABELS&gt;
[/prettify]

  1. In order for our application to handle the requests ending with
    the suffix ".fm", we also need to modify the
    Test/WEB-INF/classes/web.xml configuration file to forward
    such requests to the WebLEAF controller servlet. That implies
    adding a <servlet-mapping/> tag, like the one
    displayed at the bottom of this sample:
[prettify]&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
 &lt;!--
  Autoconfiguration servlet
 --&gt;
 &lt;servlet&gt;
  &lt;servlet-name&gt;AutoConfigurer&lt;/servlet-name&gt;
  &lt;servlet-class&gt;org.leaf.util.AutoConfigurer&lt;/servlet-class&gt;
  &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;FILE_NAME.1&lt;/param-name&gt;
   &lt;param-value&gt;WEB-INF/classes/webapp.properties&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;NODES_NAME.1&lt;/param-name&gt;
   &lt;param-value&gt;db.location&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;PATTERN.1&lt;/param-name&gt;
   &lt;param-value&gt;(.*)/WEB-INF/(.*)&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;FORMAT.1&lt;/param-name&gt;
   &lt;param-value&gt;{0}WEB-INF/{1}&lt;/param-value&gt;
  &lt;/init-param&gt;
 &lt;/servlet&gt;
 &lt;!-- End Autoconfiguration servlet --&gt;
 &lt;!--
  WebLEAF controller servlet configuration
 --&gt;
 &lt;servlet&gt;
  &lt;servlet-name&gt;WebLEAFController&lt;/servlet-name&gt;
  &lt;servlet-class&gt;org.leaf.LEAFManager&lt;/servlet-class&gt;
  &lt;load-on-startup&gt;2&lt;/load-on-startup&gt;
 &lt;/servlet&gt;
 &lt;!-- End controller servlet --&gt;
 &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;WebLEAFController&lt;/servlet-name&gt;
  &lt;url-pattern&gt;*.fm&lt;/url-pattern&gt;
 &lt;/servlet-mapping&gt;
&lt;/web-app&gt;
[/prettify]







Now it is time to test the application for the first time.

  1. We configure our favorite servlet container to deploy the
    application using the test context. We could use any other
    name, but for simplicity's sake, we will suppose we are using
    "test" for the context name. The fastest way to deploy the
    application for some popular containers is to simply move the whole
    test directory to the auto-deploy directory, usually
    called webapps.
  2. We start up the container and access the URL
    http://localhost:8080/test/showItems.fm (we need to
    modify the hostname, port, and context name if using something other
    than the defaults).

Our browser then displays something like Figure 1:

<br "Figure 1. Snapshot of the application displaying the XML produced by our business logic" width="450" height="490" />

Figure 1. Snapshot of the application displaying the XML
produced by our business logic

If we check the Test/WEB-INF/db/Test.script file, we
can see that the data in the XML corresponds to the
Labels.xml file we just created, combined with an XML view
of the data stored in the database. We see the plain XML because we
have still not configured any view technology for the
XMLOPERATION, but that's what we are about to do in
the next step.

Implementing the View with FreeMarker

Now that we have implemented the business logic, let's add the
view.

  1. As the first option, we implement the view with FreeMarker; so
    the first thing we need to do is to
    "http://freemarker.sourceforge.net/freemarkerdownload.html">download
    the FreeMarker library
    , extract it and add
    freemarker.jar to Test/WEB-INF/lib.
  2. In order to process the XML with FreeMarker using XPath
    expressions, we use an XSLT engine, "http://jaxen.codehaus.org/">Jaxen, so we download "http://jaxen.codehaus.org/releases.html">the latest release of
    Jaxen
    , extract it and copy jaxen-*.jar to
    Test/WEB-INF/lib.
  3. After that, we edit the WebLEAF configuration file
    (Test/WEB-INF/Test.leaf.conf) and simply add an
    attribute (FM_TEMPLATE="item.ftl") to the
    XMLOPERATION so it looks like the one below:
[prettify]&lt;?xml version="1.0" encoding="iso-8859-1"?&gt;
&lt;!DOCTYPE WADSET SYSTEM "http://www.uib.es/leaf/LEAFwad.dtd"&gt;
&lt;WADSET&gt;
  &lt;WAD
    NAME="Test"
    INIT_MODE="INITS_ON_START_UP"
    XML_ALLOW_SHOW="TRUE"
    XSLT_NO_CACHE="TRUE"
  &gt;
   &lt;OPERATIONSET&gt;
    &lt;SUFFIX VALUE="fm"/&gt;
    &lt;XMLOPERATION
     NAME="showItems"
     DESCRIPTION="Shows all the items and the selected one"
     CLASSNAME="org.leaf.XMLOperation"
     FM_TEMPLATE="item.ftl"
    &gt;
     &lt;SOURCES
      GLOBAL_TAG="XML_APP"
     &gt;
      &lt;SOURCE XML_SOURCE="xml/Labels.xml"/&gt;
      &lt;SOURCE XML_SOURCE="script://scr/Item.groovy/show?w_"/&gt;
     &lt;/SOURCES&gt;
    &lt;/XMLOPERATION&gt;
   &lt;/OPERATIONSET&gt;
  &lt;/WAD&gt;
&lt;/WADSET&gt;
[/prettify]

  1. We then create the Test/WEB-INF/templates directory
    and add there two files (item.ftl and main.ftl) using
    the code samples displayed below:
[prettify]&lt;#-- Include the main macros --&gt;
&lt;#include "main.ftl"&gt;
&lt;#-- Set the title for this part  --&gt;
&lt;#assign Title&gt; - ${Labels.ITEM.@Title}&lt;/#assign&gt;
&lt;#assign itemsList =
          doc["XML_APP/ITEM_QUERY[@Type='All']"][0]&gt;
&lt;#assign selectedItem =
          doc["XML_APP/ITEM_QUERY[@Type='Selected']"]&gt;
&lt;#if (selectedItem?size &gt; 0)&gt;
  &lt;#assign theItem = selectedItem.ITEM&gt;
&lt;/#if&gt;
&lt;#-- Create the page using the main macro --&gt;
&lt;@Main_Page&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;${Labels.ITEM.@FieldSetLegend}&lt;/legend&gt;
    &lt;form id="itemForm" name="itemForm" action=""&gt;
      &lt;select id="w_p_ite_code"
              name="w_p_ite_code"
              onchange="itemForm.submit();"&gt;
        &lt;option value=""&gt;${Labels.ITEM.@SelectDefault}&lt;/option&gt;
        &lt;#if (itemsList.ITEM?size &gt; 0)&gt;
        &lt;#list itemsList.ITEM as Item&gt;
          &lt;option value="${Item.@Code}"
            &lt;#if (theItem?exists &amp;&amp;
                    theItem.@Code==Item.@Code)&gt;
            selected
            &lt;/#if&gt;
          &gt;
          ${Item.@Name}
          &lt;/option&gt;
        &lt;/#list&gt;
        &lt;/#if&gt;
      &lt;/select&gt;
    &lt;/form&gt;
    &lt;#if (theItem?exists)&gt;
      &lt;br /&gt;
      &lt;b&gt;${Labels.ITEM.@Name}&lt;/b&gt;:
          ${theItem.@Name}
      &lt;br /&gt;
      &lt;b&gt;${Labels.ITEM.@Description}&lt;/b&gt;:
          ${theItem.@Description}
    &lt;/#if&gt;
  &lt;/fieldset&gt;
&lt;/@Main_Page&gt;
[/prettify]

for the first (item.ftl), and then

[prettify]&lt;#-- Direct handle for the labels --&gt;
&lt;#assign Labels = doc.XML_APP.LABELS[0]&gt;

&lt;#-- Main reusable macro to create each page --&gt;
&lt;#macro Main_Page&gt;
&lt;!DOCTYPE html
    PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd"&gt;
&lt;html&gt;
  &lt;head&gt;
  &lt;meta http-equiv="Content-Type"
        content="text/html; charset=iso-8859-1"&gt;
  &lt;title&gt;${Labels.@Title}${Title} (FREEMARKER)&lt;/title&gt;
  &lt;meta http-equiv="Expires" content="now"/&gt;
  &lt;meta http-equiv="Pragma" content="no-cache"/&gt;
  &lt;meta http-equiv="Cache-Control" content="private"/&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;#nested&gt;
    &lt;a href="./"&gt;Back to index&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;
&lt;/#macro&gt;
[/prettify]

for the second one (main.ftl).

  1. We start up the container again and access the URL
    http://localhost:8080/test/showItems.fm. This time we
    see something like Figure 2:

<br "Figure 2. Snapshot of the application displaying list of items, generated with FreeMarker" width="400" height="201" />


Figure 2. Snapshot of the application displaying list of items,
generated with FreeMarker

  1. If we select one of the items, we can see its data, as
    displayed in Figure 3:

<br "Figure 3. Snapshot of the application displaying the details of an item, generated with FreeMarker" width="400" height="220" />

Figure 3. Snapshot of the application displaying the details of
an item, generated with FreeMarker

Implementing the View with XSLT

We already implemented the view using FreeMarker, but, as we
want to see several options, we will also implement it using XSLT.
We already included an XSLT engine, Jaxen, to the classpath, so we
don't need to add any other library.

  1. In this case, we just need to add a new
    XMLOPERATION, which will use the same data as the
    previous one, but processing the XML with XSLT instead of with
    FreeMarker. As we want to have both operations working at the same
    time, we add another block of operations (an
    OPERATIONSET) that will answer under another suffix
    (.xsl). Our WebLEAF configuration file now looks like this:
[prettify]&lt;?xml version="1.0" encoding="iso-8859-1"?&gt;
&lt;!DOCTYPE WADSET SYSTEM "http://www.uib.es/leaf/LEAFwad.dtd"&gt;
&lt;WADSET&gt;
  &lt;WAD
    NAME="Test"
    INIT_MODE="INITS_ON_START_UP"
    XML_ALLOW_SHOW="TRUE"
    XSLT_NO_CACHE="TRUE"
  &gt;
   &lt;OPERATIONSET&gt;
    &lt;SUFFIX VALUE="fm"/&gt;
    &lt;XMLOPERATION
     NAME="showItems"
     DESCRIPTION="Shows all the items and the selected one"
     CLASSNAME="org.leaf.XMLOperation"
     FM_TEMPLATE="item.ftl"
    &gt;
     &lt;SOURCES
      GLOBAL_TAG="XML_APP"
     &gt;
      &lt;SOURCE XML_SOURCE="xml/Labels.xml"/&gt;
      &lt;SOURCE XML_SOURCE="script://scr/Item.groovy/show?w_"/&gt;
     &lt;/SOURCES&gt;
    &lt;/XMLOPERATION&gt;
   &lt;/OPERATIONSET&gt;
   &lt;OPERATIONSET&gt;
    &lt;SUFFIX VALUE="xsl"/&gt;
    &lt;XMLOPERATION
     NAME="showItems"
     DESCRIPTION="Shows all the items and the selected one"
     CLASSNAME="org.leaf.XMLOperation"
     XSLT_SOURCE="xsl/Item.xsl"
    &gt;
     &lt;SOURCES
      GLOBAL_TAG="XML_APP"
     &gt;
      &lt;SOURCE XML_SOURCE="xml/Labels.xml"/&gt;
      &lt;SOURCE XML_SOURCE="script://scr/Item.groovy/show?w_"/&gt;
     &lt;/SOURCES&gt;
    &lt;/XMLOPERATION&gt;
   &lt;/OPERATIONSET&gt;
  &lt;/WAD&gt;
&lt;/WADSET&gt;
[/prettify]

  1. To handle those new requests, we need to again modify the
    Test/WEB-INF/classes/web.xml configuration file, to
    forward the requests ending in ".xsl" to the WebLEAF
    controller servlet. After adding another
    <servlet-mapping/> tag at the bottom, our
    Test/WEB-INF/classes/web.xml file looks like this:
[prettify]&lt;?xml version="1.0"?&gt;
&lt;!DOCTYPE web-app
  PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
  "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
 &lt;!--
  Autoconfiguration servlet
 --&gt;
 &lt;servlet&gt;
  &lt;servlet-name&gt;AutoConfigurer&lt;/servlet-name&gt;
  &lt;servlet-class&gt;org.leaf.util.AutoConfigurer&lt;/servlet-class&gt;
  &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;FILE_NAME.1&lt;/param-name&gt;
   &lt;param-value&gt;WEB-INF/classes/webapp.properties&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;NODES_NAME.1&lt;/param-name&gt;
   &lt;param-value&gt;db.location&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;PATTERN.1&lt;/param-name&gt;
   &lt;param-value&gt;(.*)/WEB-INF/(.*)&lt;/param-value&gt;
  &lt;/init-param&gt;
  &lt;init-param&gt;
   &lt;param-name&gt;FORMAT.1&lt;/param-name&gt;
   &lt;param-value&gt;{0}WEB-INF/{1}&lt;/param-value&gt;
  &lt;/init-param&gt;

 &lt;/servlet&gt;
 &lt;!-- End Autoconfiguration servlet --&gt;
 &lt;!--
  WebLEAF controller servlet configuration
 --&gt;
 &lt;servlet&gt;
  &lt;servlet-name&gt;WebLEAFController&lt;/servlet-name&gt;
  &lt;servlet-class&gt;org.leaf.LEAFManager&lt;/servlet-class&gt;
  &lt;load-on-startup&gt;2&lt;/load-on-startup&gt;
 &lt;/servlet&gt;
 &lt;!-- End controller servlet --&gt;
 &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;WebLEAFController&lt;/servlet-name&gt;
  &lt;url-pattern&gt;*.fm&lt;/url-pattern&gt;
 &lt;/servlet-mapping&gt;
 &lt;servlet-mapping&gt;
  &lt;servlet-name&gt;WebLEAFController&lt;/servlet-name&gt;
  &lt;url-pattern&gt;*.xsl&lt;/url-pattern&gt;
 &lt;/servlet-mapping&gt;
&lt;/web-app&gt;
[/prettify]

  1. Lastly, we add the XSLT implementation of the view by creating
    the file Test/WEB-INF/xsl/Item.xsl with the following XSLT
    code:
[prettify]&lt;?xml version="1.0" encoding="iso-8859-1"?&gt;
&lt;xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0"&gt;
 &lt;xsl:output method="html"
             indent="yes"
             media-type="text/html"
             encoding="iso-8859-1"
 /&gt;
 &lt;xsl:variable name="labels" select="/XML_APP/LABELS" /&gt;
 &lt;xsl:template match="/"&gt;
  &lt;html&gt;
    &lt;head&gt;
      &lt;META HTTP-EQUIV="Expires" CONTENT="now"/&gt;
      &lt;META HTTP-EQUIV="Pragma" CONTENT="no-cache"/&gt;
      &lt;META HTTP-EQUIV="Cache-Control" CONTENT="private"/&gt;
      &lt;title&gt;&lt;xsl:call-template name="TITLE"/&gt;(XSLT)&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
      &lt;xsl:apply-templates/&gt;
        &lt;a href="./"&gt;Back to index&lt;/a&gt;
    &lt;/body&gt;
  &lt;/html&gt;
 &lt;/xsl:template&gt;
 &lt;xsl:template name="TITLE"&gt;
  &lt;xsl:value-of select="$labels/@Title"/&gt;
  &lt;xsl:apply-templates mode="SUB_TITLE"/&gt;
 &lt;/xsl:template&gt;
 &lt;xsl:template match="ITEM_QUERY[@Type='All']" mode="SUB_TITLE"&gt;
  - &lt;xsl:value-of select="$labels/ITEM/@Title"/&gt;
 &lt;/xsl:template&gt;
 &lt;xsl:template match="ITEM_QUERY[@Type='All']"&gt;
  &lt;xsl:variable name="theItem"
      select="/XML_APP/ITEM_QUERY[@Type='Selected']/ITEM"
  /&gt;
  &lt;fieldset&gt;
    &lt;legend&gt;
    &lt;xsl:value-of select="$labels/ITEM/@FieldSetLegend"/&gt;
    &lt;/legend&gt;
    &lt;form id="itemForm" name="itemForm" action=""&gt;
      &lt;select id="w_p_ite_code"
              name="w_p_ite_code"
              onchange="itemForm.submit();"
      &gt;
        &lt;option value=""&gt;
          &lt;xsl:value-of select="$labels/ITEM/@SelectDefault"/&gt;
        &lt;/option&gt;
        &lt;xsl:for-each select="ITEM"&gt;
          &lt;option value="{@Code}"&gt;
            &lt;xsl:if test="$theItem/@Code = @Code"&gt;
              &lt;xsl:attribute name="selected"&gt;
              selected
              &lt;/xsl:attribute&gt;
            &lt;/xsl:if&gt;
            &lt;xsl:value-of select="@Name"/&gt;
          &lt;/option&gt;
        &lt;/xsl:for-each&gt;
      &lt;/select&gt;
    &lt;/form&gt;
    &lt;xsl:if test="$theItem"&gt;
      &lt;b&gt;&lt;xsl:value-of select="$labels/ITEM/@Name"/&gt;&lt;/b&gt;:
          &lt;xsl:value-of select="$theItem/@Name"/&gt;
      &lt;br /&gt;
      &lt;b&gt;&lt;xsl:value-of select="$labels/ITEM/@Description"/&gt;&lt;/b&gt;:
          &lt;xsl:value-of select="$theItem/@Description"/&gt;
    &lt;/xsl:if&gt;
  &lt;/fieldset&gt;
 &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;
[/prettify]

  1. Now we can start up the container once again and access the URL
    http://localhost:8080/test/showItems.xsl. (Notice the
    suffix ".xsl" instead of ".fm".) We see exactly the same thing as
    before
    , and no, there is nothing wrong with that. It is exactly
    what we wanted to accomplish: producing the same exact result with
    two different view technologies.







Adding the Final Touch

Now that we have almost everything, we can add a small HTML file
so we have a starting place to check all the operations.

  1. The starting place will be the file Test/index.html,
    which we will create by copying this HTML code:
[prettify]&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Test application&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
 Application to test the possibilities of
 WebLEAF 3.0b + Groovy + HSQLDB
 &lt;br /&gt;
 &lt;ul&gt;Test these urls:
  &lt;li&gt;
  &lt;a href="showItems.fm?showXMLSource=true"&gt;
      Listing items in the database as XML with Groovy
  &lt;/a&gt;
  &lt;ul&gt;&lt;b&gt;Notes&lt;/b&gt;
  &lt;li&gt;The LABELS node comes from the file at
      &lt;i&gt;WEB-INF/xml/Labels.xml&lt;/i&gt;
  &lt;/li&gt;
  &lt;li&gt;The ITEM_QUERY node is generated using the Groovy script at
      &lt;i&gt;WEB-INF/scr/Item.groovy&lt;/i&gt;
  &lt;/li&gt;
  &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href="showItems.fm"&gt;
        Listing items in the database with Groovy + FreeMarker
      &lt;/a&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href="showItems.xsl"&gt;
        Listing items in the database with Groovy + XSLT
      &lt;/a&gt;
  &lt;/li&gt;
 &lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;
[/prettify]

  1. We can now access the URL
    http://localhost:8080/test/ to access the raw XML and
    both views' versions, as displayed in Figure 4:

<br "Figure 4. Snapshot of the application's default page" width="450" height="242" />
Figure 4. Snapshot of the application's default page

This is an example of how one can configure a powerful and
dynamic development environment while keeping a healthy
"separation of concerns," thanks to the new scripting
capabilities included in Java 6.

Conclusions

Now that we have everything configured, we can play directly
with the business logic (Item.groovy) or with the views
implementations (Item.ft/Main.ftl or
Item.xsl along with Labels.xml). There is no need
to recompile anything or restart the servlet context, and our
changes are picked up automatically. Moreover, if we add new
XMLOPERATION nodes, we can see that the WebLEAF configuration is
reloaded automatically (it is checked every minute by default). So
we are able to add new functionality, change the business logic, and
modify the user interface without restarting anything.

Thanks to using XML, we can reuse the libraries we already have
in the Java world and produce, for example, dynamic images using
SVG and "http://xmlgraphics.apache.org/batik/">Batik, PDF reports using
"http://jasperforge.org/sf/projects/jasperreports">JasperReports
based on XMLDataSource, etc.

It is also important noticing that as we have shown, different
view technologies are not mutually exclusive so if you favor
XSLT, but you need to
generate a plain text document (like a JSON response for an AJAX
request), you can easily reuse the same business logic operations
and implement the view for just this request with a more
text-friendly template system like "http://freemarker.sourceforge.net/">FreeMarker.

On the other hand, we might need such dynamic development for
the prototyping phase, but want to implement our final
business logic in, for example, compiled Java classes. No problem!
We just need to implement the Java classes that return the same XML
as our Groovy prototype
and then change the XML_SOURCE attributes starting with
"script://" to others starting with "class://".
As long as we return the same XML, nothing else needs to be
changed. Plain Java classes, session EJBs, "http://www.orafaq.com/faq/plsql">Oracle's PLSQL procedures,
Hibernate-based DAOs--all are
different options for the XML_SOURCE origin, so there are plenty to
choose from.

Last, but not least, we used "http://groovy.codehaus.org/">Groovy as the scripting
implementation language but, thanks to the "https://scripting.dev.java.net/">scripting java.net project,
we could have used other scripting languages by simply changing the
file the XML_SOURCE URL points to. It is worth mentioning that the
Scripting API allows you to choose the scripting engine to be
loaded depending on the extension of the file to be processed, so
it is just necessary to add the appropriate engine to the
classpath, and sometimes configure the scripting language
underneath, to be able to use other languages. Apart from testing
Groovy, at the time of
writing we had performed successfully the same tests
using other scripting engines and implementing the business logic
in Jython and "http://quercus.caucho.com/">PHP (Quercus).

Implementation Notes

In the example, access to the database in the Groovy code is
done in an inefficient way, as it opens a database connection
for each request. But this is just a script for demonstration
purposes, and we didn't want to make it more complicated by
introducing a DataSource configuration, which depends on the
servlet container used.

If you are convinced that you want to go Groovy from beginning
to end, be aware that there are other options, like "http://grails.codehaus.org/">Grails, that might be more
suitable for you. The final goal of the technique explained in this
article was to show an architecture where you can swap Groovy with
other scripting languages; and later on replace them with
Java/PLSQL, etc., so
the approach is totally different as the requirements are also
different.

Resources

  • Source codeThe sample code used in this document minus the
    .jar libraries.
  • WebLEAF: A servlet
    framework whose main goal is to help in the development of web
    server-side applications written in Java.
  • HSQDLB: An SQL relational
    database engine written in Java.
  • scripting java.net
    project
    : A project at java.net hosting script engines that
    implement the JSR 223 Scripting APIs introduced with Java 6.
  • Groovy: An agile
    dynamic language for the Java platform with many features that are
    inspired by languages like Python, Ruby, and Smalltalk, making
    modern programming features available to Java developers with
    an almost-zero learning curve.
  • FreeMarker: A
    "template engine;" a generic tool to generate text output (anything
    from HTML to autogenerated source code) based on templates.
  • XSLT: A language for
    transforming XML documents into other XML documents.
  • Jaxen: A Java XPath
    engine under an Apache-style open source license.
  • Scalable Vector
    Graphics (SVG)
    : A language for describing two-dimensional
    graphics and graphical applications in XML.
  • Batik: A
    Java-based toolkit for applications or applets that want to use
    images in the SVG format for various purposes, such as display,
    generation, or manipulation.
  • "http://jasperforge.org/sf/projects/jasperreports">JasperReports:
    A powerful open source Java reporting tool that has the ability to
    deliver rich content onto the screen, to the printer, or into PDF,
    HTML, XLS, CSV, or XML files.
  • Hibernate: A powerful,
    high performance object/relational persistence and query
    service.
  • PLSQL: Oracle's
    Procedural Language extension to SQL.
  • Grails: An
    open source web application framework that leverages the Groovy
    language and complements Java web development.
  • Jython: An implementation
    of the high-level, dynamic, object-oriented language Python written
    in 100 percent pure Java, and seamlessly integrated with the Java
    platform.
  • JRuby: A 100 percent
    pure-Java implementation of the Ruby programming language.
  • Quercus PHP: Caucho
    Technology's 100 percent Java implementation of PHP 5, released under the
    Open Source GPL license.

width="1" height="1" border="0" alt=" " />
Daniel López currently works at the University of the Balearic Islands (UIB) as technical leader of the web development group.
Related Topics >> Web Development Tools   |