Commit 5c06b7a8 authored by Matt Tucker's avatar Matt Tucker Committed by matt

Finished work on dynamic proxies.

git-svn-id: http://svn.igniterealtime.org/svn/repos/wildfire/trunk@6102 b35dd754-fafc-0310-a699-88a17e54d16e
parent cd86edce
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.database;
import java.sql.*;
import java.util.Map;
/**
* An implementation of the Connection interface that wraps an underlying
* Connection object.
*
* @author Gaston Dombiak
*/
public abstract class AbstractConnection implements Connection {
protected Connection connection;
public AbstractConnection(Connection connection) {
this.connection = connection;
}
public Statement createStatement() throws SQLException {
return connection.createStatement();
}
public PreparedStatement prepareStatement(String sql) throws SQLException {
return connection.prepareStatement(sql);
}
public CallableStatement prepareCall(String sql) throws SQLException {
return connection.prepareCall(sql);
}
public String nativeSQL(String sql) throws SQLException {
return connection.nativeSQL(sql);
}
public void setAutoCommit(boolean autoCommit) throws SQLException {
connection.setAutoCommit(autoCommit);
}
public boolean getAutoCommit() throws SQLException {
return connection.getAutoCommit();
}
public void close() throws SQLException {
connection.close();
}
public void commit() throws SQLException {
connection.commit();
}
public void rollback() throws SQLException {
connection.rollback();
}
public boolean isClosed() throws SQLException {
return connection.isClosed();
}
public DatabaseMetaData getMetaData() throws SQLException {
return connection.getMetaData();
}
public void setReadOnly(boolean readOnly) throws SQLException {
connection.setReadOnly(readOnly);
}
public boolean isReadOnly() throws SQLException {
return connection.isReadOnly();
}
public void setCatalog(String catalog) throws SQLException {
connection.setCatalog(catalog);
}
public String getCatalog() throws SQLException {
return connection.getCatalog();
}
public void setTransactionIsolation(int level) throws SQLException {
connection.setTransactionIsolation(level);
}
public int getTransactionIsolation() throws SQLException {
return connection.getTransactionIsolation();
}
public SQLWarning getWarnings() throws SQLException {
return connection.getWarnings();
}
public void clearWarnings() throws SQLException {
connection.clearWarnings();
}
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException {
return connection.createStatement(resultSetType, resultSetConcurrency);
}
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
}
public Map<String, Class<?>> getTypeMap() throws SQLException {
return connection.getTypeMap();
}
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
connection.setTypeMap(map);
}
public void setHoldability(int holdability) throws SQLException {
connection.setHoldability(holdability);
}
public int getHoldability() throws SQLException {
return connection.getHoldability();
}
public Savepoint setSavepoint() throws SQLException {
return connection.setSavepoint();
}
public Savepoint setSavepoint(String name) throws SQLException {
return connection.setSavepoint(name);
}
public void rollback(Savepoint savepoint) throws SQLException {
connection.rollback(savepoint);
}
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
connection.releaseSavepoint(savepoint);
}
public Statement createStatement(int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException
{
return connection.createStatement(resultSetType, resultSetConcurrency,
resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability) throws SQLException
{
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency,
resultSetHoldability);
}
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException
{
return connection.prepareCall(sql, resultSetType, resultSetConcurrency,
resultSetHoldability);
}
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException
{
return connection.prepareStatement(sql, autoGeneratedKeys);
}
public PreparedStatement prepareStatement(String sql, int columnIndexes[]) throws SQLException {
return connection.prepareStatement(sql, columnIndexes);
}
public PreparedStatement prepareStatement(String sql, String columnNames[])
throws SQLException {
return connection.prepareStatement(sql, columnNames);
}
}
\ No newline at end of file
......@@ -134,29 +134,29 @@ public class ConnectionPool implements Runnable {
// Check to see if there are any connections available. If not, then enter wait-based
// retry loop
ConnectionWrapper con = getCon();
ConnectionWrapper wrapper = getCon();
if (con != null) {
synchronized (con) {
con.checkedout = true;
con.lockTime = System.currentTimeMillis();
if (wrapper != null) {
synchronized (wrapper) {
wrapper.checkedout = true;
wrapper.lockTime = System.currentTimeMillis();
}
return con;
return wrapper.getConnection();
}
else {
synchronized (waitLock) {
try {
waitingForCon++;
while (true) {
con = getCon();
wrapper = getCon();
if (con != null) {
if (wrapper != null) {
--waitingForCon;
synchronized (con) {
con.checkedout = true;
con.lockTime = System.currentTimeMillis();
synchronized (wrapper) {
wrapper.checkedout = true;
wrapper.lockTime = System.currentTimeMillis();
}
return con;
return wrapper.getConnection();
}
else {
waitLock.wait();
......@@ -482,5 +482,4 @@ public class ConnectionPool implements Runnable {
throw new SQLException(e.getMessage());
}
}
}
}
\ No newline at end of file
......@@ -11,11 +11,10 @@
package org.jivesoftware.database;
import org.jivesoftware.database.AbstractConnection;
import org.jivesoftware.database.ConnectionPool;
import java.sql.Connection;
import java.sql.SQLException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* An implementation of the Connection interface that wraps an underlying
......@@ -24,18 +23,30 @@ import java.sql.SQLException;
*
* @author Jive Software
*/
public class ConnectionWrapper extends AbstractConnection {
public class ConnectionWrapper {
public ConnectionPool pool;
public boolean checkedout = false;
public long createTime;
public long lockTime;
public long checkinTime;
public Exception exception;
public boolean hasLoggedException = false;
private static Method close;
static {
try {
close = Connection.class.getMethod("close");
}
catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
ConnectionPool pool;
boolean checkedout = false;
long createTime;
long lockTime;
long checkinTime;
Exception exception;
boolean hasLoggedException = false;
private Connection poolConnection;
public ConnectionWrapper(Connection connection, ConnectionPool pool) {
super(connection);
setConnection(connection);
this.pool = pool;
createTime = System.currentTimeMillis();
......@@ -44,28 +55,24 @@ public class ConnectionWrapper extends AbstractConnection {
}
public void setConnection(Connection connection) {
super.connection = connection;
}
/**
* Instead of closing the underlying connection, we simply release
* it back into the pool.
*/
public void close() throws SQLException {
synchronized (this) {
checkedout = false;
checkinTime = System.currentTimeMillis();
if (connection == null) {
this.poolConnection = null;
}
else {
this.poolConnection = (Connection)java.lang.reflect.Proxy.newProxyInstance(
connection.getClass().getClassLoader(),
connection.getClass().getInterfaces(),
new ConnectionProxy(connection));
}
}
pool.freeConnection();
// Release object references. Any further method calls on the connection will fail.
// super.connection = null;
public Connection getConnection() {
return poolConnection;
}
public String toString() {
if (connection != null) {
return connection.toString();
if (poolConnection != null) {
return poolConnection.toString();
}
else {
return "Jive Software Connection Wrapper";
......@@ -75,4 +82,47 @@ public class ConnectionWrapper extends AbstractConnection {
public synchronized boolean isCheckedOut() {
return checkedout;
}
}
/**
* Dynamic proxy for connection object that returns connection to the pool when
* closing.
*/
public class ConnectionProxy implements InvocationHandler {
private Connection connection;
public ConnectionProxy(Connection connection) {
this.connection = connection;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.equals(close)) {
close();
return null;
}
else {
// Invoke the method normally if all else fails.
return method.invoke(connection, args);
}
}
/**
* Instead of closing the underlying connection, we simply release
* it back into the pool.
*
* @throws SQLException if an SQL Exception occurs.
*/
private void close() throws SQLException {
synchronized (this) {
checkedout = false;
checkinTime = System.currentTimeMillis();
}
pool.freeConnection();
// Release object references. Any further method calls on the connection will fail.
poolConnection = null;
connection = null;
}
}
}
\ No newline at end of file
......@@ -59,6 +59,9 @@ public class DbConnectionManager {
/**
* Returns a database connection from the currently active connection
* provider. (auto commit is set to true).
*
* @return a connection.
* @throws SQLException if a SQL exception occurs.
*/
public static Connection getConnection() throws SQLException {
if (connectionProvider == null) {
......@@ -95,7 +98,7 @@ public class DbConnectionManager {
// See if profiling is enabled. If yes, wrap the connection with a
// profiled connection.
if (profilingEnabled) {
return new ProfiledConnection(con);
return (Connection)ProfiledConnection.newInstance(con);
}
else {
return con;
......@@ -105,6 +108,9 @@ public class DbConnectionManager {
/**
* Returns a Connection from the currently active connection provider that
* is ready to participate in transactions (auto commit is set to false).
*
* @return a connection with transactions enabled.
* @throws SQLException if a SQL exception occurs.
*/
public static Connection getTransactionConnection() throws SQLException {
Connection con = getConnection();
......@@ -117,6 +123,10 @@ public class DbConnectionManager {
/**
* Closes a PreparedStatement and Connection. However, it first rolls back the transaction or
* commits it depending on the value of <code>abortTransaction</code>.
*
* @param pstmt the prepared statement to close.
* @param con the connection to close.
* @param abortTransaction true if the transaction should be rolled back.
*/
public static void closeTransactionConnection(PreparedStatement pstmt, Connection con,
boolean abortTransaction)
......@@ -135,6 +145,9 @@ public class DbConnectionManager {
/**
* Closes a Connection. However, it first rolls back the transaction or
* commits it depending on the value of <code>abortTransaction</code>.
*
* @param con the connection to close.
* @param abortTransaction true if the transaction should be rolled back.
*/
public static void closeTransactionConnection(Connection con, boolean abortTransaction) {
// test to see if the connection passed in is null
......@@ -195,6 +208,8 @@ public class DbConnectionManager {
* ConnectionManager.closePreparedStatement(pstmt);
* }
* } </pre>
*
* @param rs the result set to close.
*/
public static void closeResultSet(ResultSet rs) {
try {
......@@ -261,6 +276,7 @@ public class DbConnectionManager {
* ConnectionManager.closeConnection(rs, pstmt, con);
* }</pre>
*
* @param rs the result set.
* @param stmt the statement.
* @param con the connection.
*/
......@@ -417,6 +433,8 @@ public class DbConnectionManager {
* method should be called is if more information about the current
* connection provider is needed. Database connections should always be
* obtained by calling the getConnection method of this class.
*
* @return the connection provider.
*/
public static ConnectionProvider getConnectionProvider() {
return connectionProvider;
......@@ -487,9 +505,10 @@ public class DbConnectionManager {
* different JDBC drivers have different capabilities and methods for
* retrieving large text values.
*
* @param rs the ResultSet to retrieve the text field from.
* @param rs the ResultSet to retrieve the text field from.
* @param columnIndex the column in the ResultSet of the text field.
* @return the String value of the text field.
* @throws SQLException if an SQL exception occurs.
*/
public static String getLargeTextField(ResultSet rs, int columnIndex) throws SQLException {
if (isStreamTextRequired()) {
......@@ -539,6 +558,7 @@ public class DbConnectionManager {
* @param pstmt the PreparedStatement to set the text field in.
* @param parameterIndex the index corresponding to the text field.
* @param value the String to set.
* @throws SQLException if an SQL exception occurs.
*/
public static void setLargeTextField(PreparedStatement pstmt, int parameterIndex,
String value) throws SQLException {
......@@ -621,6 +641,9 @@ public class DbConnectionManager {
/**
* Uses a connection from the database to set meta data information about
* what different JDBC drivers and databases support.
*
* @param con the connection.
* @throws SQLException if an SQL exception occurs.
*/
private static void setMetaData(Connection con) throws SQLException {
DatabaseMetaData metaData = con.getMetaData();
......
......@@ -26,8 +26,11 @@ import java.lang.reflect.Method;
*
* @author Jive Software
*/
public class ProfiledConnection extends AbstractConnection {
public class ProfiledConnection implements InvocationHandler {
/**
* The type of the database operation.
*/
public enum Type {
/**
......@@ -581,56 +584,90 @@ public class ProfiledConnection extends AbstractConnection {
//--------------------- Connection Wrapping Code ---------------------//
// Preloaded Method objects that we override.
private static Method close;
private static Method createStatement;
private static Method createStatementWithParams;
private static Method prepareStatement;
private static Method prepareStatementWithParams;
private static Method prepareCall;
private static Method prepareCallWithParams;
static {
try {
close = Connection.class.getMethod("close");
createStatement = Connection.class.getMethod("createStatement");
createStatementWithParams = Connection.class.getMethod("createStatement",
int.class, int.class);
prepareStatement = Connection.class.getMethod("prepareStatement", String.class);
prepareStatementWithParams = Connection.class.getMethod("prepareStatement",
String.class, int.class, int.class);
prepareCall = Connection.class.getMethod("prepareCall", String.class);
prepareCallWithParams = Connection.class.getMethod("prepareCall",
String.class, int.class, int.class);
/**
* Creates a new ProfiledConnection that wraps the specified connection.
*
* @param connection the Connection to wrap and collect stats for.
*/
public ProfiledConnection(Connection connection) {
super(connection);
}
public void close() throws SQLException {
// Close underlying connection.
if (connection != null) {
connection.close();
}
catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
public Statement createStatement() throws SQLException {
// Returned a TimedStatement so that we can do db timings.
return (Statement)TimedStatement.newInstance(connection.createStatement());
}
public PreparedStatement prepareStatement(String sql) throws SQLException {
// Returned a TimedPreparedStatement so that we can do db timings.
return (PreparedStatement)TimedPreparedStatement.newInstance(
connection.prepareStatement(sql), sql);
}
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException
{
return (Statement)TimedStatement.newInstance(connection.createStatement(resultSetType,
resultSetConcurrency));
public static Object newInstance(Connection con) {
return java.lang.reflect.Proxy.newProxyInstance(
con.getClass().getClassLoader(),
con.getClass().getInterfaces(),
new ProfiledConnection(con));
}
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
return (PreparedStatement)TimedPreparedStatement.newInstance(
connection.prepareStatement(sql, resultSetType, resultSetConcurrency), sql);
}
private Connection connection;
public CallableStatement prepareCall(String sql) throws SQLException {
return (CallableStatement)TimedCallableStatement.newInstance(
connection.prepareCall(sql), sql);
/**
* Creates a new ProfiledConnection that wraps the specified connection.
*
* @param connection the Connection to wrap and collect stats for.
*/
private ProfiledConnection(Connection connection) {
this.connection = connection;
}
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
return (CallableStatement)TimedCallableStatement.newInstance(
connection.prepareCall(sql, resultSetType, resultSetConcurrency), sql);
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.equals(close)) {
// Close underlying connection.
if (connection != null) {
connection.close();
}
return null;
}
else if (method.equals(prepareStatement)) {
return TimedPreparedStatement.newInstance(
connection.prepareStatement((String)args[0]), (String)args[0]);
}
else if (method.equals(prepareStatementWithParams)) {
return TimedPreparedStatement.newInstance(
connection.prepareStatement((String)args[0], (Integer)args[1], (Integer)args[2]),
(String)args[0]);
}
else if (method.equals(createStatement)) {
return TimedStatement.newInstance(connection.createStatement());
}
else if (method.equals(createStatementWithParams)) {
return TimedStatement.newInstance(connection.createStatement(
(Integer)args[0], (Integer)args[1]));
}
else if (method.equals(prepareCall)) {
return TimedCallableStatement.newInstance(
connection.prepareCall((String)args[0]), (String)args[0]);
}
else if (method.equals(prepareCallWithParams)) {
return TimedCallableStatement.newInstance(
connection.prepareCall((String)args[0], (Integer)args[1], (Integer)args[2]),
(String)args[0]);
}
else {
// Invoke the method normally if all else fails.
return method.invoke(connection, args);
}
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment