Spring declarative transactions with xDB

 

Introduction

This article shows you how to elegantly and easily use EMC Documentum xDB in applications based on the Spring Framework, which xDB versions 10.0.1 and later contain an adapter for.

 

Note: xDB provides an implementation of XA transactions for use with the Java Transaction API (JTA), supporting development of xDB applications that participate in transactions managed by an application server. However, in many applications, full JTA and the host of technologies it requires might be overkill.

 

Example source code is attached below this article. For instructions, see the section "Running the Example" below.

 

xDB Transactions in Spring

 

The Spring Framework is at its core a dependency injection based framework. User code is injected with references to the services it depends on to obtain less coupling. Spring provides many adapters to other open source projects, and xDB versions 10.0.1 and later include its own Spring adapter.

 

Our goal is to provide an unobtrusive way to perform transactional operations on an xDB database, without cluttering our user code with transaction handling, connection configuration, etc., similar to Spring's well-known JDBC template.

 

The listing below shows TestController, a trivial Spring WebMVC controller using regular Spring annotations to map itself to a web path. The special trick is in the field XhiveSessionAccess acc, and the @Transactional annotation. This field will provide access to an xDB transactional context, either through an XhiveSessionIf, or through the helper method query(). In the example below, we execute an XQuery using a parameter from our URI, retrieve the result node, and return it in a ModelAndView object.

 

// imports omitted for brevity
@Controller
public class TestController {
  @Autowired
  private XhiveSessionAccess acc;

  @Transactional(readOnly = true)
  @RequestMapping("/test-{name}.html")
  public ModelAndView test(@PathVariable String name) {
    HashMap<String, Object> params = Maps.newHashMap();
    params.put("name", name);
    XhiveXQueryValueIf result = acc.query("document { <result>Hello, { $name }, it is now { current-dateTime() }</result> }",
      params).next();
    return new ModelAndView("main", "xmlSource", new DOMSource(result.asNode()));
  }
}







 

In our example application, the result XML document is then transformed using an XSLT view - however there is nothing binding you to XSLT. The attached Spring support project can be easily used together with any of the typical Spring technologies, including JSPs.

 

Spring + xDB configuration

 

In Spring, the different parts making up your application are configured through a beans definition file, typically called applicationContext.xml. Using the Spring-xDB adapter, we can configure the application context for our example:

 

applicationContext.xml

 

<?xml version="1.0" encoding="UTF-8"?>
  <context:property-placeholder location="/WEB-INF/connection.properties"/>
  <context:annotation-config/>
  <bean class="com.xhive.spring.XhiveSessionAccess"/>
  <bean id="transactionManager" class="com.xhive.spring.XhiveTransactionManager"/>
  <bean id="dataSource" class="com.xhive.spring.XhiveDataSource">
    <constructor-arg value="${sessionPool.bootstrap}"/>
    <constructor-arg value="${sessionPool.database}"/>
    <constructor-arg value="${sessionPool.username}"/>
    <constructor-arg value="${sessionPool.password}"/>
  </bean>
</beans>

This beans definition file configures three important beans: XhiveDataSource, XhiveTransactionManager, and XhiveSessionAccess.

 

  • XhiveDataSource is comparable to a JDBC session pool. This class holds the XhiveDriverIf reference and pools XhiveSessionIf objects for our application. We first read our connection settings from a properties file, connection.properties in the WEB-INF folder of our war file, and then pass the bootstrap, database, username, and password values for XhiveDataSource's constructor.
  • XhiveTransactionManager is a Spring TransactionManager that will handle transactions for us according to Spring's rules. In particular, it will open transactions, make sure to commit them or roll them back after web requests, and handle configuration such as the readOnly = true flag from above.
  • XhiveSessionAccess provides user code with access to the xDB transaction. We need to explicitly tell Spring about its presence, so that Spring can automatically inject it into our user code.

 

To make configuration easier, we use the <context:annotation-config/> directive. This means that the interdependencies of data source, transaction manager, and session access will be resolved automatically for us by Spring, based on annotations in those classes. If this is not desired, we could pass the data source as an explicit parameter to the transaction manager and session access.

 

In a regular Spring application, this context would probably contain more beans and configuration, but for our simple application this will suffice.

 

dispatch-servlet.xml

 

While the applicationContext.xml file configures global components of the application, each Spring servlet has its own configuration in a file usually called "servletname-servlet.xml". As we are using Spring's WebMVC, we will rely on the standard DispatcherServlet. This is its configuration file, dispatch-servlet.xml:

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  http://www.springframework.org/schema/context
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/context/spring-context-3.2.xsd  http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
  <context:component-scan base-package="test"/>
  <tx:annotation-driven/>
  <bean id="xsltViewResolver" class="org.springframework.web.servlet.view.xslt.XsltViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.xslt.XsltView"/>
    <property name="sourceKey" value="xmlSource"/>
    <property name="suffix" value=".xsl"/>
    <property name="prefix" value="/WEB-INF/xsl/"/>
  </bean>
</beans>

 

 

 

 

 

 

<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  http://www.springframework.org/schema/context
http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/context/spring-context-3.2.xsd  http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
  <context:component-scan base-package="test"/>
  <tx:annotation-driven/>
  <bean id="xsltViewResolver" class="org.springframework.web.servlet.view.xslt.XsltViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.xslt.XsltView"/>
    <property name="sourceKey" value="xmlSource"/>
    <property name="suffix" value=".xsl"/>
    <property name="prefix" value="/WEB-INF/xsl/"/>
  </bean>
</beans>







 

The two lines <context:component-scan base-package="test"/> and <tx:annotation-driven/> tell Spring to configure our application code based on annotations. This lets Spring automatically pick up our TestController, as it has the @Controller annotation. The tx:annotation-driver configuration tells Spring to use declarative transactions, again based on annotations like our @Transactional annotation.

 

The third item in the configuration is an XSLT based view resolver. As we are returning XML fragments, the natural view technology to use are XSL transformations. Effectively, when our controller returns a ModelAndView with the view name "main" (as above), Spring will look for an XSL stylesheet called main.xsl in the WEB-INF/xsl/ directory.

 

All of this servlet configuration is standard Spring.

 

web.xml

 

As in any Java web application, the web.xml configures the application for the servlet container. In our application, this means setting up the Spring context listener and Spring's dispatcher servlet.

 

<web-app id="blog" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee  http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <display-name>spring-xdb-example</display-name>
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <filter>
    <filter-name>closeSessionAfterView</filter-name>
    <filter-class>com.xhive.spring.CloseXhiveSessionAfterViewFilter</filter-class>
  </filter>
  <servlet>
    <servlet-name>dispatch</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatch</servlet-name>
    <url-pattern>*.html</url-pattern>
  </servlet-mapping>
    <filter-mapping>
    <filter-name>closeSessionAfterView</filter-name>
    <servlet-name>dispatch</servlet-name>
  </filter-mapping>
</web-app>







 

The only xDB-specific thing in this configuration file is the closeSessionAfterView filter. Normally, Spring will automatically close transactions after the particular code snippet that is annotated with @Transactional. Because we want to use XSL transformations on xDB documents for the view rendering, which happens after the controller code, we need to keep the transaction open until all rendering has happened. The CloseXhiveSessionAfterViewFilter does exactly that for us.

 

Running the Example

 

This article has a zip file attached, containing an example application based on the xDB Spring adaptor (spring-xdb-example.zip). Running the example requires:

 

 

To run the example, follow these instructions:

 

  1. Download spring-xdb-example.zip and unpack it somewhere on your hard drive.
  2. Adjust the xDB connection settings in the file src/main/webapp/WEB-INF/connection.properties. The example assumes you have an xDB server running locally at port 1235 (which is the default), and a database called MyDatabase with an administrator password "secret". Adjust this to your local settings, or create a database like this.
  3. In the folder where you unpacked spring-xdb-example.zip, run "mvn jetty:run" on the command line. This will compile the sources and start a Jetty web server running our example application.
  4. Point a web browser to http://localhost:8080/test-Martin.html. A greeting and the current time should be displayed.
  5. If you want to play around with the example, type "mvn eclipse:eclipse" in the spring-xdb-example directory. This will create a Java project that you can import in Eclipse.

 

Troubleshooting

 

  • If you get a build error from Maven that it cannot satisfy the dependency on com.xhive:xdb:10.x.x, make sure that you installed xDB itself properly as described above in the prerequisites.
  • If you get an exception with the message CONNECTION_REFUSED when running the project, make sure that you have an xDB server running, and that it is running on the port configured in connection.properties.
  • If you run into any other issues, please leave a comment below.