com.feilong.spring.jdbc.datasource.MultipleGroupReadWriteDataSourceAspect.java Source code

Java tutorial

Introduction

Here is the source code for com.feilong.spring.jdbc.datasource.MultipleGroupReadWriteDataSourceAspect.java

Source

/*
 * Copyright (C) 2008 feilong (venusdrogon@163.com)
 *
 * 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 com.feilong.spring.jdbc.datasource;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;

import net.sf.json.JSONException;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttributeSource;

import com.feilong.commons.core.date.DateExtensionUtil;
import com.feilong.commons.core.lang.ThreadUtil;
import com.feilong.commons.core.tools.json.JsonUtil;
import com.feilong.commons.core.util.Validator;
import com.feilong.spring.aop.AbstractAspect;
import com.feilong.spring.aop.ProceedingJoinPointUtil;
import com.feilong.spring.transaction.interceptor.TransactionAttributeUtil;

/**
 * ,/?,??()?.
 * 
 * <h3>??:</h3>
 * 
 * <blockquote>
 * <ol>
 * <li>
 * controller AManager.a(); A adatabase</li>
 * <li></li>
 * <li></li>
 * <li>
 * 
 * <pre>
 * ?   AManager.a(){  BManager.b();}  ,
 * A adatabase
 * B bdatabase
 * controller A ?, MultipleGroupReadWriteStatusHolder.setMultipleDataSourceGroupName(adatabase);
 *  A.a(){  B.b();}
 * ?,?
 * </pre>
 * 
 * </li>
 * </ol>
 * </blockquote>
 * 
 * @author <a href="mailto:venusdrogon@163.com">feilong</a>
 * @version 1.1.1 201545 ?6:59:04
 * @see com.feilong.spring.aop.AbstractAspect
 * @see org.aspectj.lang.annotation.Aspect
 * @see org.aspectj.lang.annotation.Around
 * @see org.springframework.transaction.interceptor.TransactionAttributeSource
 * @see org.springframework.core.Ordered
 * @see "loxia.aspect.ReadWriteDataSourceAspect"
 * @see org.springframework.transaction.annotation.Transactional
 * @since 1.1.1
 */
@Aspect
public class MultipleGroupReadWriteDataSourceAspect extends AbstractAspect {

    /** The Constant logger. */
    private static final Logger log = LoggerFactory.getLogger(MultipleGroupReadWriteDataSourceAspect.class);

    /** The transaction attribute souce. */
    @Autowired(required = false)
    private TransactionAttributeSource transactionAttributeSouce;

    /**
     * Point.
     *
     * @param proceedingJoinPoint
     *            the proceeding join point
     * @return the object
     * @throws Throwable
     *             the throwable
     */
    @Around("this(loxia.dao.ReadWriteSupport)")
    public Object point(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Signature signature = proceedingJoinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        //
        TransactionAttribute transactionAttribute = null;
        if (null != transactionAttributeSouce) {
            transactionAttribute = transactionAttributeSouce.getTransactionAttribute(methodSignature.getMethod(),
                    proceedingJoinPoint.getTarget().getClass());
        }

        MultipleGroupDataSource multipleGroupDataSourceAnnotation = getAnnotation(proceedingJoinPoint,
                MultipleGroupDataSource.class);
        //??
        String groupName;
        //?multipleGroupDataSourceAnnotation
        //? ? 
        if (null == multipleGroupDataSourceAnnotation
                || Validator.isNullOrEmpty(multipleGroupDataSourceAnnotation.value())) {
            //nothing to do
            groupName = null;
        } else {
            groupName = multipleGroupDataSourceAnnotation.value();
        }
        return this.proceed(proceedingJoinPoint, transactionAttribute, groupName);
    }

    /**
     * Proceed.
     *
     * @param proceedingJoinPoint
     *            the proceeding join point
     * @param transactionAttribute
     *            the transaction attribute
     * @param groupName
     *            the group name
     * @return the object
     * @throws Throwable
     *             the throwable
     * @since 1.1.1
     */
    private Object proceed(ProceedingJoinPoint proceedingJoinPoint, TransactionAttribute transactionAttribute,
            String groupName) throws Throwable {
        //?holder
        String previousDataSourceNameHolder = MultipleGroupReadWriteStatusHolder.getMultipleDataSourceGroupName();

        String currentThreadInfo = JsonUtil.format(ThreadUtil.getCurrentThreadMapForLog());
        if (log.isInfoEnabled()) {

            Map<String, Object> mapForLog = new LinkedHashMap<String, Object>();
            mapForLog.put("groupName", groupName);
            mapForLog.put("previousDataSourceNameHolder", previousDataSourceNameHolder);
            mapForLog.put("transactionAttribute:", TransactionAttributeUtil.getMapForLog(transactionAttribute));

            log.info("before determine datasource :[{}],proceedingJoinPoint info:[{}],current thread info:[{}]",
                    JsonUtil.format(mapForLog),
                    getProceedingJoinPointJsonInfoExcludeJsonException(proceedingJoinPoint), currentThreadInfo);
        }

        boolean isSetHolder = isSetHolder(transactionAttribute, groupName);

        //***************************************************************************
        if (isSetHolder) {
            //read or write
            String readWriteSupport = this.getReadWriteSupport(transactionAttribute);

            String targetDataSourcesKey = MultipleGroupReadWriteUtil.getTargetDataSourcesKey(groupName,
                    readWriteSupport);
            log.info("set targetDataSourcesKey:[{}],current thread info:[{}]", targetDataSourcesKey,
                    currentThreadInfo);
            MultipleGroupReadWriteStatusHolder.setMultipleDataSourceGroupName(targetDataSourcesKey);
        }

        try {
            return this.proceed(proceedingJoinPoint);
        } catch (Throwable e) {
            throw e;
        } finally {
            if (Validator.isNotNullOrEmpty(previousDataSourceNameHolder)) {
                log.info("Back to previous Read/Write Status:[{}],current thread info:[{}]",
                        previousDataSourceNameHolder, currentThreadInfo);
                //?,? 
                MultipleGroupReadWriteStatusHolder.setMultipleDataSourceGroupName(previousDataSourceNameHolder);
            }

            //TODO ?? loxia?
            //?previousDataSourceNameHolder,
            else {
                log.info(
                        "previousDataSourceNameHolder is NullOrEmpty,Clear Read/Write Status:[{}],current thread info:[{}]",
                        MultipleGroupReadWriteStatusHolder.getMultipleDataSourceGroupName(), currentThreadInfo);
                MultipleGroupReadWriteStatusHolder.clearMultipleDataSourceGroupName();
            }
        }
    }

    /**
     * ???.
     *
     * @param transactionAttribute
     *            the transaction attribute
     * @param groupName
     *            the group name
     * @return true, if checks if is set holder
     * @since 1.1.1
     */
    private boolean isSetHolder(TransactionAttribute transactionAttribute, String groupName) {
        if (Validator.isNotNullOrEmpty(groupName)) {
            return true;
        } else {

            if (null == transactionAttribute) {
                return true;
            }

            int propagationBehavior = transactionAttribute.getPropagationBehavior();
            //TODO ?? loxia?
            return propagationBehavior != TransactionDefinition.PROPAGATION_REQUIRES_NEW;
        }
    }

    /**
     * ?,<br>
     *  {@link org.springframework.transaction.interceptor.TransactionAttribute}?.
     *
     * @param transactionAttribute
     *            the transaction attribute
     * @return the read write support
     * @see "org.postgresql.jdbc2.AbstractJdbc2Connection#setReadOnly(boolean)"
     * @since 1.1.1
     */
    private String getReadWriteSupport(TransactionAttribute transactionAttribute) {

        String readWriteSupport = "";

        if (null == transactionAttribute) {
            return loxia.dao.ReadWriteSupport.READ;
        }

        boolean mustWrite = false;

        int propagationBehavior = transactionAttribute.getPropagationBehavior();

        switch (propagationBehavior) {

        //?
        case TransactionDefinition.PROPAGATION_REQUIRED:
            break;

        //,????,???????
        case TransactionDefinition.PROPAGATION_REQUIRES_NEW:
            mustWrite = true;
            break;

        //? ???
        case TransactionDefinition.PROPAGATION_SUPPORTS:
            break;

        //????,?????
        case TransactionDefinition.PROPAGATION_NOT_SUPPORTED:
            break;

        //???IllegalTransactionStateException
        case TransactionDefinition.PROPAGATION_MANDATORY:
            break;

        //??  ?IllegalTransactionStateException
        case TransactionDefinition.PROPAGATION_NEVER:
            break;

        //?
        case TransactionDefinition.PROPAGATION_NESTED:
            break;

        default:
            throw new UnsupportedOperationException(
                    "propagationBehavior:[" + propagationBehavior + "] not support!");
        }

        if (mustWrite) {
            log.info("New writable connection is required for new transaction.");
            readWriteSupport = loxia.dao.ReadWriteSupport.WRITE;
        } else {
            //see "org.postgresql.jdbc2.AbstractJdbc2Connection#setReadOnly(boolean)"
            boolean readOnly = transactionAttribute.isReadOnly();
            readWriteSupport = readOnly ? loxia.dao.ReadWriteSupport.READ : loxia.dao.ReadWriteSupport.WRITE;
        }
        return readWriteSupport;
    }

    /**
     * Proceed.
     *
     * @param proceedingJoinPoint
     *            the proceeding join point
     * @return the object
     * @throws Throwable
     *             the throwable
     * @since 1.1.1
     */
    private Object proceed(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object[] args = proceedingJoinPoint.getArgs();
        String format = getProceedingJoinPointJsonInfoExcludeJsonException(proceedingJoinPoint);

        if (log.isInfoEnabled()) {
            log.info("begin proceed ,ProceedingJoinPoint info:[{}],Thread info:{}", format,
                    JsonUtil.format(ThreadUtil.getCurrentThreadMapForLog()));
        }
        Date beginDate = new Date();

        //***********************************************************

        Object returnValue = proceedingJoinPoint.proceed(args);

        //***********************************************************
        Date endDate = new Date();

        if (log.isInfoEnabled()) {
            log.info("end proceed:[{}],thread info:[{}],time:{},return:[{}]", format,
                    JsonUtil.format(ThreadUtil.getCurrentThreadMapForLog()),
                    DateExtensionUtil.getIntervalForView(beginDate, endDate), returnValue);
        }
        return returnValue;
    }

    /**
     * ??,request??json ?, ???, .
     *
     * @param proceedingJoinPoint
     *            the proceeding join point
     * @return the proceeding join point json info exclude json exception
     * @since 1.1.1
     */
    private String getProceedingJoinPointJsonInfoExcludeJsonException(ProceedingJoinPoint proceedingJoinPoint) {
        String format = "";
        try {
            format = JsonUtil.format(ProceedingJoinPointUtil.getMapForLog(proceedingJoinPoint));
        } catch (JSONException e) {
            format = e.getMessage();
            log.error("", e);
        }
        return format;
    }
}