/*
* Bitronix Transaction Manager
*
* Copyright (c) 2010, Bitronix Software.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package bitronix.tm.resource.jdbc.proxy;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.Set;
import javax.sql.XAConnection;
import bitronix.tm.resource.jdbc.JdbcPooledConnection;
import bitronix.tm.resource.jdbc.LruStatementCache.CacheKey;
import bitronix.tm.resource.jdbc.PooledConnectionProxy;
import bitronix.tm.resource.jdbc.lrc.LrcXAResource;
import bitronix.tm.utils.ClassLoaderUtils;
/**
* This class generates JDBC proxy classes using stardard java.lang.reflect.Proxy
* implementations.
*
* @author Brett Wooldridge
*/
public class JdbcJavaProxyFactory implements JdbcProxyFactory {
private ProxyFactory<Connection> proxyConnectionFactory;
private ProxyFactory<XAConnection> proxyXAConnectionFactory;
private ProxyFactory<Statement> proxyStatementFactory;
private ProxyFactory<CallableStatement> proxyCallableStatementFactory;
private ProxyFactory<PreparedStatement> proxyPreparedStatementFactory;
JdbcJavaProxyFactory() {
proxyConnectionFactory = createProxyConnectionFactory();
proxyXAConnectionFactory = createProxyXAConnectionFactory();
proxyStatementFactory = createProxyStatementFactory();
proxyCallableStatementFactory = createProxyCallableStatementFactory();
proxyPreparedStatementFactory = createProxyPreparedStatementFactory();
}
/** {@inheritDoc} */
public Connection getProxyConnection(JdbcPooledConnection jdbcPooledConnection, Connection connection) {
try {
ConnectionJavaProxy jdbcConnectionProxy = new ConnectionJavaProxy(jdbcPooledConnection, connection);
return proxyConnectionFactory.getConstructor().newInstance(jdbcConnectionProxy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
public Statement getProxyStatement(JdbcPooledConnection jdbcPooledConnection, Statement statement) {
try {
StatementJavaProxy jdbcStatementProxy = new StatementJavaProxy(jdbcPooledConnection, statement);
return proxyStatementFactory.getConstructor().newInstance(jdbcStatementProxy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
public CallableStatement getProxyCallableStatement(JdbcPooledConnection jdbcPooledConnection, CallableStatement statement) {
try {
CallableStatementJavaProxy jdbcStatementProxy = new CallableStatementJavaProxy(jdbcPooledConnection, statement);
return proxyCallableStatementFactory.getConstructor().newInstance(jdbcStatementProxy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
public PreparedStatement getProxyPreparedStatement(JdbcPooledConnection jdbcPooledConnection, PreparedStatement statement, CacheKey cacheKey) {
try {
PreparedStatementJavaProxy jdbcStatementProxy = new PreparedStatementJavaProxy(jdbcPooledConnection, statement, cacheKey);
return proxyPreparedStatementFactory.getConstructor().newInstance(jdbcStatementProxy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
public XAConnection getProxyXaConnection(Connection connection) {
try {
LrcXAConnectionJavaProxy jdbcLrcXaConnectionProxy = new LrcXAConnectionJavaProxy(connection);
return proxyXAConnectionFactory.getConstructor().newInstance(jdbcLrcXaConnectionProxy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
public Connection getProxyConnection(LrcXAResource xaResource, Connection connection) {
try {
LrcConnectionJavaProxy lrcConnectionJavaProxy = new LrcConnectionJavaProxy(xaResource, connection);
return proxyConnectionFactory.getConstructor().newInstance(lrcConnectionJavaProxy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
// ---------------------------------------------------------------
// Generate high-efficiency Java Proxy Classes
// ---------------------------------------------------------------
private ProxyFactory<Connection> createProxyConnectionFactory() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(Connection.class);
interfaces.add(PooledConnectionProxy.class);
return new ProxyFactory<Connection>(interfaces.toArray(new Class<?>[0]));
}
private ProxyFactory<Statement> createProxyStatementFactory() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(Statement.class);
return new ProxyFactory<Statement>(interfaces.toArray(new Class<?>[0]));
}
private ProxyFactory<PreparedStatement> createProxyPreparedStatementFactory() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(PreparedStatement.class);
return new ProxyFactory<PreparedStatement>(interfaces.toArray(new Class<?>[0]));
}
private ProxyFactory<CallableStatement> createProxyCallableStatementFactory() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(CallableStatement.class);
return new ProxyFactory<CallableStatement>(interfaces.toArray(new Class<?>[0]));
}
private ProxyFactory<XAConnection> createProxyXAConnectionFactory() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(Connection.class);
interfaces.add(XAConnection.class);
return new ProxyFactory<XAConnection>(interfaces.toArray(new Class<?>[0]));
}
public static class ProxyFactory<T> {
private final Class<?>[] interfaces;
private Reference<Constructor<T>> ctorRef;
public ProxyFactory(Class<?>[] interfaces) {
this.interfaces = interfaces;
}
public T newInstance(InvocationHandler handler) {
if (handler == null)
throw new NullPointerException();
try {
return getConstructor().newInstance(new Object[] { handler });
} catch (Exception e) {
throw new InternalError(e.toString());
}
}
@SuppressWarnings("unchecked")
private synchronized Constructor<T> getConstructor() {
Constructor<T> ctor = ctorRef == null ? null : ctorRef.get();
if (ctor == null) {
try {
ctor = (Constructor<T>) Proxy.getProxyClass(getClass().getClassLoader(), interfaces)
.getConstructor(new Class[] { InvocationHandler.class });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
ctorRef = new SoftReference<Constructor<T>>(ctor);
}
return ctor;
}
}
}