Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

JELLY: An XML-Based Scripting Language


Jul03: JELLY: An XML-Based Scripting Language

Siegfried is a software quality-assurance consultant and can be contacted at [email protected].


Jelly is a scripting language from the Jakarta community. Although implemented in Java, Jelly (http://jakarta.apache.org/commons/sandbox/jelly/) is designed to let you turn XML into executable code. Once you've imported the Jelly tag library, every JavaBean implementing Jelly's Tag interface can be turned into a Jelly Tag and used in Jelly scripts. Jelly can be used as a standalone scripting language or embedded in tools such as Maven (http://maven.apache.org/), the Jakarta Project's project management and build tool; or eQ!, a commercially available Java development platform from Browsersoft (http://www.browsersoft.com/).

Listing One is a "Hello World" program written in Jelly, while Listing Two embeds the same functionality into a Java application that creates a Jelly instance, then loads and compiles the script. JellyContext is used to exchange variables between the application and the Jelly interpreter. After running the Jelly script, you get the result from JellyContext. Listing Three is the executed Jelly script that creates the result variable with the value Hello Dr. Dobb's Journal!!.

Custom tag libraries let you extend Jelly functionality. In Listing Four, for example, I use the doTag method in Jelly's Tag interface to implement a tag library containing two Jelly tags for Base64 encoding/decoding. doTag() retrieves the input value from either evaluating an expression or the XML body text. The resulting Base64-encoded string is written into XMLOutput or stored as the value of a variable. Since the Jelly interpreter needs to know about the new tags, a tag library associates a name with a class (Listing Five). The corresponding Jelly script creates a variable plainString with the value "Foo Is Not Bar." The content of this variable is used to create the base64-encoded variable base64String. The Jelly interpreter finds two attributes—setValue() and setVar(), both of which use reflection to resolve to the corresponding setters. To prove correctness, you can decode base64String by evaluating the XML body tag of <base64:decode> (Listing Six), and Listing Seven shows that Jelly has successfully been extended. In short, there's no need to roll your own tag library, since Jelly already comes with more than 30 tag libraries as part of the standard distribution.

Of course, "Hello World" programs are one thing, and real-world applications quite another. To that end, Maven was selected by Together Teamloesungen (http://www.together.at/) as the project management and build environment for the company's insurance brokerage platform. Maven's core functionality involves automated project building, regression testing, deployment, and web-site creation. The Maven-generated web sites contain project description, documentation, and HTML reports, including software metrics, coding style checks, and JUnit regression test reports.

Since the company also needed automated web testing, it selected Canoo WebTest (http://webtest.canoo.com/), a functional web-testing tool based on HTTPUnit (http://httpunit.sourceforge .net/). Canoo WebTest provides ANT support because the tests scripts are actually executed by ANT (http://ant.apache.org/), but with no Maven integration. Since Maven's core functionality is implemented as plug-ins written in Jelly, Together Teamloesungen developers were able to write a plug-in that provided seamless integration of Canoo WebTest into Maven. (The Maven Canoo WebTest plug-in for Maven is available at http://maven-plugins/ sourceforge.net/.)

The requirements for a Canoo WebTest plug-in include being able to:

  • Determine that all test scripts executed are based on regular expressions.

  • Execute the test scripts using an ANT process.

  • Transform the resulting test reports into HTML using XSLT.

  • Create an XML test summary report to be transformed into HTML

Listing Eight finds all the Canoo WebTest scripts to be executed. In line 1, a local variable currSourceDir is created; it contains the directory where the test scripts are located. Line 3 instantiates a file scanner that looks in the test script directory for all files matching the pattern defined in the Jelly variable maven.webtest.file. Test script files are executed using the ANT process in Listing Nine. In line 1, the forEach statement iterates over the list of test scripts to be executed. In line 2, a Java process is created, where the classpath consists of predefined JAR files and all JAR files found in the plug-in directory. Passing system properties configures the newly created ANT process, and the variable testScriptFile contains the name of the test script to be executed. Executing a Canoo WebTest script generates an XML report, which is transformed to HTML using an XSLT processor. Listing Ten highlights Jelly's power—the file iterator returns a java.io.File object, not a string. Jelly invokes arbitrary methods on Java objects within the script. In line 13, the File.getName() method retrieves the name of the currently processed file.

An important feature is the creation of an XML test summary report reflecting the result of the individual tests. This is done with Jelly and its XML libraries, giving you the power of XML processing within a Jelly script. In line 1 of Listing Eleven, the XML file "webtest-raw-report.xml" is created. The content of this file includes the previously generated Canoo WebTest XML reports. In line 6, you create an XML root element named "summary." Using an iterator, you load and parse the individual XML test reports (line 8) and extract the information using XPath expression applied to the parsed XML document.

Jelly is an exciting solution for enabling scripting in Java applications. It provides direct access to Java objects using reflection and is easily extended with custom tag libraries. Moreover, the resource consumption of an embedded Jelly interpreter is reasonable, adding only 4 MB to your memory usage and executing a compiled script in less than a millisecond. If you find yourself thinking of embedding a scripting language rather than hardwiring application logic, then Jelly might be the answer.

DDJ

Listing One

<?xml version="1.0"?>
<j:jelly xmlns:j="jelly:core">
   Hello World!
</j:jelly>

Back to Article

Listing Two

public class JellySample1
{
   public static void main(String[] args)
   {
      try
      {
         Jelly jelly = new Jelly();
         jelly.setUrl( new File("scripts/hello_world.jelly").toURL() );
         Script script = jelly.compileScript();
         JellyContext context = new JellyContext();
         context.setVariable("name", "Dr. Dobb's Journal");
         script.run( context, xmlOutput );
         System.out.println(context.getVariable("result"));
     }
     catch (Exception e)
     {
        e.printStackTrace();
     }
   }
}

Back to Article

Listing Three

<?xml version="1.0"?>
<j:jelly xmlns:j="jelly:core">
   <j:set var="result" value="Hello ${name}!!"/>
</j:jelly>

Back to Article

Listing Four

public class Base64EncodeTag extends TagSupport
{
   private String var;
   private Expression value;
   public void doTag(XMLOutput output) throws Exception
   {
      String input = null;
      if ( value != null ) {
         input = value.evaluateAsString(context);
      } else {
         input = getBodyText(false);
      }
      String result = new String(
         Base64.encode(input.getBytes())
         );
      if ( var != null ) {
         context.setVariable( var, result);
      } else {
         output.write(result);
      }
   }
   public void setValue(Expression value) {
      this.value = value;
   }
   public void setVar(String var) {
      this.var = var;
   }
}

Back to Article

Listing Five

public class Base64TagLibrary extends TagLibrary
{
   public Base64TagLibrary()
   {
      this.registerTag( "encode", Base64EncodeTag.class );
      this.registerTag( "decode", Base64DecodeTag.class );
   }
}

Back to Article

Listing Six

<?xml version="1.0"?>
<j:jelly
   xmlns:j="jelly:core"
   xmlns:base64="jelly:Base64TagLibrary">
   <j:set var="plainString">Foo Is Not Bar</j:set>
   <base64:encode var="base64String" value="${plainString}"/>
   Plain string : ${plainString}
   Base64 encoded : ${base64String}
   Base64 decoded : <base64:decode>${base64String}</base64:decode>
</j:jelly>

Back to Article

Listing Seven

Plain string : Foo Is Not Bar
Base64 encoded : Rm9vIElzIE5vdCBCYXI=
Base64 decoded : Foo Is Not Bar

Back to Article

Listing Eight

1 <j:set var="currSourceDir" value="${maven.webtest.src}"/>
2
3 <fileScanner var="testScripts">
4    <fileset dir="${currSourceDir}">
5       <patternset>
6          <include name="${maven.webtest.file}"/>
7       </patternset>
8    </fileset>
9 </fileScanner>

Back to Article

Listing Nine

1 <j:forEach var="testScriptFile" items="${testScripts.iterator()}">
2    <java classname="org.apache.tools.ant.Main"
3          fork="yes"
4          dir="${basedir}"
5          failonerror="false">
6       <classpath>
7       <fileset dir="${plugin.dir}/jars">
8       <include name="*.jar"/>
9       </fileset>
10      <pathelement path="${plugin.getDependencyPath('ant')}"/>
11      <pathelement ... />
12      <pathelement path="${plugin.getDependencyPath('xercesImpl')}"/>
13      </classpath>
14      <sysproperty key="basepath" value="${maven.webtest.config.basepath}"/>
15      <sysproperty ... />
16      <sysproperty key="verbose" value="${maven.webtest.config.verbose}"/>
17      <arg value="-f"/>
18      <arg value="${testScriptFile}"/>
19   </java>
20 </j:forEach>

Back to Article

Listing Ten

1 <j:forEach var="testSummaryFile"
2    items="${testSummaries.iterator()}">
3    <echo>Transforming ${testSummaryFile.getName()} into HTML</echo>
4    <java classname="org.apache.xalan.xslt.Process" fork="yes">
5       <classpath>
6          <pathelement path="${plugin.getDependencyPath('xercesImpl')}"/>
7          <pathelement path="${plugin.getDependencyPath('xalan')}"/>
8          <pathelement path="${plugin.getDependencyPath('xml-apis')}"/>
9       </classpath>
10      <arg value="-in"/>
11      <arg value="${testSummaryFile.toString()}"/>
12      <arg value="-out"/>
13      <arg value="${maven.docs.dest}/webtest/${testSummaryFile.getName()}.html"/>
14      <arg value="-xsl"/>
15      <arg value="${plugin.dir}/xslt/reportFromSummary.xsl"/>
16    </java>
17 </j:forEach>

Back to Article

Listing Eleven

1 <j:file
2  name="${maven.build.dir}/webtest-raw-report.xml"
3  outputMode="XML"
4  encoding="ISO-8859-1"
5  prettyPrint="true">
6  <x:element name="summary">
7   <j:forEach var="testSummaryFile" items="${testSummaries.iterator()}">
8   <x:parse var="xmlTestReport" xml="${testSummaryFile.toString()}"/>
9   <x:set var="success" select="string($xmlTestReport/summary/testresult/@successful"/>
10  <x:set var="starttime" select="string($xmlTestReport/summary/testresult/@starttime"/>
11  <x:set var="endtime" select="string($xmlTestReport/summary/testresult/@endtime"/>
12  <x:element name="test">
13          <x:element name="file">${testSummaryFile.getName()}</x:element>
14          <x:element name="success">${success}</x:element>
15          <x:element name="starttime">${starttime}</x:element>
16          <x:element name="endtime">${endtime}</x:element>
17  </x:element>
18  </j:forEach>
19  </x:element>
20 </j:file>

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.