The Source for Java Technology Collaboration
User: Password:



   

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

by Daniel López
06/19/2007

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, Java SE 6 introduced the new 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 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 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, 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 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:
<?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>

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:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE WADSET SYSTEM "http://www.uib.es/leaf/LEAFwad.dtd">
<WADSET>
  <WAD
    NAME="Test"
    INIT_MODE="INITS_ON_START_UP"
    XML_ALLOW_SHOW="TRUE"
    XSLT_NO_CACHE="TRUE"
    >
  </WAD>
</WADSET>
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 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:
#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
The second file is called Test.script and has this content:
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')

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:
db.location=REAL_PATH/WEB-INF/db
  1. We then edit Test/WEB-INF/classes/web.xml to add the Autoconfigurer servlet, so it ends up as the sample below:
<?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>
 <!--
  Autoconfiguration servlet
 -->
 <servlet>
  <servlet-name>AutoConfigurer</servlet-name>
  <servlet-class>org.leaf.util.AutoConfigurer</servlet-class>
  <load-on-startup>1</load-on-startup>
  <init-param>
   <param-name>FILE_NAME.1</param-name>
   <param-value>WEB-INF/classes/webapp.properties</param-value>
  </init-param>
  <init-param>
   <param-name>NODES_NAME.1</param-name>
   <param-value>db.location</param-value>
  </init-param>
  <init-param>
   <param-name>PATTERN.1</param-name>
   <param-value>(.*)/WEB-INF/(.*)</param-value>
  </init-param>
  <init-param>
   <param-name>FORMAT.1</param-name>
   <param-value>{0}WEB-INF/{1}</param-value>
  </init-param>
 </servlet>
 <!-- End Autoconfiguration servlet -->
 <!--
  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>

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 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 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 jsr223-engines from the 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:
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()
}
  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:
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE WADSET SYSTEM "http://www.uib.es/leaf/LEAFwad.dtd">
<WADSET>
  <WAD
    NAME="Test"
    INIT_MODE="INITS_ON_START_UP"
    XML_ALLOW_SHOW="TRUE"
    XSLT_NO_CACHE="TRUE"
  >
   <OPERATIONSET>
    <SUFFIX VALUE="fm"/>
    <XMLOPERATION
     NAME="showItems"
     DESCRIPTION="Shows all the items and the selected one"
     CLASSNAME="org.leaf.XMLOperation"
    >
     <SOURCES
      GLOBAL_TAG="XML_APP"
     >
      <SOURCE XML_SOURCE="xml/Labels.xml"/>
      <SOURCE XML_SOURCE="script://scr/Item.groovy/show?w_"/>
     </SOURCES>
    </XMLOPERATION>
   </OPERATIONSET>
  </WAD>
</WADSET>

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:
<LABELS
 Title="Test application"
>
 <ITEM
  Title="Item Management"
  FieldSetLegend="Item data"
  SelectDefault="... select an item to display its data"
  Code="Code"
  Name="Name"
  Description="Description"
 />
</LABELS>
  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:
<?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>
 <!--
  Autoconfiguration servlet
 -->
 <servlet>
  <servlet-name>AutoConfigurer</servlet-name>
  <servlet-class>org.leaf.util.AutoConfigurer</servlet-class>
  <load-on-startup>1</load-on-startup>
  <init-param>
   <param-name>FILE_NAME.1</param-name>
   <param-value>WEB-INF/classes/webapp.properties</param-value>
  </init-param>
  <init-param>
   <param-name>NODES_NAME.1</param-name>
   <param-value>db.location</param-value>
  </init-param>
  <init-param>
   <param-name>PATTERN.1</param-name>
   <param-value>(.*)/WEB-INF/(.*)</param-value>
  </init-param>
  <init-param>
   <param-name>FORMAT.1</param-name>
   <param-value>{0}WEB-INF/{1}</param-value>
  </init-param>
 </servlet>
 <!-- End Autoconfiguration servlet -->
 <!--
  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 -->
 <servlet-mapping>
  <servlet-name>WebLEAFController</servlet-name>
  <url-pattern>*.fm</url-pattern>
 </servlet-mapping>
</web-app>

Pages: 1, 2, 3

Next Page » 

View all java.net Articles.

 Feed java.net RSS Feeds