Print
Integrate IBatis With Legacy System

This article shows a real scenario where dimple helped a lot.

Background

A legacy system uses a ConnectionManager class to manage connections:

public class ConnectionManager {
  public static Connection checkOut();
  public static void checkIn(Connection connection);
}

Client code is required to call ConnectionManager.checkIn(connection) to release a connection.

To Integrate iBatis

Things all work fine until an architect tries to bring in iBatis to handle persistence.

The best way to integrate the legacy system with iBatis is to implement a javax.sql.DataSource and a com.ibatis.sqlmap.engine.datasource.DataSourceFactory.

In order to implement DataSource, the Connection object checked out should be wrapped so that the "close" method calls ConnectionManager.checkIn().

Here goes pseudo code that does not compile:

class LegacyConnection implements Connection {
  private boolean closed = false;
  private final Connection realConnection;
  public void close() {
    if(closed) return;
    ConnectionManager.checkIn(realConnection);
    closed = true;
  }
  public boolean isClosed() {
    return closed;
  }
  //All other methods delegate to realConnection.
}
class LegacyDataSource implements DataSource {
  public Connection getConnection() {
    return new LegacyConnection(ConnectionManager.checkOut());
  }
  public PrintWriter getLogWriter() {
    return null;// do not support tracing
  }
  public int getLoginTimeout() {
    return 0; // do not support timeout
  }
  //All other methods throw UnsupportedOperationException
}
public class LegacyDataSourceFactory implements DataSourceFactory {
  public void initialize(Map props) {}
  public DataSource getDataSource() {
    return new LegacyDataSource();
  }
}
  • LegacyConnection implements Connection.close() and Connection.isClosed() method to perform ConnectionManager.checkIn() as well as to make sure isClosed() return true when close() is called.
  • LegacyDataSource calls ConnectionManager.checkOut() when getConnection() is called. The checked out connection is then wrapped up in a LegacyConnection to implement the close logic.
  • LegacyDataSourceFactory implements iBatis DataSourceFactory and simply return a LegacyDataSource object.

The ibatis config file can then be configured using the "transactionManager" as:

  <transactionManager type="JDBC">
    <dataSource type="com.mycompany.mylegacysystem.LegacyDataSourceFactory"/>
  </transactionManager>

However, obviously the pseudo code doesn't compile because the "All other methods" in Connection interface and DataSource interface need yet to be implemented.

One could use the IDE's code generation support to "Generate Delegate Methods" for LegacyConnection and to "Implement/Override Methods" for LegacyDataSource. This technique works but has some serious drawbacks:

  1. It has versioning problem. If you are generating code for jdbc 3.0, it probably won't compile for jdbc 4.0 because some new methods may have been added to Connection interface or DataSource interface or possibly both.
  2. Code generation in Eclipse may be easy, but the hundreds of lines of generated code may be annoying.
  3. You need to generate delegate method for deprecated methods, this generates compiler warning and worry (what if this method is later removed from the interface? The delegate method will not compile!)

Dimple Solution

Using dimple, we can be rid of versioning problem and deprecated method problem. The code will become:

class LegacyConnection /*implements Connection*/ {
  private boolean closed = false;
  private final Connection realConnection;
  public void close() {
    if(closed) return;
    ConnectionManager.checkIn(realConnection);
    closed = true;
  }
  public boolean isClosed() {
    return closed;
  }
}
class LegacyDataSource /*implements DataSource*/ {
  public Connection getConnection() {
    Connection realConnection = ConnectionManager.checkOut();
    return Implementor.proxy(Connection.class, 
      new LegacyConnection(realConnection), realConnection);
  }
  public PrintWriter getLogWriter() {
    return null;// do not support tracing
  }
  public int getLoginTimeout() {
    return 0; // do not support timeout
  }
}
public class LegacyDataSourceFactory implements DataSourceFactory {
  public void initialize(Map props) {}
  public DataSource getDataSource() {
    return Implementor.proxy(DataSource.class, new LegacyDataSource());
  }
}
  • Comment out "implements Connection" and "implements DataSource".
  • Use dimple Implementor to convert LegacyConnection to Connection and LegacyDataSource to DataSource.

Created by benyu

On Sun Jan 07 10:59:51 CST 2007

Using TimTam

Powered by Atlassian Confluence