com.vmware.bdd.aop.tx.RetryTransactionAdvice.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.bdd.aop.tx.RetryTransactionAdvice.java

Source

/***************************************************************************
 * Copyright (c) 2012-2013 VMware, Inc. All Rights Reserved.
 * 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.vmware.bdd.aop.tx;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.exception.LockAcquisitionException;
import org.springframework.core.annotation.AnnotationUtils;

import com.vmware.bdd.aop.annotation.RetryTransaction;
import com.vmware.bdd.exception.BddException;
import com.vmware.bdd.exception.TxRetryException;
import com.vmware.bdd.exception.UniqueConstraintViolationException;

/**
 * @author Jarred Li
 * @since 0.8
 * @version 0.8
 *
 */
public class RetryTransactionAdvice {

    private static final Logger logger = Logger.getLogger(RetryTransactionAdvice.class);

    public void retry(ProceedingJoinPoint pjp) throws Throwable {
        logger.info("retry transaction");
        int retriesLeft = 5;
        String methodName = pjp.getSignature().getName();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        if (method.getDeclaringClass().isInterface()) {
            method = pjp.getTarget().getClass().getDeclaredMethod(methodName, method.getParameterTypes());
        }
        Annotation[] annotations = method.getDeclaredAnnotations();
        for (Annotation a : annotations) {
            if (a instanceof RetryTransaction) {
                RetryTransaction retryAnno = (RetryTransaction) a;
                retriesLeft = retryAnno.value();
            }
        }

        RetryTransaction retryTrx = AnnotationUtils.findAnnotation(method, RetryTransaction.class);
        retriesLeft = retryTrx.value();
        Throwable rootCause = null;
        boolean success = false;
        while (!success) {
            try {
                pjp.proceed();
                success = true;
            } catch (Throwable ex) {
                rootCause = (ex instanceof BddException) ? ex.getCause() : ex;
                if (isRetryable(rootCause)) {
                    if (retriesLeft > 0) {
                        retriesLeft--;
                    } else {
                        throw TxRetryException.wrap(rootCause, false);
                    }
                } else if (isUniqViolation(rootCause)) {
                    throw UniqueConstraintViolationException.wrap((ConstraintViolationException) rootCause);
                } else {
                    throw BddException.wrapIfNeeded(ex, "Exception in a DAL transaction.");
                }
            }
        }
        if (!success) {
            if (rootCause != null) {
                logger.warn("retry transaction failed.", rootCause);
                throw rootCause;
            } else {
                logger.warn("retry transction failed.");
                throw new Exception("retry transaction failed");
            }
        } else {
            if (rootCause != null) {
                logger.warn("retry transaction completed. Failure root cause:" + rootCause.getMessage());
            } else {
                logger.info("normal operation");
            }
        }
    }

    /**
     * @param ex
     *           -- the "real" exception thrown from the transaction body. It can
     *           be null if we throw BddException with no cause.
     * @return true if the given exception corresponds to Hibernate's uniqueness
     *         violation.
     **/
    private boolean isUniqViolation(Throwable ex) {
        final String uniquenessViolation = new String("23505"); // SQL State error
        return ex instanceof ConstraintViolationException
                && ((ConstraintViolationException) ex).getSQLState().equals(uniquenessViolation);
    }

    /**
     * @param ex
     *           -- the "real" exception thrown from the transaction body. It can
     *           be null if we throw BddException with no cause.
     * @return true if the given exception should cause a retry, e.g.
     *         serialization failure or deadlock etc.
     **/
    private boolean isRetryable(Throwable ex) {
        final String psqlDeadlockDetected = new String("40P01"); // SQL State error specific to PSQL
        return ((ex instanceof LockAcquisitionException) || (ex instanceof GenericJDBCException
                && ((GenericJDBCException) ex).getSQLState().equals(psqlDeadlockDetected)));
    }

}