/* * Bitronix Transaction Manager * * Copyright (c) 2012, 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.reflect.Method; import java.lang.reflect.InvocationHandler; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Statement; import java.util.Map; import java.util.Set; import javax.sql.XAConnection; 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 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 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 Class<Connection> proxyConnectionClass; private Class<Statement> proxyStatementClass; private Class<CallableStatement> proxyCallableStatementClass; private Class<PreparedStatement> proxyPreparedStatementClass; // For LRC we just use the standard Java Proxies private JdbcJavaProxyFactory lrcProxyFactory; JdbcCglibProxyFactory() { proxyConnectionClass = createProxyConnectionClass(); proxyStatementClass = createProxyStatementClass(); proxyCallableStatementClass = createProxyCallableStatementClass(); proxyPreparedStatementClass = createProxyPreparedStatementClass(); lrcProxyFactory = new JdbcJavaProxyFactory(); } /** {@inheritDoc} */ 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} */ 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} */ 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} */ 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} */ public XAConnection getProxyXaConnection(Connection connection) { return lrcProxyFactory.getProxyXaConnection(connection); } /** {@inheritDoc} */ 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(); } // --------------------------------------------------------------- // CGLIB Classes // --------------------------------------------------------------- static class FastDispatcher implements LazyLoader { private Object delegate; public FastDispatcher(Object delegate) { this.delegate = delegate; } public Object loadObject() throws Exception { return delegate; } } static class Interceptor implements MethodInterceptor { private InvocationHandler interceptor; public Interceptor(InvocationHandler interceptor) { this.interceptor = interceptor; } public Object intercept(Object enhanced, Method method, Object[] args, MethodProxy fastProxy) throws Throwable { return interceptor.invoke(interceptor, method, args); } } static class InterceptorFilter implements CallbackFilter { private Map<String, Method> methodMap; public InterceptorFilter(JavaProxyBase<?> proxyClass) { methodMap = proxyClass.getMethodMap(); } public int accept(Method method) { if (methodMap.containsKey(JavaProxyBase.getMethodKey(method))) { // Use the Interceptor return 1; } // Use the FastDispatcher return 0; } } }