All Downloads are FREE. Search and download functionalities are using the official Maven repository.

bitronix.tm.resource.jdbc.proxy.JdbcJavassistProxyFactory Maven / Gradle / Ivy

There is a newer version: 62
Show newest version
/*
 * 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.internal.BitronixRuntimeException;
import bitronix.tm.resource.jdbc.JdbcPooledConnection;
import bitronix.tm.resource.jdbc.LruStatementCache.CacheKey;
import bitronix.tm.resource.jdbc.lrc.LrcXAResource;
import bitronix.tm.utils.ClassLoaderUtils;
import javassist.*;

import javax.sql.XAConnection;
import java.lang.reflect.Constructor;
import java.sql.*;
import java.util.HashSet;
import java.util.Set;

/**
 * This class generates JDBC proxy classes using Javassist bytecode generated
 * implementations.  This is the most efficient proxy factory.
 *
 * @author Brett Wooldridge
 */
public class JdbcJavassistProxyFactory
		implements JdbcProxyFactory
{

	private final ClassMap classMap;
	// For LRC we just use the standard Java Proxies
	private final JdbcJavaProxyFactory lrcProxyFactory;

	private ClassPool classPool;
	private Constructor proxyConnectionConstructor;
	private Constructor proxyStatementConstructor;
	private Constructor proxyCallableStatementConstructor;
	private Constructor proxyPreparedStatementConstructor;
	private Constructor proxyResultSetConstructor;

	/**
	 * Constructor JdbcJavassistProxyFactory creates a new JdbcJavassistProxyFactory instance.
	 */
	public JdbcJavassistProxyFactory()
	{
		classMap = new ClassMap();
		ClassPool defaultPool = ClassPool.getDefault();
		classPool = new ClassPool(defaultPool);
		classPool.insertClassPath(new ClassClassPath(this.getClass()));
		classPool.childFirstLookup = true;

		createProxyConnectionClass();
		createProxyStatementClass();
		createProxyCallableStatementClass();
		createProxyPreparedStatementClass();
		createProxyResultSetClass();

		lrcProxyFactory = new JdbcJavaProxyFactory();

		// Clear the map, we don't need it anymore
		classMap.clear();
		classPool = null;
	}

	/**
	 * Create a proxy class: class ConnectionJavassistProxy extends ConnectionJavaProxy implements java.sql.Connection
	 */
	private void createProxyConnectionClass()
	{
		try
		{
			Class proxyClass = generateProxyClass(Connection.class, ConnectionJavaProxy.class);
			proxyConnectionConstructor = proxyClass.getConstructor(JdbcPooledConnection.class, Connection.class);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * Create a proxy class: class StatementJavassistProxy extends StatementJavaProxy implements java.sql.Statement
	 */
	private void createProxyStatementClass()
	{
		try
		{
			Class proxyClass = generateProxyClass(Statement.class, StatementJavaProxy.class);
			proxyStatementConstructor = proxyClass.getConstructor(JdbcPooledConnection.class, Statement.class);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * Create a proxy class: class CallableStatementJavassistProxy extends CallableStatementJavaProxy implements java.sql.CallableStatement
	 */
	private void createProxyCallableStatementClass()
	{
		try
		{
			Class proxyClass = generateProxyClass(CallableStatement.class, CallableStatementJavaProxy.class);
			proxyCallableStatementConstructor = proxyClass.getConstructor(JdbcPooledConnection.class, CallableStatement.class);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * Create a proxy class: class PreparedStatementJavassistProxy extends PreparedStatementJavaProxy implements java.sql.PreparedStatement
	 */
	private void createProxyPreparedStatementClass()
	{
		try
		{
			Class proxyClass = generateProxyClass(PreparedStatement.class, PreparedStatementJavaProxy.class);
			proxyPreparedStatementConstructor = proxyClass.getConstructor(JdbcPooledConnection.class, PreparedStatement.class, CacheKey.class);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * Create a proxy class: class ResultSetJavassistProxy extends ResultSetJavaProxy implements java.sql.ResultSet
	 */
	private void createProxyResultSetClass()
	{
		try
		{
			Class proxyClass = generateProxyClass(ResultSet.class, ResultSetJavaProxy.class);
			proxyResultSetConstructor = proxyClass.getConstructor(Statement.class, ResultSet.class);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * Method generateProxyClass ...
	 *
	 * @param primaryInterface
	 * 		of type Class
	 * @param superClass
	 * 		of type Class
	 *
	 * @return Class
	 *
	 * @throws NotFoundException
	 * 		when
	 * @throws CannotCompileException
	 * 		when
	 * @throws NoSuchMethodException
	 * 		when
	 * @throws SecurityException
	 * 		when
	 */
	@SuppressWarnings("unchecked")
	private  Class generateProxyClass(Class primaryInterface, Class superClass)
			throws NotFoundException, CannotCompileException
	{

		// Make a new class that extends one of the JavaProxy classes (ie. superClass); use the name to XxxJavassistProxy instead of XxxJavaProxy
		String superClassName = superClass.getName();
		CtClass superClassCt = classPool.getCtClass(superClassName);
		CtClass targetCt = classPool.makeClass(superClassName.replace("JavaProxy", "JavassistProxy"), superClassCt);

		// Generate constructors that simply call super(..)
		for (CtConstructor constructor : superClassCt.getConstructors())
		{
			CtConstructor ctConstructor = CtNewConstructor.make(constructor.getParameterTypes(), constructor.getExceptionTypes(), targetCt);
			targetCt.addConstructor(ctConstructor);
		}

		// Make a set of method signatures we inherit implementation for, so we don't generate delegates for these
		Set superSigs = new HashSet<>();
		for (CtMethod method : superClassCt.getMethods())
		{
			superSigs.add(method.getName() + method.getSignature());
		}

		Set methods = new HashSet<>();
		Set> interfaces = ClassLoaderUtils.getAllInterfaces(primaryInterface);
		for (Class intf : interfaces)
		{
			CtClass intfCt = classPool.getCtClass(intf.getName());
			targetCt.addInterface(intfCt);
			for (CtMethod intfMethod : intfCt.getDeclaredMethods())
			{
				if (superSigs.contains(intfMethod.getName() + intfMethod.getSignature()))
				{
					// don't generate delegates for methods we override
					continue;
				}

				CtMethod method = CtNewMethod.copy(intfMethod, targetCt, classMap);
				// Ignore already added methods that come from other interfaces
				if (methods.contains(intfMethod.getName() + intfMethod.getSignature()))
				{
					continue;
				}

				methods.add(intfMethod.getName() + intfMethod.getSignature());

				// Generate a method that simply invokes the same method on the delegate
				StringBuilder call = new StringBuilder("{");
				if (method.getReturnType() != CtClass.voidType)
				{
					call.append("return ");
				}
				call.append("((")
				    .append(primaryInterface.getName())
				    .append(')'); // cast to primary interface
				call.append("delegate).");
				call.append(method.getName())
				    .append("($$);");
				call.append('}');
				method.setBody(call.toString());
				targetCt.addMethod(method);
			}
		}

		return (Class) targetCt.toClass(ClassLoaderUtils.getClassLoader(), null);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Connection getProxyConnection(JdbcPooledConnection jdbcPooledConnection, Connection connection)
	{
		try
		{
			return proxyConnectionConstructor.newInstance(jdbcPooledConnection, connection);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	// ---------------------------------------------------------------
	//  Generate Javassist Proxy Classes
	// ---------------------------------------------------------------

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Statement getProxyStatement(JdbcPooledConnection jdbcPooledConnection, Statement statement)
	{
		try
		{
			return proxyStatementConstructor.newInstance(jdbcPooledConnection, statement);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public CallableStatement getProxyCallableStatement(JdbcPooledConnection jdbcPooledConnection, CallableStatement statement)
	{
		try
		{
			return proxyCallableStatementConstructor.newInstance(jdbcPooledConnection, statement);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PreparedStatement getProxyPreparedStatement(JdbcPooledConnection jdbcPooledConnection, PreparedStatement statement, CacheKey cacheKey)
	{
		try
		{
			return proxyPreparedStatementConstructor.newInstance(jdbcPooledConnection, statement, cacheKey);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ResultSet getProxyResultSet(Statement statement, ResultSet resultSet)
	{
		try
		{
			return proxyResultSetConstructor.newInstance(statement, resultSet);
		}
		catch (Exception e)
		{
			throw new BitronixRuntimeException(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);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy