package com.hotent.base.transaction;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl;
import com.hotent.base.util.AppUtil;
import com.hotent.base.util.TransactionThreadLocal;

/**
 * 
 * @company 广州宏天软件股份有限公司
 * @author jason
 * @email liygui@jee-soft.cn
 * @date 2021年2月24日
 */
@Aspect
@Component
public class TransactionAspect{
	
	@Around("execution(* javax.sql.DataSource.getConnection(..))")
	public Object aroundGetConnection(ProceedingJoinPoint joinPoint) throws Throwable{
		Object proceed = joinPoint.proceed();
		Connection connection = (Connection) proceed;
		return Proxy.newProxyInstance(connection.getClass().getClassLoader(),
				ConnectionProxyImpl.class.getInterfaces(), new ConnectionProxy(connection));
	}

}

/**
 * 代理Connection 实现类 处理commit和rollback
 * 
 * @author jason
 *
 */
class ConnectionProxy implements InvocationHandler {
	
	private static final String COMMIT = "commit";
	private static final String ROLLBACK = "rollback";
	private static final String CLOSE = "close";
	
	Connection connection;
	
	public ConnectionProxy(Connection connection) {
		this.connection = connection;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		String methodName = method.getName();
		Object invoke = method.invoke(connection, args);
		if (COMMIT.equals(methodName)) {
			TransactionThreadLocal transactionThreadLocal = getTransactionThreadLocal();
			if(transactionThreadLocal!=null) {				
				transactionThreadLocal.commit();
			}
		}
		if(ROLLBACK.equals(methodName)) {
			TransactionThreadLocal transactionThreadLocal = getTransactionThreadLocal();
			if(transactionThreadLocal!=null) {				
				transactionThreadLocal.rollback();
			}
		}
		if(CLOSE.equals(methodName)) {
			TransactionThreadLocal transactionThreadLocal = getTransactionThreadLocal();
			if(transactionThreadLocal!=null) {				
				transactionThreadLocal.close();
			}
		}
		return invoke;
	}

	private TransactionThreadLocal getTransactionThreadLocal() {
		ApplicationContext applicaitonContext = AppUtil.getApplicaitonContext();
		if (applicaitonContext != null && applicaitonContext.containsBean("transactionThreadLocal")) {
			return applicaitonContext.getBean(TransactionThreadLocal.class);
		}
		return null;
	}

}
