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.xmlfile in a declarative fashion for all of the updatable contentInterposition 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.