Spring 2.0 Integration

Introduction

This article explains how to integrate o:XML with the Spring 2.0 framework. It shows how you can create o:XML Spring beans and make use of Springs many IOC (Inversion of Control) features, such as dependency injection. Spring 2.0 also has support for other languages, including JRuby, XSLT and Groovy, which may all be mixed with Java and o:XML in the same application.

Overview

New in Spring 2.0 is support for creating beans with scripting languages. They call this Dynamic Language Support, and the Spring distribution currently comes with support for JRuby, Groovy and Beanshell. To extend this with o:XML support, just add objectbox.jar (see Prerequisites).

The Spring documentation has a very good chapter on Dynamic Language Support that is worth reading in conjunction with this article, you can find it here. In fact, our examples are to some extent based on those in the Spring documentation.

We have also expanded on their Scenarios section with some interesting uses of o:XML to delegate XML processing, see Scenarios.

Prerequisites

To follow the examples it helps to be somewhat familiar with the Spring Framework. If you don't already have a Spring 2.0 environment set up you will need to download and install a recent version from the Spring website.

In order to use o:XML in Spring 2.0, all you need to do is add objectbox.jar to the Spring environment classpath. Alternatively, you may add the Spring jars to your o:XML installation, webapplication or similar. As a minimum you will need spring.jar and commons-logging.jar. If you use schema-based Spring configuration you will also need cglib-nodep-2.1_3.jar or equivalent.

Calculator Example

A simple but useful example of an o:XML Spring bean is where we configure an o:XML object so that it appears to implement a Java interface. We can then call methods on the bean, ie the o:XML object, as if it was a plain old Java object.

Example 1: Calculator Java Test


ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
Calculator calculator = (Calculator)ctx.getBean("calculator");
System.out.println("1+2=" + calculator.add(1, 2));
      

To use this we need three more things:

  • a Java Calculator interface (Calculator.java)
  • our o:XML Calculator (Calculator.oml).
  • a Spring context file (context.xml)

The Calculator interface is very simple:

Example 2: Calculator Interface


public interface Calculator {
	int add(int x, int y);
}
      

The Calculator itself might look like this:

Example 3: o:XML Calculator


<o:program>
  <-- sample o:XML bean that implements (implicitly) the Calculator interface -->
  <o:type name="Calculator">
    <o:function name="add">
      <o:param name="x"/>
      <o:param name="y"/>
      <o:do><o:return select="$x + $y"/></o:do>
    </o:function>
  </o:type>
  <o:return select="Calculator()"/>
</o:program>
      

In this and following examples, o:XML programs are assumed to have the namespace declaration xmlns:o="http://www.o-xml.org/lang/".

The last line, <o:return select="Calculator()"/>, is necessary so that Spring gets an object that it can return to the calling code. The script file does not have to define a type (though it may define several) but it must always return an object that implements the expected interface.

The Spring context file is what ties everything together. In the context file we define our bean, its Java interface and script file. We can also set properties and provide constructor values as we will see later.

Example 4: Calculator Context


<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:script="http://www.o-xml.org/namespace/spring/"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
                           http://www.o-xml.org/namespace/spring/ 
                           http://www.o-xml.org/namespace/spring/spring.xsd">
  <script:oxml id="calculator" script-interfaces="Calculator"
               script-source="file:Calculator.oml"/>
</beans>

The lengthy namespace and schema declarations at the top of the context file are necessary for Spring to find the right schema definitions.

Messenger Example

The Messenger example will show how bean properties can be set in the Spring context file.

The Java client code looks very similar to the Calculator - we get a bean from Spring, then call a method on the bean.

Example 5: Messenger Java Test


ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
Messenger messenger = (Messenger)ctx.getBean("messenger");
System.out.println("message: "+messenger.getMessage());
      

The Java interface is also very simple.

Example 6: Messenger Interface


public interface Messenger {
	String getMessage();
}

No surprises in the Messenger o:XML code either. Note that we define a setMessage() function here, but not in the Java interface.

Example 7: o:XML Messenger


<o:program>
  <o:type name="Messenger">
    <o:variable name="message"/>

    <o:function name="setMessage">
      <o:param name="message"/>
      <o:do/>
    </o:function>

    <o:function name="getMessage">
      <o:do><o:return select="$message"/></o:do>
    </o:function>
  </o:type>

  <o:return select="Messenger()"/>
</o:program>
      

The interesting part is in the Spring context. When a property is defined like this Spring finds and calls the right setter method when it initialises the bean for us.

Example 8: Messenger Context


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:lang="http://www.springframework.org/schema/lang"
       xmlns:script="http://www.o-xml.org/namespace/spring/"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
                           http://www.springframework.org/schema/lang
                           http://www.springframework.org/schema/lang/spring-lang-2.0.xsd
                           http://www.o-xml.org/namespace/spring/
                           http://www.o-xml.org/namespace/spring/spring.xsd">
	<script:oxml id="messenger" script-interfaces="Messenger"
                     script-source="file:Messenger.oml">
          <lang:property name="message" value="Hello o:XML!"/>
	</script:oxml>
</beans>

Parameter Types

When sending parameters to and from o:XML beans, parameter types are automatically converted. Basic types such as String, Number and Boolean are converted to and from their Java counterparts as defined by the given Java interface. Nodesets, Elements etc should be handled as DOM org.w3c.dom.DocumentFragment objects on the Java side. You can also use the ObjectBox Java extensions to pass Java objects of arbitrary type to and from o:XML scripts.

Scenarios

Using o:XML within the Spring framework opens up some particularly interesting use cases. An obvious advantage of o:XML is its native XML support, and XML processing in one form or another has become integral to most modern software projects. Java suffers greatly from the paradigm mismatch which becomes apparent when you try to do serious XML processing with it. With o:XML Spring beans, otherwise painful handling of XML structures can be delegated.

Reading XML

Often in a Java application you will want to extract some information provided in XML format. This can be a configuration file, data from the user or another application, or an internal format. Using DOM and SAX can be a longwinded way of getting to the information. Newer API's such as JDOM offer little improvement. In contrast it is very easy to retrieve the data with o:XML, which gives you full XPath support and native XML data types. You can write reusable types and functions, and easily implement fault-tolerance which can be excruciatingly difficult to achieve in Java.

In the simplest case you can define a bean with methods to return single values from an XML structure. This might be useful when you know exactly what information items you need to extract, and you know that this will not change. Often however it will be advantagous to let the bean create and/or populate an object for you that holds all of the information. This approach is usually called XML marshalling. The object can be either a plain Java object or another Spring bean.

When creating an o:XML Spring bean, the framework automatically maps certain parameter types for you (see Parameter Types). This means that you can define your Java bean interface to return and accept DOM org.w3c.dom.DocumentFragment parameters, instead of o:XML's native types. That way the o:XML beans can automatically pass XML constructs to and from the framework.

Example 9: Reading XML

Imagine that you have a Java LunchMenu class that you wish to populate with information that comes to you in XML format. LunchMenu might look like this:


public class LunchMenu {
    public List getStarters();
    public List getMains();
    public List getDesserts();
    public List getSpecials();
    public void addStarter(String name, String description, float price);
    public void addMain(String name, String description, float price);
    public void addDessert(String name, String description, float price);
    public void addSpecial(String name, String description, float price);
}
      

Your o:XML bean interface looks like this:


interface MenuReader {
    public LunchMenu readMenu(org.w3c.dom.DocumentFragment xml);
}
      

The o:XML bean only needs to define the readMenu() function.


<o:program xmlns:o="http://www.o-xml.org/lang/"
           xmlns:java="http://www.o-xml.com/java/">
  <!-- We call java:resolve here so that we can access the Java constructor later. -->
  <java:resolve classname="org.oXML.example.LunchMenu"/>
  <o:type name="MenuReader">
    <o:function name="readMenu">
      <o:param name="xml"/>
      <o:do>
        <!-- create new LunchMenu (a Plain Old Java Object) -->
        <o:set menu="ex:LunchMenu()"
               xmlns:ex="http://www.o-xml.com/java/org.oXML.example"/>
        <o:for-each select="$xml/Menu/Starter">
          <o:do select="$menu.addStarter(Name, Description, number(Price))"/>
        </o:for-each>
        <!-- et cetera... -->
      </o:do>
    </o:function>
  </o:type>
  <o:return select="MenuReader()"/>
</o:program>

The interesting bit is how easy this is all put together on the client side:


org.w3c.dom.DocumentFragment xml;
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
MenuReader bean = (MenuReader)ctx.getBean("menureader");
LunchMenu menu = bean.readMenu(xml);
      

As you can see in the code, our bean will create a LunchMenu with each call to readMenu(). With a small code change, the object can equally well be instantiated on the Java client side and then passed to the bean to be populated.

Generating and Translating XML

Since XML is ubiquitous in inter-application and intra-application communication protocols as well as file formats, chances are you will have to provide XML as well as read it.

The principle is the same as for reading XML, in order to receive XML back from an o:XML Spring bean you only need to define a DOM org.w3c.dom.DocumentFragment return parameter in corresponding Java interface.

Following on from the previous example, let's add a MenuWriter.

Example 10: Writing XML

The o:XML bean interface now looks like this:


interface MenuWriter {
    public org.w3c.dom.DocumentFragment writeMenu(LunchMenu menu);
}
      

Now we define the MenuWriter o:XML type.


<o:program xmlns:o="http://www.o-xml.org/lang/"
           xmlns:java="http://www.o-xml.com/java/">
  <o:type name="MenuWriter">
    <o:function name="writeMenu">
      <o:param name="menu"/>
      <o:do>
        <Menu>
          <o:set specials="$menu.getSpecials()"/>
          <o:for-each name="i" to="$specials.size()">
            <Special>
              <o:eval select="$this.writeSpecial($specials.get($i))"/>
            </Special>
          </o:for-each>
        </Menu>
      </o:do>
    </o:function>
    <o:function name="writeSpecial">
      <o:param name="special"/>
      <o:do>....</o:do>
    </o:function>
  </o:type>
  <o:return select="MenuWriter()"/>
</o:program>

Again, writing the code to use the bean is trivial:


LunchMenu menu;
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
MenuWriter bean = (MenuWriter)ctx.getBean("menuwriter");
org.w3c.dom.DocumentFragment xml = bean.writeMenu(menu);
      

XML/RDBMS Mappings

In many scenarios you may well want to access an SQL database directly from your o:XML Spring beans. Since o:XML offers excellent JDBC integration through its database extensions, this will often save you a lot of development and testing time, without costing in performance or reliability - probably the contrary.

More information about ObjectBox database extensions can be found here.

DTD-Based Configuration

The Spring context files in the examples all use XML Schema-based configuration. It is equally possible to define o:XML beans with DTD-based configuration. The Calculator context file would look like this:

Example 11: Calculator DTD Context


<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
  <bean id="calculator" class="org.oXML.extras.springframework.oXMLScriptFactory">
    <constructor-arg value="classpath:Calculator.oml"/>
    <constructor-arg value="Calculator"/>
  </bean>
</beans>

Advanced Use

It is also possible to mix and match beans defined in different languages, including of course Java. Some examples are given in the unit tests that form part of the ObjectBox source code (see eg src/junit/org/oXML/extras/springframework/oXMLScriptFactoryTests.java). For more information on defining Ruby, Groovy and Beanshell beans, and passing beans as arguments to other beans, please see the Spring documentation on Dynamic Language Support.