Showing posts with label iBatis. Show all posts
Showing posts with label iBatis. Show all posts

Thursday, December 16, 2010

Populate Immutable Objects with iBatis Mapping

In a previous post, I provided iBatis configuration and mapping that supported populating Java objects on a per-property basis, i.e. the classes in use had to provide setters for this approach. Here I describe another iBatis pattern that populates immutable objects with more than a few properties.

Thursday, August 26, 2010

iBatis Mapping for One-to-Many Relationship

Perhaps you had a problem, like I did, understanding the iBatis writeup on loading complex collection properties, i.e. populating a list-based field in a domain object where that list is the many-side of a one-to-many relationship. I'll admit it took me a few iterations to totally grasp what was being said, so for my future reference and yours, I'll write down my notes here.

Monday, August 23, 2010

Handling Partially Failed Transactions

Requirements for a web application I've developed include submitting a form that results in many database operations, including a mix-and-match of updates, inserts and deletes. Since there can be hundreds of such operations, my initial approach was to execute them in a single batch for a reasonable response time. Now, if what you want is fast execution and truly an all-or-nothing transactional behavior, using batch execution is a good idea. However, on iteration with my customers, the requirements evolved into something a bit more sophisticated: if any operations in the submitted set fails - e.g. attempting to insert a duplicate row with a unique index - we want all other operations to succeed, and we want to identify the operations that did not succeed.

What's I've described of course is not strictly "transactional" - i.e. the all-or-nothing constraint is not in play, but for the application's needs, this is perfectly acceptable. And since what I describe involves removing the batch execution, the overall set of operations will take longer to execute - but again, this is acceptable for the application's goals. You won't always find these trade-offs to be acceptable, but when you do, I'll describe one mechanism for making it work - my strategy includes the following:

1 - Execute the collection of operations in a single transaction, albeit without batch
2 - Catch and deal with exceptions around operations that fail
3 - Provide return values that includes lists of successes and failures

Here are some snippets that provides these things. I start with an enumeration of the operation types I care about:
    private enum OP {add, update, delete}
Next I provide a result object that can be examined by the client to identify successes and failures. I use generics for type-safety - the Key and Value types represent your application-specific database key and object classes:
public class OperationResult {
    private Map<Key, Value> successes = new HashMap();
    private Map<Key, Exception> failures = new HashMap();

    public OperationResult(Map<Key, Value> succeeded, Map<Key, Value> failed) {
        successes.putAll(succeeded != null? succeeded : new HashMap());
        failures.putAll(failed != null ? failed : new HashMap());
    }

    public Map<Key, Value> getSuccesses() {
        return successes;
    }

    public Map<Key, Exception> getFailures() {
        return failures;
    }
}
Finally I provide the workhorse method that executes all operations, capturing exception details in the face of any failures, and proceeding in either event so it can commit as many successes as possible. Due to the way this API is set up, clients can execute only one operation type at a time:
    private OperationResult execute(Map<Key, Value> map, String query, OP operation) {
        Map<Key, Value> successes = new HashMap();
        Map<Key, Exception> failures = new HashMap();
        try {
            try {
                sqlMapClient.startTransaction();
            } catch (SQLException e) {
                throw new IllegalStateException("None of the operations succeeded - txn could not be started",
                    e);
            }

            for (Map.Entry<Key, Value> entry : map.entrySet()) {
                try {
                    switch (operation) {
                        case add:
                            return sqlMapClient.add(query, entry.getValue()); break;
                        case update:
                            return sqlMapClient.update(query, entry.getValue()); break;
                        case delete:
                            return sqlMapClient.delete(query, entry.getKey()); break;
                    }
                    successes.put(entry.getKey(), entry.getValue());
                } catch (Exception e) {
                    failures.put(entry.getKey(), e);
                }
            }

            try {
                sqlMapClient.commitTransaction();
            } catch (SQLException e) {
                throw new IllegalStateException("None of the operations succeeded - txn could not be committed",
                    e);
            }
        } finally {
            try {
                sqlMapClient.endTransaction();
            } catch (SQLException e) {
                throw new IllegalStateException("Problem ending the txn - you should check whether the operations succeeded or not",
                    e);
            }

            return new OperationResult(successes, failures);
        }
    }
I'm using iBatis 2.x, as you might be able to guess from the SQL Map Client reference; it is assumed that the client passes in the correct query ID for the given operation.

One guideline from my Computer Science education that always stuck with me is "make it right first, then make it fast". The solution provided here is, as noted, not going to be as fast as batch execution - but it does meet the requirements correctly. If there is still a need to make it "faster", we could consider using an asynchronous API so that the response time from the user's point of view is improved. This type of approach is in fact used in many enterprise settings, leveraging so called BASE behavior instead of the traditional ACID approach.

Wednesday, July 28, 2010

Log SQL Statements with iBatis

Here's what I've done to enable logging of SQL statements in an iBatis context. This adds to the various bits of advice I've found on the web.

Perhaps it's due to the environment I'm using (Java 1.6, iBatis 2.3.4, SLF4J 1.5.11, Log4J 1.2.15), but the guidance offered in the iBatis Dev Guide is, in my experience, not quite right. In fairness, something must have changed since last I tried that advice (was I using Java 1.5 back then? I'm not sure), since, as I recall, it resulted in the SQL logging I needed. However, at the moment I find that simply configuring this:
<logger name="java.sql.PreparedStatement">
    <level value="debug"/>
</logger>
...yields no SQL output at all.

On target is the advice given in the Apache Bookstore, i.e. to specify this:
<logger name="java.sql">
    <level value="debug"/>
</logger>
This yields all of connection information, prepared statements, values inserted as prepared statement parameters and result sets, something like this:
Connection
{conn-100008} Preparing Statement:  select name, description  from mytable where name like ?      
{pstm-100009} Executing Statement:  select name, description  from mytable  where name like ?               
{pstm-100009} Parameters: [name%]
{pstm-100009} Types: []
{rset-100010} ResultSet
{rset-100010} Header: [NAME, DESCRIPTION]
{rset-100010} Result: [name2, Desc for name2]
{rset-100010} Result: [name3, Desc for name3]
You'll possibly get even more output, depending on your app - since the java.sql namespace is more than just these things. If this is a concern, use the following:
    <logger name="java.sql.Connection">
        <level value="debug"/>
    </logger>

    <logger name="java.sql.PreparedStatement">
        <level value="debug"/>
    </logger>

    <logger name="java.sql.ResultSet">
        <level value="debug"/>
    </logger>
I've personally found that the output from PreparedStatement and ResultSet are the most useful - but omitting Connection appears to preclude these log statements from appearing. The Connection logger also appears to control the output level of PreparedStatement and ResultSet - i.e., setting Connection to INFO results in zero output from any of them. I begin to suspect that Connection has a parent relationship to the other two; perhaps this is what's changed since last I did this.

In either event, here are some SQL-interceptor tools recommended by various posts (though I've yet to try either one):

p6spy: http://www.p6spy.com/
jdbcdslog: http://code.google.com/p/jdbcdslog/

And here are all the web references I used for this post:

Stack Overflow:
http://stackoverflow.com/questions/3014318/how-to-do-ibatis-version-of-show-sql
http://stackoverflow.com/questions/2635058/ibatis-get-executed-sql
Apache Bookstore: http://www.apachebookstore.com/confluence/oss/display/IBATIS/How+do+I+get+SqlMapClient+to+log+SQL+statements
iBatis Dev Guide: http://ibatisnet.sourceforge.net/DevGuide.html#d0e2600

Monday, July 19, 2010

What Database Am I Connecting To?

If you run your functional tests in different environments (your local box, the nightly build, etc.), in addition to running your components in a production setting, you might want to get feedback at startup time around just what database you're connecting to. Since I was recently bit by nightly tests that were wiping out my development-local database tables - since I'd hardwired the connection URL - I was motivated to put this kind of feedback in place. Here are some details around how I did this.

Our nightly build has certain environment variables set:

dbip=BuildHost
dbinstance=TestDatabase
dbuser=username
dbpass=password

To simulate this environment, I set these same variables in my shell (Unix, Cygwin, ...), changing the values as needed to facilitate connecting to my local database. Next I set up a property configuration in my Spring startup file that uses these variables, replacing things per the environment:
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="properties">
            <value>
                database.driver=oracle.jdbc.OracleDriver
                database.url=jdbc:oracle:thin:@//${dbip}:1521/${dbinstance}
                database.user=${dbuser}
                database.password=${dbpass}
            </value>
        </property>
    </bean>
These Spring-generated properties are used to create a data source, which is used to configure an iBatis SQLMapClient, which I then use in my DAO:
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="${database.driver}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.user}"/>
        <property name="password" value="${database.password}"/>
    </bean>
....
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:SqlMapConfig.xml"/>
        <property name="dataSource" ref="dataSource"/>
    </bean>
....
    <bean id="ibatisDao" class="com.mybiz.resource.db.ibatis.IbatisDao">
        <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>
The DAO implements ApplicationContextAware so that it can examine the Spring context, in particular the properties of interest in the data source:
public class MyDao implements ApplicationContextAware
{
    private ApplicationContext context;

    public void setApplicationContext(ApplicationContext ctx)
    {
        context = ctx;
        dumpDataSource(ctx);
    }

    public void dumpDataSource(ApplicationContext ctx)
    {
        BasicDataSource dataSource =  ctx.getBean(BasicDataSource.class);
        logger.info("============>>> Data Source: " + dataSource.getUrl()
                + " (user " + dataSource.getUsername() + ")");
    }
}
Finally, I get myself into the habit of looking for that log output at the start of my test runs to be sure I'm connecting to the expected database. Once burned, twice shy.

Friday, July 17, 2009

Using Spring-iBatis to access LDAP - Part 2

Introduction

In the first half of this series, I described our experience with the latest Spring-LDAP module, concluding that an alternative mechanism would be needed due to deployment issues. I promised an alternative iBatis-over-LDAP solution, and in this article I provide demonstration code samples.
With this approach in place, we can view an LDAP repository as an ordinary SQL-based database, albeit with its own variant of SQL. Other advantages include the standard iBatis benefits, in particular the ability to declaratively specify SQL queries and as such manage them from a single point of change.

What follows is a description of how I put a quick-and-dirty prototype together, in particular using Spring 3.0.

Dependencies

The key to this solution is a JDBC driver that wraps LDAP repositories, created by Octet String (and now owned by Oracle). Once we have a JDBC driver, in theory we should be able to layer iBatis over it.

Download version 2.1 of that driver:
https://sourceforge.net/projects/myvd/files/

Put these jars from that download into your runtime classpath:

jdbcLdap.jar
ldap.jar

You'll need the standard set of jars for Spring 3.0 - for the minimal Spring 3.0 prototyping I've done so far, here is the set that has worked for me:

commons-lang.jar
commons-logging.jar
antlr-3.0.1.jar
org.springframework.asm-3.0.0.M3.jar
org.springframework.beans-3.0.0.M3.jar
org.springframework.context-3.0.0.M3.jar
org.springframework.core-3.0.0.M3.jar
org.springframework.expression-3.0.0.M3.jar

Add in iBatis:

iBatis-2.3.4.jar

Add what's needed to support Spring 3.0 over iBatis:

commons-dbcp.jar 
com.springsource.org.apache.commons.pool-1.4.0.jar
org.springframework.jdbc-3.0.0.M3.jar
org.springframework.orm-3.0.0.M3.jar
org.springframework.transaction-3.0.0.M3.jar

The last jar listed above is the Spring 3.0 version of transactions. The 2.5.6 version is required for Spring-LDAP, and since the classes in both jars are largely (if not completely) the same, this raises a warning flag about classloading conflicts. I didn't get far enough into a test execution to hit any problems; instead I found that deploying Spring-LDAP with Spring 3.0 AOP causes a runtime error out of the gate (during Spring initialization), and at that point I bailed out on use of Spring LDAP (please refer to my first article for the details).

Finally, though it's not necessary for this example, I include the Spring-AOP libraries, if for nothing else to demonstrate that we won't see the same runtime error as happened using Spring-LDAP with Spring 3.0. You can safely exclude these for your own prototyping of this solution (but if so, just be sure to remove the AOP references in the Spring configuration file described below):

com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.runtime-1.6.5.RELEASE.jar
com.springsource.org.aspectj.weaver-1.6.5.RELEASE.jar
org.springframework.aop-3.0.0.M3.jar


Code Artifacts


Now you're ready to do some coding. First, construct the domain object:

package com.mybiz.model;

public class User {

    private String uid;

    public User() {}
   
    public String getUid() { return uid; }
    public void setUid(String uid) { this.uid = uid; }

    @Override
    public String toString() { return this.uid; }
}

Construct the DAO for the domain object. I recommend using an interface and its implementation - because (1) using interfaces is in general a good idea for testability, polymorphism, and etc., and (2) in particular with AOP-proxied classes in Spring, I'd favor this approach because it facilitates use of JDK dynamic proxies instead of CGLIB proxies. Use of CGLIB involves these things: (1) more library dependencies, (2)  final methods cannot participate in AOP, and (3) constructors are called twice due to CGLIB implementation details. Again, use of an interface is an optional step; if you exclude it, you'll need the CGLIB jars (which I haven't used - and plan to avoid using - so I can't point you to the exact set needed).

In either event, here's the DAO interface and implementation:

package com.mybiz.model;

import java.util.List;

public interface UserDao {
    public List getAllUsers();
}

package com.mybiz.model;

import com.ibatis.sqlmap.client.SqlMapClient;

import java.sql.SQLException;
import java.util.List;

public class MyUserDao implements UserDao { 

    // injected via Spring
    private SqlMapClient sqlMapClient;
    public void setSqlMapClient(SqlMapClient sqlMapClient) { this.sqlMapClient = sqlMapClient; }
    private SqlMapClient getSqlMapClient() { return this.sqlMapClient; }

    public List<user> getAllUsers()
    {
        String query = "selectAll";
        try {
            return (List<user>)getSqlMapClient().queryForList(query);
        } catch (SQLException e) {
            System.err.println("Query '" + query + "' failed: " + e);
            throw new IllegalStateException(e);
        }
    }
}

The database properties are described in a standalone file, to facilitate easy modifications to suit different environments (test, development, production). The key here is to specify the ignore_transactions parameter in the URL; adding the search_scope as a default scope is an optional convenience so that this won't be needed in every one of your SQL queries. Let's name the file database.properties in the com/mybiz/model directory; you'll of course need to adjust the parameters here to match your environment:

database.driver=com.octetstring.jdbcLdap.sql.JdbcLdapDriver
database.url=jdbc:ldap://127.0.0.1:389/dc=mybiz,dc=com?search_scope:=subTreeScope&ignore_transactions:=true
database.user=cn=Manager,dc=mybiz,dc=com
database.password=verycleverpassword

Following through with my convention from the first half of this series, I've bold-faced the relevant configuration pieces so you can compare how things are specified. Above, we see that the database connection parameters (LDAP URL, base node specification, principal and password) have moved from the Spring configuration (when using Spring-LDAP, as per my previous article) and into this file.

Next, layer iBatis over the database connection. We need first the SQL Map Configuration file; it doesn't need to do anything but point to your collection of SQL Maps. Let's call it SqlMapConfig.xml in the com/mybiz/model directory:

<!DOCTYPE sqlMapConfig
        PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
        "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

<sqlMapConfig>
    <sqlMap resource="com/mybiz/model/SqlMap.xml"/>
</sqlMapConfig>

Here is the specified SQL Map. As per the above specification, it should be named SqlMap.xml in the com/mybiz/model directory:

<!DOCTYPE sqlMap
    PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
    "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="user">
  <typeAlias alias="user" type="com.mybiz.model.User"/>
  <select id="selectAll" resultClass="user">
    SELECT uid  FROM dc=mybiz,dc=com  where objectclass='person'
  </select>
</sqlMap>

Here, we have bold-faced the LDAP attribute to be fetched, the node from which the search begins, and the search qualifier. While these query parameters plus the database connection parameters were all in one place using Spring-LDAP, here they've been split up into separate files. It's your judgment as to whether this is a good thing or not; personally, I consider these things as separate concerns, so I prefer it. However, you might prefer single point of change for all things database-related and/or all things configuration-related. More importantly, we can now use iBatis' parameterized substitution to declare a query, keeping it readably intact while varying its parameter values dynamically. With Spring-LDAP, we can likewise achieve dynamic query construction, but the model is more procedural.

In the above SQL Map, you'll notice the interesting use of an LDAP base specification instead of a table name (and again, you'll need to adjust this query to match up with your LDAP repository structure). The docs for the JDBC-LDAP bridge do mention that one can map things to an aliased table name, but for our purposes here, transparency and simplicity are more important.

The next step is to configure iBatis with Spring. I included transactional and AOP support, though these are optional for this exercise. Some explanations:
  • Though LDAP itself is not transactional (hence the ignore_transactions parameter above), you might be in an environment where you'll also be accessing normal relational databases, for which you'll want Spring's transactional support. Since we're now solely Spring 3.0, there is in either event no fear of classloading conflicts. However, to make this a bare-bones proof-of-concept, you can exclude the tx namespace declaration in the beans tag, the transaction manager bean and the tx:annotation-driven tag.
  • This Spring config file also configures in AOP solely to demonstrate that there is no longer any deployment conflict as was seen with combining Spring-LDAP and Spring-3.0-AOP; it's not essential for the iBatis-over-LDAP solution per se. As I mentioned above, if you specify AOP in this Spring configuration, you'll need the Spring-AOP runtime jars. Again, to make this a bare-bones proof-of-concept, you can exclude the aop namespace declaration in the beans tag and the aop:aspectj-autoproxy tag.
  • For that matter, Spring itself is not essential to layering iBatis over LDAP - I was able to remove Spring from the stack entirely and get the same successful result. However, Spring provides various benefits to an iBatis application, so I include it here for completeness (and, frankly, because I rely on Spring to help me). If you exclude it, you'll need to configure iBatis manually; I do not include those details here.
Let's name the file beans.xml, in the com/mybiz/model directory:

<beans xmlns="http://www.springframework.org/schema/beans"   
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

    <!-- OPTIONAL: AOP -->
    <aop:aspectj-autoproxy/>

    <!-- support for referencing properties from the database.propeties file  in the data source bean, below -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:com/mybiz/model/database.properties"/>
    </bean>

    <!-- OPTIONAL: transaction manager - needed more for regular databases, not for LDAP -->
    <bean id="txnMgr" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dbcpDataSource"/>
    </bean>
    <!-- OPTIONAL -->
    <tx:annotation-driven transaction-manager="txnMgr"/>

    <!-- Apache DBCP data source, configured with properties from database.properties file -->
    <bean id="dbcpDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${database.driver}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.user}"/>
        <property name="password" value="${database.password}"/>
    </bean>

    <!-- iBatis SQL Map client, configured with the config-file location and the data source   -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:com/mybiz/model/SqlMapConfig.xml"/>
        <property name="dataSource" ref="dbcpDataSource"/>
    </bean>

    <!-- inject the iBatis SQL Map client into the application DAO -->
    <bean id="userDao" class="com.mybiz.model.MyUserDao">
        <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>
    
</beans>

You'll notice there are no bold-faced sections in the Spring configuration file (i.e., no application-specific configurations are specified). The database connection and database query configuration information are now in separate files dedicated to these responsibilities alone, vs being embedded in what's potentially a rather large Spring configuration. Again, your take on this may vary from mine: I consider this a good thing, but you might prefer all configuration information to be in a single file.

Finally, we provide a test driver program:

package com.mybiz.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mybiz.model.User;
import com.mybiz.model.UserDao;
import java.util.List;

public class IBatisOverLdapDemo {

    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext(
                "com/mybiz/model/beans.xml");
        UserDao dao = (UserDao) context.getBean("userDao");

        List<user> users = dao.getAllUsers();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

Executing this program, with database connection information, queries, and etc. tailored to your LDAP environment, should yield a list of attribute values as expected.

Conclusion

Using iBatis-over-LDAP instead of JNDI-LDAP, you can:
  • configure, connect to and query both RDBMS and LDAP databases in the same manner
  • be unencumbered by JNDI-LDAP programming models
  • gain all other benefits that iBatis has to offer, since the LDAP-ness has been (mostly) abstracted away. For example, fine-tuning queries for performance can be done without changing Java code.
Using iBatis-over-LDAP instead of Spring-LDAP, you gain all of the above advantages, plus these:
  • all SQL is established declaratively (vs being constructed programmatically), facilitating maintenance and debugging
  • as a result, the DAO needs fewer properties - i.e., with Spring LDAP, the DAO needs things like the LDAP base, LDAP attribute(s) to be returned and a search-filter qualifier to configure the search method. However, using iBatis, all of these things are simply part of the SQL statement specified in the SQL Map file.
Spring-LDAP has a lot to offer, including numerous features that this solution lacks, and for many applications will be very suitable. In my opinion, its strongest value-add is the compensating transactional control in an LDAP environment. If our application was more demanding than it is, I'd be motivated to dig further into the Spring-LDAP feature set and would likely be all the more impressed - in my opinion, what the Spring team has been doing is the best thing to ever happen to J2EE, and their LDAP work is likely no exception. But for our application needs - very simple CRUD + Spring 3.0 AOP - the Spring LDAP module is not the right choice. The iBatis-over-LDAP solution is clean and simple, meeting our requirements with a minimum of fuss.

Resources

Using Spring-iBatis to access LDAP - Part 1
, by Gary Horton, July 2009
MyVD Virtual Directory: JDBC-LDAP Bridge
Spring LDAP 1.3.x Reference Documentation, by Mattias Arthursson, Ulrik Sandberg, Eric Dalquist, 2009 Spring Framework
Spring Framework 3.0.M3 Reference Documentation, by Rod Johnson, et. al, 2009 Spring Framework

Thursday, July 16, 2009

Using Spring-iBatis to access LDAP - Part 1

Introduction

As the Spring team points out, the standard JNDI-LDAP programming model is cumbersome, at best - and there's no question that the Spring-LDAP module simplifies that problem considerably, though not as completely as some would prefer. In a 2008 article, Colin Lu describes a homegrown facade using an iBatis-style framework to address his concerns with Spring-LDAP. Our own experience with Spring-LDAP has been a mixed bag, and in this 2-part series, I'll discuss another approach that leverages iBatis-over-LDAP directly. This first article starts with the JNDI-LDAP approach, looks at the improvements available with Spring-LDAP, and describes our attempt to leverage Spring-LDAP in a Spring 3.0 environment. Since that attempt was not successful, my followup article will present an alternate solution that layers Spring-iBatis over LDAP.

JNDI-LDAP Example

Ill start with the plain-vanilla JNDI-LDAP model, and evolve it in two steps - first to Spring-LDAP, and secondly using Spring-iBatis. As presented by Sunil D. Patil in his 2007 article, an example class might look something like this (key pieces are bold-faced so you can keep track of them as we evolve the program):

public class PlainLDAPDemo {
    private static final String LDAP_BASE = "dc=mybiz,dc=com";
    private static final String LDAP_USERNAME = "uid";
    private static final String LDAP_PROVIDER_URL = "ldap://127.0.0.1:389";
    private static final String LDAP_SECURITY_AUTHENTICATION = "simple";
    private static final String LDAP_SECURITY_PRINCIPAL = "cn=Manager,dc=mybiz,dc=com";
    private static final String LDAP_SECURITY_CREDENTIALS = "verycleverpassword";

    public static void main(String[] args) {

        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, LDAP_PROVIDER_URL);
        env.put(Context.SECURITY_AUTHENTICATION, LDAP_SECURITY_AUTHENTICATION);
        env.put(Context.SECURITY_PRINCIPAL, LDAP_SECURITY_PRINCIPAL);
        env.put(Context.SECURITY_CREDENTIALS, LDAP_SECURITY_CREDENTIALS);

        DirContext ctx = null;
        NamingEnumeration results = null;
        try {
            ctx = new InitialDirContext(env);
            SearchControls searchControls = new SearchControls();
            searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            results = ctx.search(LDAP_BASE, "(objectclass=person)",
                    new String[]{LDAP_USERNAME}, searchControls);

            List<string> users = new ArrayList<string>();
            while (results.hasMore()) {
                SearchResult searchResult = (SearchResult) results.next();
                Attributes attributes = searchResult.getAttributes();
                Attribute attr = attributes.get(LDAP_USERNAME);
                String cn = (String) attr.get();
                users.add(cn);
            }
            Collections.sort(users);
            for (String user : users) {
                System.out.println(" UID = " + user);
            }

        } catch (NamingException e) {
            throw new RuntimeException(e);
        } finally {
            if (results != null) {
                try { results.close(); } catch (Exception ignored) {}
            }
            if (ctx != null) {
                try { ctx.close(); } catch (Exception ignored) {}
            }
        }
    }
}

As you can see, the bold-faced portions comprise the application-specific configuration, and are a small part of the overall program - most of the rest is boilerplate that can and should be factored out. That's what the Spring team has done for us.

Spring-LDAP Example

Mr. Patil does an excellent job of moving you from this verbose beginning to a lean Spring-LDAP treatment of the same program, so I won't repeat those details here. The transformation I came up with includes a test driver program, a DAO and a Spring configuration file. Here is my test driver:

public class SpringLDAPDemo {

    public static void main(String[] args) {

        Resource resource = new ClassPathResource("beans.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        UserDao ldapContact = (UserDao) factory.getBean("ldapContact");
        List<String> users = ldapContact.getAllUsers();

        Collections.sort(users);
        for (String user : users) {
            System.out.println(" UID  = " + user);
        }
    }
}

This is noticeably simpler client access. Here is the DAO:

public class UserDao { 

    private LdapTemplate ldapTemplate;
    private String ldapBase;
    private String ldapUsername; 
    private String searchQualifier; 

    public void setLdapTemplate(LdapTemplate ldapTemplate) { this.ldapTemplate = ldapTemplate; }

    public String getLdapBase() { return ldapBase; }

    public void setLdapBase(String ldapBase) { this.ldapBase = ldapBase; }

    public String getLdapUsername() { return ldapUsername; }

    public void setLdapUsername(String ldapUsername) { this.ldapUsername = ldapUsername; }

    public String getSearchQualifier() { return searchQualifier; }

    public void setSearchQualifier(String searchQualifier) { this.searchQualifier = searchQualifier; }

    public List getAllUsers() {

        return this.ldapTemplate.search(getLdapBase(), getSearchQualifier(),
                new AttributesMapper() {
                    public Object mapFromAttributes(Attributes attrs)
                            throws NamingException {
                        return attrs.get(getLdapUsername()).get();
                    }
                });
    }
}

Again, much cleaner than the original - perhaps even reusable. All of the application-specific configuration and most of the cruft has been factored out, thanks to Spring. Here's the Spring configuration that encapsulates application-specific details:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="contextSource"
          class="org.springframework.ldap.core.support.LdapContextSource">
        <property name="url" value="ldap://127.0.0.1:389"/>
        <property name="userDn" value="cn=Manager,dc=mybiz,dc=com"/>
        <property name="password" value="verycleverpassword"/>
        <property name="pooled" value="true"/>
    </bean>
    <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
        <constructor-arg ref="contextSource"/>
    </bean>
    <bean id="ldapContact" class="UserDao">
        <property name="ldapTemplate" ref="ldapTemplate"/>
        <property name="ldapBase" value="dc=mybiz,dc=com"/>
        <property name="ldapUsername" value="uid"/>
        <property name="searchQualifier" value="(objectclass=person)"/>
    </bean>
</beans>

Note that I'm working with the latest Spring-LDAP release, 1.3.0, and as such my packages and property names are slightly different from the 2.0.1-based example in Mr. Patil's article - in particular, please note the bold-italic sections above.

The Problem

Now, I'm also working with Spring 3.0 to gain the latest-and-greatest dependency injection, AOP and iBatis integration. Since the Spring LDAP Reference document claims that "...Spring LDAP 1.3 is supported on Spring 2.0 and later", I initially assumed I'd be OK. However, when my program is deployed as a standalone, it actually fails with this:

java.lang.ClassNotFoundException: org.springframework.dao.EmptyResultDataAccessException

I used an Ivy configuration to fetch dependencies for org.springframework.ldap, and in checking back with the artifacts from that retrieval, I found the missing class is available only in the 2.5.6 distribution (in particular, I needed org.springframework.transaction-2.5.6-SEC01.jar), so I would need to do some mixing-and-matching of 3.0 and 2.5.6 jars in my deployment to get this to work.

Yes, I had misgivings about this; the last thing I want is to experiment with combinations of libraries that might cause classloading or other runtime mismatches later on - a costly misadventure in a production environment. So my next step was to test drive the Spring-LDAP standalone with the 3.0 core and the 2.5.6 transaction libraries; this worked out just fine - the missing class is of course available and my simple test program succeeded without a problem. Next, I combined this program with another test driver that applies Spring AOP - but now this message appears:

Cannot convert value of type [$Proxy15 implementing org.springframework.ldap.core.LdapOperations,org.springframework.beans.factory.InitializingBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [org.springframework.ldap.core.LdapTemplate] for property 'ldapTemplate': no matching editors or conversion strategy

Removing Spring-LDAP from the mix of course remedies my AOP program - this is no surprise since the LdapTemplate is implicated in the error message above. Checking a little deeper, I find that the 2.5.6 transaction jar has many (if not all) of the same classes as the 3.0 transaction jar that I need to support my AOP configurations. So there's a growing doubt that I will succeed in a full-blown deployment, given the obvious concern around classloading problems, let alone how quickly I encountered the error as seen above.

Analysis of the Problem

Now, I could certainly take steps at this point to Google the error message (which, in fairness, I did - but I found no solution), experiment further, query the forums, and etcetera - but the cost/benefit of further effort here is a question mark. My mission is not to leverage Spring-LDAP; my mission is to establish a clean alternative to some existing JNDI-LDAP code. If Spring-LDAP worked without a hitch in my 3.0 deployment, I would choose that direction and move on. In fairness, and impressively enough, "it just works" has been the case for everything else I've tried so far with Spring 3.0 - injection, iBatis integration, AOP and in particular AOP-based transaction management. But here are some points about our application requirements, rationalizing why I'm reluctant to put much more effort into Spring-LDAP:
  1. Our LDAP-based use cases are dirt simple; this part of the application is not enterprise-scale. We need simple CRUD operations, nothing more. The path of least resistance here is to just wrap that CRUD in our own DAO facade and be done with it.
  2. The application itself, however, is enterprise-scale, and we're prototyping modern technologies like Spring to facilitate a rewrite of the web tier. The legacy web tier is a mix-and-match of more than a few technologies - Struts 1.x, Dojo, (massive amounts of) JavaScript, iFrames, JSTL, JSP, etc. - and is a poster child for a "how many different ways can we do the same thing?" approach. We are motivated this time to keep the technology selection down to a bare minimum, establish a minimal set of reusable patterns, choose frameworks that "just work" and think twice before going with things that have problems out of the gate. The resistance we're meeting here with Spring-LDAP in a 3.0-AOP context gives us cause for pause.
  3. While we could get around the deployment problems with Spring 3.0 AOP and 2.5.6 LDAP modules by isolating the LDAP component as a web service (e.g.), this begins to feel like overkill. This would add an extra network hop to retrieve information, and gives us one more point of failure (the new service) - this is probably not worth it, given #1 above, and especially since we'd be doing this only to enable use of Spring-LDAP. That's the tail wagging the dog.
Consider an Alternative Solution?

As such, I am now motivated to explore a second alternative to JNDI-LDAP, that of wrapping Spring-iBatis around a JDBC-LDAP bridge driver. But first, here are some points further validating my conclusion on #3 above, i.e. reluctance to use Spring LDAP in a separate service for our application:
  1. Clearly Spring-LDAP is a vast improvement over the JNDI-LDAP programming model. But, while the code is cleaner and simpler, it'sjust not that much code to worry about for our application (see #1 above).
  2. Spring-LDAP provides the ability to execute some business logic either before or after a search (but, we can also do this using regular Spring AOP, if I understand this feature correctly).
  3. We can optionally implement a method in the Spring-LDAP inner class that converts LDAP object into custom Java objects (but, we can also do this using an iBatis approach).
  4. Spring-LDAP provides the ability to create dynamic filters (but, this obscures the query being constructed since it's done in Java code. An iBatis approach simply externalizes the SQL in one place, supporting dynamic queries using parameterized templating).
  5. Likewise, basic CRUD idioms in Spring-LDAP are available; but these are also constructed procedurally in java code. Again, an iBatis approach is our preference.
  6. Spring-LDAP provides simplified "dynamic authentication" (i.e. an arbitrary user logging in to the LDAP system, as opposed to an implicitly declared principal as done in the example). However, this is not one of our requirements - the use of a single declaratively specified principal (via Spring configuration) is all we need.
  7. Spring-LDAP provides "compensating transaction support" - which is a solid improvement over the transaction-impaired LDAP environment - but we do not need this. From the Spring-LDAP docs: "The client side transaction support will add some overhead in addition to the work required by the original operations. While this overhead should not be something to worry about in most cases, if your application will not perform several LDAP operations within the same transaction (e.g. a modifyAttributes followed by a rebind), or if transaction synchronization with a JDBC data source is not required (see below) there will be nothing to gain by using the LDAP transaction support."
The list of Spring-LDAP features goes on, but for our application needs, none are compelling enough to warrant deploying it as a separate service (which for now is the only solution we have to get around the deployment issue with 3.0 AOP). In my next post, I'll present our solution that layers Spring-iBatis over the JDBC-LDAP bridge.

Resources

Spring Framework 1.3.x Reference Documentation, by Mattias Arthursson, Ulrik Sandberg, Eric Dalquist, 2009 Spring Framework
Simplify directory access with Spring LDAP, by Sunil D. Patil, 2007 JavaWorld
Extending Spring LDAP with an iBATIS-style XML Data Mapper, by Colin (Chun) Lu, 2008 JavaWorld
SpringSource Enterprise Bundle Repository
MyVD Virtual Directory: JDBC-LDAP Bridge
Simple Authentication Using Spring LDAP, by Mattias Arthursson, 2009 Spring Framework

Thursday, July 9, 2009

Spring 3.0 Cheatsheet: Integration with iBatis

This is the next in a series of tips, insights and recommendations around Spring 3.0. This time, I offer our experiences to-date around use of Spring's ORM facility, or more accurately its integration with iBatis (which is not, strictly speaking, an OR mapper).

iBatis - General
  • Use iBatis "implicit result mapping", i.e. adding an alias to each column in the select field list, to match up with Java object properties so iBatis will automatically map them for you (and as such, there's no need for resultMap constructs). For example, the following maps database column names (that might need to follow database naming conventions) to the property names of the corresponding Java object:
<sql id="allFieldsFromWebapp">
      APP_ID as appID,
      DISPLAY_NAME as displayName,
      DESCRIPTION as description,
      URL as url
    from WEBAPP
</sql>

Now, instead of referring to resultMap id's, reference the class to be populated:

<typeAlias alias="WebApp" type="com.mybiz.model.WebApp"/>
....
<select id="selectAll" resultClass="WebApp">
    select  <include refid="allFieldsFromWebapp"/>
</select>
<select id="selectById" parameterClass="String"  resultClass="WebApp">
    select <include refid="allFieldsFromWebapp"/>
    where APPID = #id#
</select>

Using the <sql>, <include> and <typeAlias> tags, we gain opportunity for reuse in the SQL map file.
  • Add JDBC type as suffix to placeholder parameters in iBatis queries, as needed:

UPDATE person SET
title = #title#,
given_names = #givenNames#,
last_name = #lastName#,
date_of_birth = #dateOfBirth:DATE#
WHERE id = #id#

In the above statement, #dateOfBirth:DATE# specifies day, month and year only - since, in this case, there's no need for hours, minutes and seconds.

  • To monitor iBatis-generated SQL queries, add log4j.logger.java.sql=DEBUG to your log4j configuration file; however, this shows the precompiled statements only, without parameter substitution.
iBatis-Spring: General Database
  • Factor connection details out of the iBatis SQL map configuration and into Spring beans configuration instead, to simplify the iBatis configuration (since these details often vary between environments (eg development, test, production)) and to consolidate application-wide configuration information into a single place. Put these connection details into e.g. an Apache DBCP DataSource bean. Use the Spring PropertyPlaceholderConfigurer bean to support the externalization.
In the Spring beans file:

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:database.properties"/>
    </bean>

    <bean id="dbcp-dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${database.driver}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.user}"/>
        <property name="password" value="${database.password}"/>
    </bean>

In database.properties, which exists in the default package of the classpath as per the above specification:


database.driver=oracle.jdbc.OracleDriver
database.url=jdbc:oracle:thin:@my-dev-host.mybiz.com:1521:XE
database.user=system
database.password=foobar


Now the SQL map configuration file is not concerned with connection details; it simply deals with SQL maps:

<sqlMapConfig>
  <sqlMap resource="com/mybiz/persistence/WebApp.xml"/>
</sqlMapConfig>

  • Initialize your DAO using Spring's SQL map client factory, handing it both the location of the iBatis SQL map configuration file and the DBCP data source bean as initialization parameters.
In the DAO, provide for setter-injection of an SQL map client, to be used elsewhere in your code to invoke iBatis queries:

package com.mybiz.persistence;

import com.ibatis.sqlmap.client.SqlMapClient;

public class MyDao implements DaoBase {

    private SqlMapClient sqlMapClient;

    public SqlMapClient getSqlMapClient() {
        return this.sqlMapClient;
    }

    public void setSqlMapClient(SqlMapClient sqlMapClient) {
        this.sqlMapClient = sqlMapClient;
    }

    .....

    public MyObjectCollection getAll() throws SQLException {
        MyObjectCollection coll= new MyObjectCollection ();
        coll.set((List) getSqlMapClient().queryForList("selectAll"));
        return coll;
    }
}

In the Spring beans configuration:

    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="classpath:com/twc/registry/persistence/WebAppDao.xml"/>
        <property name="dataSource" ref="dbcp-dataSource"/>
    </bean>

    <bean id="webapp-dao" class="com.twc.registry.persistence.WebAppDao">
        <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>


The SQL map client will automagically read in the SQL map configuration information.
Note that with the Spring XML beans file under ./WEB-INF, you must use the "classpath:" prefix to reference the iBatis SQL map configuration file.

Alternately, one can use the Spring SQL map client class instead of the iBatis version (see section 14.5 in the the Spring Reference Doc). The primary advantage here, as far as I can tell, is that formerly checked SQLExceptions are now wrapped in unchecked exceptions. However, I'd consider this optional, and for that matter undesirable in certain situations - e.g., in a RESTful web service where specific checked exceptions are caught so they can be mapped to corresponding HTTP response codes (and in just such a situation, I've chosen the iBatis SQL map client instead of the Spring version). The debate around documenting checked (and unchecked) exceptions as an important part of specifying program behavior is beyond the scope of this post; bottom line, sometimes wrapping exceptions that you can't do anything about with unchecked ones is useful, but sometimes your methods will intentionally throw their own checked exceptions that clients are meant to handle explicitly.

iBatis-Spring: Transactions
  • Use the Spring TransactionManager and annotation-driven transaction management to establish simple, declarative transactional behavior. Use the JDBC data source transaction manager unless you expect to be managing multiple resources in "global" transactions or want the application server to manager transactions (e.g. to take advantage of advanced features like transaction suspension, etc.), in which case use the JTA transaction manager. Downside of JTA transactions is lack of ability to test outside the container (since it requires JNDI and possibly other container functionality).You'll need to provide the AOP autoproxy tag and associated namespace attributes to get declarative transactions to work.
In the Spring beans file - note the transaction manager references the previously established DBCP data source:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       ">
    <bean id="txnMgr" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dbcp-dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="txnMgr"/>
    <aop:aspectj-autoproxy/>

  • Annotate DAO methods as appropriate (i.e. non-read-only unless you care about non-repeatable reads, etc.) with @Transactional. This tells Spring that a transaction is required and to start one if it's not already executing within a transactional context (because the default propagation is REQUIRED). That method body will execute as one transactional unit and will auto-commit if it completes successfully. If a runtime exception is thrown, the transaction will be rolled back. This becomes increasingly important as your java method executes more than one operation (multiple updates, delete + insert, etc.)
    @Transactional
    public void delete(String objId) {
        getSqlMapClient().delete("deleteById", objId);
    }
  • Use of Spring AOP means use of proxies; so, if you want to use JDK dynamic proxying instead of CGLIB proxying, your DAOs should implement an interface that specifies its public methods (as implied above with MyDao implements DaoBase). Dynamic proxying is recommended for these reasons: with CGLIB,
    • there are more library dependencies
    • final methods cannot participate in AOP
    • constructors are called twice due to CGLIB implementation details (see the Spring Reference manual, section 9.5.5).
  • While using interfaces supports annotating methods in the interface only - i.e. you don't need to maintain these in the implementation - this is considered risky by the Spring team; so, at a minimum annotate concrete classes, and optionally annotate interfaces also (the former for the functionality, optionally the latter for documentation).
  • How do you know you're getting transactional behavior? You can monitor transaction activity by turning on a log4j appender to debug for TransactionInterceptor; then, add and remove @Transactional to various methods in your interface to observe the logging statements. In a log4j.properties file:

log4j.logger.org.springframework.transaction.interceptor.TransactionInterceptor=debug


  • While transaction behaviors can be configured programmatically, this couples the code to Spring Framework and as such is not preferred.
  • Beware of self-invocation where you expect transactional behavior - unless there's already a transactional context, a new one will not be created. Consider the use of so-called AspectJ mode if you expect self-invocations to be wrapped with transactions as well (set tx:annotation-driven mode attribute to "proxy" - but this will require use of CGLIB libraries).

Resources


Spring and iBatis Tutorial
http://www.cforcoding.com/2009/06/spring-and-ibatis-tutorial.html

iBatis Wiki
http://opensource.atlassian.com/confluence/oss/display/IBATIS/Converting+iBATIS+DAO+to+Spring+DAO

iBatis FAQ
http://opensource.atlassian.com/confluence/oss/display/IBATIS/How+do+I+reuse+SQL-fragments

Spring Reference Manual
http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/pdf/spring-framework-reference.pdf