Well, finally I’m writing the third part of the blog. The thing that pushed me to finish this was a talk I had with Tim Hall of Oracle-base fame after his Unconference presentation in Oracle OpenWorld. Tim told me that his Java developers are claiming that adding user context information in an already existing application (Swing) is a non trivial task. You know, I’ve been hearing this from a lot of our customers and while I agree it is not trivial, I will try to outline a method of doing so without changing application code. In this day and age when there are advanced tools such as AspectJ and Spring framework, adding cross-cutting concerns to an application should not be an insurmountable task.

So, without further ad0, I will detail an AspectJ aspect that will wrap around an Oracle connection and add user context information to every statement. This aspect can be used with existing programs and also adapted and extended to catch login information in a Swing based application. I will build of the previous examples in providing the necessary infrastructure of domain and DAO classes.

The first thing I will show is the actual AspectJ aspect:

package com.slaviks_blog.aspects;

import java.sql.Connection;
import java.sql.SQLException;

import oracle.jdbc.OracleConnection;
import oracle.jdbc.pool.OracleDataSource;
import oracle.jdbc.pool.OraclePooledConnection;

import com.slaviks_blog.auth.AuthContainer;
import com.slaviks_blog.domain.Context;

/**
* This aspect will inject code into the getConnection and close of a logical
* connection to set and clear user info
*/
public aspect ConnectionMetricsAdvisor
{
/**
* A pointcut catching all the getConnection methods
*/
private pointcut oracleGetConnection() :
execution(public Connection OracleDataSource.getConnection(..);

/**
* A pointcut catching the close of an OraclePooledConnection
*/
private pointcut oracleCloseLogicalConnection(Connection conn) :
this(conn) && execution(public void OraclePooledConnection.close());
/**
* An after advice for the getConnection. We should only be sure that we are catching
* only that uppermost getConnection method so as not to call the API many times
*/
after() returning (Connection conn) : oracleGetConnection() && !cflowbelow(oracleGetConnection())
{
try
{
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
Context c = AuthContainer.getCurrentContext();
// The details from the annotation
metrics[OracleConnection.END_TO_END_ACTION_INDEX] = c.getAction();
metrics[OracleConnection.END_TO_END_MODULE_INDEX] = c.getModule();
// The client identifier from the external tier (be that web or something else)
metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = c.getClientIdentifier();
// Set these metrics
((OracleConnection) conn).setEndToEndMetrics(metrics, (short) 0);
}
catch (SQLException sqle)
{
// For now, just ignore the exception (should be logged later on)
}
}
/**
* A before advice on closing the connection.
* We clear the user context information.
*/
before(Connection conn) : oracleCloseLogicalConnection(conn)
{
try
{
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
metrics[OracleConnection.END_TO_END_ACTION_INDEX] = null;
metrics[OracleConnection.END_TO_END_MODULE_INDEX] = null;
metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = null;
Connection physicalConn = ((OraclePooledConnection) conn).getPhysicalHandle();
((OracleConnection) physicalConn).setEndToEndMetrics(metrics,
Short.MIN_VALUE);
}
catch (SQLException sqle)
{
// For now, just ignore the exception (should be logged later on)
}
}
}

As you can see, it’s a fairly simple implementation of an aspect but since this is a proof of concept blog entry, I leave it to the curious reader to further expand this aspect to catch login information and set it to the AuthContainer.

The rest of the example can be directly taken from the previous blog entry on the subject. I’ll just add here the data-access.xml file for completeness.

<?xml version=\”1.0\” encoding=\”UTF-8\”?>
<beans xmlns=\”
http://www.springframework.org/schema/beans\”
xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\”
xmlns:aop=\”http://www.springframework.org/schema/aop\”
xmlns:tx=\”http://www.springframework.org/schema/tx\”
xsi:schemaLocation=\”http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd\”>

<!–================================================================–>
<!– Datasource –>
<!–================================================================–>
<bean id=\”dataSource\” class=\”oracle.jdbc.pool.OracleDataSource\”
destroy-method=\”close\”>
<property name=\”URL\”
value=\”jdbc:oracle:thin:@localhost:1521:db1110\” />
<property name=\”user\” value=\”scott\” />
<property name=\”password\” value=\”tiger\” />
<property name=\”connectionCachingEnabled\” value=\”true\” />
</bean>

<!–================================================================–>
<!– Transaction Manager for a datasource using Hibernate –>
<!–================================================================–>
<bean id=\”transactionManager\”
class=\”org.springframework.jdbc.datasource.DataSourceTransactionManager\”>
<property name=\”dataSource\” ref=\”dataSource\” />
</bean>

<tx:annotation-driven transaction-manager=\”transactionManager\” />
<!–================================================================–>
<!– Data Access Objects (DAOs) –>
<!–================================================================–>

<bean id=\”testDao\” class=\”com.slaviks_blog.dao.jdbc.TestDaoJdbc\”>
<property name=\”dataSource\”>
<ref local=\”dataSource\”></ref>
</property>
</bean>
</beans>

And, for the sharp eyed reader who might have noticed – yes, I’m running this test on a 11g database and it’s working without changing anything at all. Go Oracle.