Sample Code
The first part of this example illustrates BaseConnectionDecorator and BaseStatementDecorator classes. These classes define base delegation semantics for the JDBC Connection and Statement interfaces, respectively. They implement JDBC's java.sql.Connection and java.sql.Statement interfaces by dispatching all operations to a reference instance that is assigned in their constructors.
public class BaseConnectionDecorator
implements Connection {
private Connection reference;
public BaseConnectionDecorator(Connection reference) {
this.reference = reference;
}
public void close() throws SQLException {
reference.close();
}
public Statement createStatement() throws SQLException {
return reference.createStatement();
}
// Repeat for the rest of the operations
// that the Connection interface defines.
// ...
}
public class BaseStatementDecorator
implements Statement {
private Statement reference;
public BaseStatementDecorator(Statement reference) {
this.reference = reference;
}
public void close() throws SQLException {
reference.close();
}
public ResultSet executeQuery(String sql)
throws SQLException {
return reference.executeQuery(sql);
}
// Repeat for the rest of the operations
// that the Statement interface defines.
// ...
}
The next set of classes defines concrete decorator implementations called LeakDetectionConnectionDecorator and LeakDetectionStatementDecorator. These classes print messages whenever the caller creates and closes connections and statements. In addition, they log the number of connections and statements open at a given time. The messages are intended to be a debugging aid. If either number continually increases, it is a good indication that there is a resource leak in the client code.
Notice how LeakDetectionConnectionDecorator implements createStatement by wrapping the concrete Statement object with a LeakDetectionStatementDecorator. This is an example of a factory operation. A full implementation of this solution would do the same for PreparedStatement, CallableStatement, and ResultSet objects.
public class LeakDetectionConnectionDecorator
extends BaseConnectionDecorator {
private static int openConnections = 0;
private static int nextId = 0;
private int id;
private PrintStream out;
/**
Constructs a LeakDetectionConnectionDecorator object.
@param reference The reference Connection implementation.
@param out The print stream to which this object writes
debug messages.
*/
public LeakDetectionConnectionDecorator(
Connection reference, PrintStream out) {
// Initialize the reference and print stream.
super(reference);
this.out = out;
// Assign this connection a unique identifier.
id = nextId++;
// Report that a connection has been created and
// dump the current stack trace.
out.println("Connection " + id + " was created. "
+ "Open connections = " + (++openConnections)
+ ".");
(new Throwable()).printStackTrace(out);
}
public void close() throws SQLException {
// Delegate the close operation.
super.close();
// Report that a connection has been closed.
out.println("Connection " + id + " was closed. "
+ "Open connections = " + (--openConnections)
+ ".");
}
public Statement createStatement() throws SQLException {
// Wrap the concrete Statement in a
// LeakDetectionStatementDecorator object.
Statement statement = super.createStatement();
return new LeakDetectionStatementDecorator(
statement, out);
}
// There is no need to override methods to which we are
// not attaching additional behavior. To complete this
// example, we need to override the other methods that
// create Statements to decorate all such objects
// appropriately.
}
public class LeakDetectionStatementDecorator
extends BaseStatementDecorator {
private static int openStatements = 0;
private static int nextId = 0;
private int id;
private PrintStream out;
/**
Constructs a LeakDetectionStatementDecorator object.
@param reference The reference Statement implementation.
@param out The print stream to which this object writes
debug messages.
*/
public LeakDetectionStatementDecorator(
Statement reference, PrintStream out) {
// Initialize the reference and print stream.
super(reference);
this.out = out;
// Assign this statement a unique identifier.
id = nextId++;
// Report that a statement has been created and
// dump the current stack trace.
out.println("Statement " + id + " was created. "
+ "Open statements = " + (++openStatements)
+ ".");
(new Throwable()).printStackTrace(out);
}
public void close() throws SQLException {
// Delegate the close operation.
super.close();
// Report that a statement has been closed.
out.println("Statement " + id + " was closed. "
+ "Open statements = " + (--openStatements)
+ ".");
}
public ResultSet executeQuery(String sql)
throws SQLException {
// Wrap the concrete ResultSet in a
// LeakDetectionResultSetDecorator object.
// (This class is not included in this example,
// but is similar to this one.)
ResultSet resultSet = super.executeQuery(sql);
return new LeakDetectionResultSetDecorator(
resultSet, out);
}
// There is no need to override methods to which we are
// not attaching additional behavior. To complete this
// example, we need to override the other methods that
// create ResultSets to decorate all such objects
// appropriately.
}
This sample application code shows the only additional line needed to attach leak detection functionality to a JDBC Connection object. The fact that the application incorporates the additional functionality with a single line of code makes it easy to use it during development and then remove it for production.
// Get the appropriate physical connection.
Connection connection = DriverManager.getConnection(...);
// Attach leak detection to the connection.
connection = new LeakDetectionConnectionDecorator(connection, System.out);
// The rest of the application works the same with
// or without the additional functionality.
// ...
You can easily augment this code to attach the additional behavior based on a configuration file or flag. A more extensive approach is to package the LeakDetectionConnectionDecorator and related classes as a discrete JDBC driver complete with its own URL and connection property definitions. This enables you to plug it in to nearly any application that allows you to configure a JDBC driver.
 |