/*
* Copyright (C) 2006-2013 Bitronix Software (http://www.bitronix.be)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package bitronix.tm.resource.jdbc.proxy;
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;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.LazyLoader;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import javax.sql.XAConnection;
import java.lang.reflect.Method;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Map;
import java.util.Set;
/**
* This class generates JDBC proxy classes using CGLIB bytecode generated
* implementations. This factory's proxies are more efficient than JdbcJavaProxyFactory
* but less efficient than JdbcJavassistProxyFactory.
*
* @author Brett Wooldridge
*/
public class JdbcCglibProxyFactory implements JdbcProxyFactory {
private final Class<Connection> proxyConnectionClass;
private final Class<Statement> proxyStatementClass;
private final Class<CallableStatement> proxyCallableStatementClass;
private final Class<PreparedStatement> proxyPreparedStatementClass;
private final Class<ResultSet> proxyResultSetClass;
// For LRC we just use the standard Java Proxies
private final JdbcJavaProxyFactory lrcProxyFactory;
JdbcCglibProxyFactory() {
proxyConnectionClass = createProxyConnectionClass();
proxyStatementClass = createProxyStatementClass();
proxyCallableStatementClass = createProxyCallableStatementClass();
proxyPreparedStatementClass = createProxyPreparedStatementClass();
proxyResultSetClass = createProxyResultSetClass();
lrcProxyFactory = new JdbcJavaProxyFactory();
}
/** {@inheritDoc} */
@Override
public Connection getProxyConnection(JdbcPooledConnection jdbcPooledConnection, Connection connection) {
ConnectionJavaProxy methodInterceptor = new ConnectionJavaProxy(jdbcPooledConnection, connection);
Interceptor interceptor = new Interceptor(methodInterceptor);
FastDispatcher fastDispatcher = new FastDispatcher(connection);
try {
Connection connectionCglibProxy = proxyConnectionClass.newInstance();
((Factory) connectionCglibProxy).setCallbacks(new Callback[] { fastDispatcher, interceptor });
return connectionCglibProxy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
@Override
public Statement getProxyStatement(JdbcPooledConnection jdbcPooledConnection, Statement statement) {
StatementJavaProxy methodInterceptor = new StatementJavaProxy(jdbcPooledConnection, statement);
Interceptor interceptor = new Interceptor(methodInterceptor);
FastDispatcher fastDispatcher = new FastDispatcher(statement);
try {
Statement statementCglibProxy = proxyStatementClass.newInstance();
((Factory) statementCglibProxy).setCallbacks(new Callback[] { fastDispatcher, interceptor });
return statementCglibProxy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
@Override
public CallableStatement getProxyCallableStatement(JdbcPooledConnection jdbcPooledConnection, CallableStatement statement) {
CallableStatementJavaProxy methodInterceptor = new CallableStatementJavaProxy(jdbcPooledConnection, statement);
Interceptor interceptor = new Interceptor(methodInterceptor);
FastDispatcher fastDispatcher = new FastDispatcher(statement);
try {
CallableStatement statementCglibProxy = proxyCallableStatementClass.newInstance();
((Factory) statementCglibProxy).setCallbacks(new Callback[] { fastDispatcher, interceptor });
return statementCglibProxy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
@Override
public PreparedStatement getProxyPreparedStatement(JdbcPooledConnection jdbcPooledConnection, PreparedStatement statement, CacheKey cacheKey) {
PreparedStatementJavaProxy methodInterceptor = new PreparedStatementJavaProxy(jdbcPooledConnection, statement, cacheKey);
Interceptor interceptor = new Interceptor(methodInterceptor);
FastDispatcher fastDispatcher = new FastDispatcher(statement);
try {
PreparedStatement statementCglibProxy = proxyPreparedStatementClass.newInstance();
((Factory) statementCglibProxy).setCallbacks(new Callback[] { fastDispatcher, interceptor });
return statementCglibProxy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
@Override
public ResultSet getProxyResultSet(Statement statement, ResultSet resultSet) {
ResultSetJavaProxy methodInterceptor = new ResultSetJavaProxy(statement, resultSet);
Interceptor interceptor = new Interceptor(methodInterceptor);
FastDispatcher fastDispatcher = new FastDispatcher(resultSet);
try {
ResultSet resultSetCglibProxy = proxyResultSetClass.newInstance();
((Factory) resultSetCglibProxy).setCallbacks(new Callback[] { fastDispatcher, interceptor });
return resultSetCglibProxy;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/** {@inheritDoc} */
@Override
public XAConnection getProxyXaConnection(Connection connection) {
return lrcProxyFactory.getProxyXaConnection(connection);
}
/** {@inheritDoc} */
@Override
public Connection getProxyConnection(LrcXAResource xaResource, Connection connection) {
return lrcProxyFactory.getProxyConnection(xaResource, connection);
}
// ---------------------------------------------------------------
// Generate CGLIB Proxy Classes
// ---------------------------------------------------------------
@SuppressWarnings("unchecked")
private Class<Connection> createProxyConnectionClass() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(Connection.class);
interfaces.add(PooledConnectionProxy.class);
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(interfaces.toArray(new Class<?>[0]));
enhancer.setCallbackTypes(new Class[] {FastDispatcher.class, Interceptor.class} );
enhancer.setCallbackFilter(new InterceptorFilter(new ConnectionJavaProxy()));
return enhancer.createClass();
}
@SuppressWarnings("unchecked")
private Class<PreparedStatement> createProxyPreparedStatementClass() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(PreparedStatement.class);
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(interfaces.toArray(new Class<?>[0]));
enhancer.setCallbackTypes(new Class[] {FastDispatcher.class, Interceptor.class} );
enhancer.setCallbackFilter(new InterceptorFilter(new PreparedStatementJavaProxy()));
return enhancer.createClass();
}
@SuppressWarnings("unchecked")
private Class<Statement> createProxyStatementClass() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(Statement.class);
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(interfaces.toArray(new Class<?>[0]));
enhancer.setCallbackTypes(new Class[] {FastDispatcher.class, Interceptor.class} );
enhancer.setCallbackFilter(new InterceptorFilter(new StatementJavaProxy()));
return enhancer.createClass();
}
@SuppressWarnings("unchecked")
private Class<CallableStatement> createProxyCallableStatementClass() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(CallableStatement.class);
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(interfaces.toArray(new Class<?>[0]));
enhancer.setCallbackTypes(new Class[] {FastDispatcher.class, Interceptor.class} );
enhancer.setCallbackFilter(new InterceptorFilter(new CallableStatementJavaProxy()));
return enhancer.createClass();
}
@SuppressWarnings("unchecked")
private Class<ResultSet> createProxyResultSetClass() {
Set<Class<?>> interfaces = ClassLoaderUtils.getAllInterfaces(ResultSet.class);
Enhancer enhancer = new Enhancer();
enhancer.setInterfaces(interfaces.toArray(new Class<?>[0]));
enhancer.setCallbackTypes(new Class[] {FastDispatcher.class, Interceptor.class} );
enhancer.setCallbackFilter(new InterceptorFilter(new ResultSetJavaProxy()));
return enhancer.createClass();
}
// ---------------------------------------------------------------
// CGLIB Classes
// ---------------------------------------------------------------
static class FastDispatcher implements LazyLoader {
private final Object delegate;
public FastDispatcher(Object delegate) {
this.delegate = delegate;
}
@Override
public Object loadObject() throws Exception {
return delegate;
}
}
static class Interceptor implements MethodInterceptor {
private final JavaProxyBase<?> interceptor;
public Interceptor(JavaProxyBase<?> interceptor) {
this.interceptor = interceptor;
}
@Override
public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy fastProxy) throws Throwable {
interceptor.proxy = enhanced;
return interceptor.invoke(interceptor, method, args);
}
}
static class InterceptorFilter implements CallbackFilter {
private final Map<String, Method> methodMap;
public InterceptorFilter(JavaProxyBase<?> proxyClass) {
methodMap = proxyClass.getMethodMap();
}
@Override
public int accept(Method method) {
if (methodMap.containsKey(JavaProxyBase.getMethodKey(method))) {
// Use the Interceptor
return 1;
}
// Use the FastDispatcher
return 0;
}
}
}