ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Learning Servlet Filters
Pages: 1, 2, 3

The Magical Connection Pool Manager

Let us investigate where this magic is taking place, allowing us to return the same connection when executed on the same thread. Obviously, the trick lies within the implementation of the IConnectionPoolManager class. As a matter of good practice, the implementation of this class is obtained via a factory, allowing us to migrate to an EJB instance when it is justified, while keeping the rest of the code unchanged.



interface IConnectionPoolManager
{
  SQLConnection getConnection(String dataSourceName) throws SQLException;
  void putConnection(SQLConnection con) throws SQLException;
}

TransactionalConnectionPool implements IConnectionPoolManager
{
  // Hashtable<Thread,SQLConnection>
  Hashtable threadVsConnection = new Hashtable();

  String m_getConnectionLock = "lock";

  SQLConnection getConnection(String datasourceName) throws SQLException
  {
    SQLConnection con = (SQLConnection)threadVsConnection.get(datasourceName);
    if (con != null)
    {
      return con;
    }
    // no connection available
    synchronized(m_getConnectionLock)
    {
      SQLConnection con = (SQLConnection)threadVsConnection.get(datasourceName);
      if (con != null)
      {
        return con;
      }
      con = m_dataSources.get(dataSourceName).getConnection();
      threadVsConnection.put(getCurrentThread,con);
    }
  }

  void putConnection(SQLConnection con) throws SQLException
  {
    .. return connection to the underlying data source either by closing it or returning it
  }

  // called by the filter to remove any
  // connections associated with this thread
  void removeAssociation()
  {
    threadVsConnection.remove(getCurrentThread());
  }
  
  // returns the current thread
  Thread getCurrentThread()
  {
    ....
  }
  
  void commitForThisThread()
  {
    SQLConnection con = threadVsConnection.get(getCurrentThread());
    ..commit logic
  }
  void rollbackForThisThread()
  {
    SQLConnection con = threadVsConnection.get(getCurrentThread());
    .. rollbacklogic
  }
}

As you can see, when you ask the connection pool manager to give you a connection, it checks to see if the current thread is already associated with a connection. If it is, the connection pool manager will return the same connection. If not, it will contact the necessary data source, obtain a connection, and register with the current thread. Eventually, the filter will remove this threadVsConnection association when it is no longer required.

Now that we have seen how the connection pool manager coordinates threading with connections, it is time to see how the filters could use this property to effectively demarcate transactions.

Filter Code

The references included in this article are sufficient to understand filters. Unlike EJBs and like Servlets, the spec is small and can be mastered in a single sitting. To summarize the spec: the servlet container will call a series of Java objects that are registered for a given URL. These objects are called filters. Each filter will control the input and output of the downstream filter. The servlet itself is conceptually at the end of this chain. Besides the initialization and destroy methods, there is only one significant method that we need to worry about. And the code below overrides that doFilter method to intercept calls to the intended servlets requiring transactional support.

class TransactionFilter implements Filter
{
  // An implementation of the IConnectionPoolManager
  // and obtainedat the filter initialization
  private TransactionConnectionPoolManager icpm = null;
  ..
  void doFilter( ServletRequest request, ServletResponse response, FilterChain chain)
  {
    try
    {
      chain.doFilter(request,response,chain);
      icpm = Factory.getInstance().getObject(IConnectionPoolManager.Name);
      icpm.commitForThisThread();
    }
    catch(..)
    {
      .. analyze exception to see if this requires a rollback
      .. for commit
      icpm.rollbackForThisThread();
    }
    finally
    {
      // remove the connection from the connection pool
      if (icpm != null)
      {
        icpm.removeAssociation();
      }
    }
  }
}

The principle here is quite simple: at the end of the servlet request, just tell the connection pool manager that the transaction has ended (whether for good or bad) by calling its removeAssociation().

Observations

  • The developer is not responsible for commits or rollbacks. When there is no exception, the transaction is automatically commited

  • Allows a developer to write code as if a task is independent while allowing that task to be part of an another larger composed task

  • Filters can be selectively placed using the web.xml file in a declarative fashion for all of the updatable content

  • Interposition is nicely provided because of the interception priniciples of filters

Assumptions

  • This sample code is only demonstrating one connection per data source per one thread. This can be extended for multiple data sources, provided the underlying data sources support XAConnections.

  • Again, this is a lightweight transaction management that is highly practical for majority of the single database solutions

  • Nevertheless, this can be easily extended for multiple data sources for queries

  • In the case of updates and multiple databases, they are restricted to one per thread. That is, one thread could be using data source 1 to update, and thread 2 could be using another data source to update. And as mentioned earlier, to update multiple data sources on the same thread, one would at the least require XA support, and possibly JTA support.

Pages: 1, 2, 3

Next Pagearrow