podd.dataaccess.hibernate.DeadlockInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for podd.dataaccess.hibernate.DeadlockInterceptor.java

Source

/*
 * Copyright (c) 2009 - 2010. School of Information Technology and Electrical
 * Engineering, The University of Queensland.  This software is being developed
 * for the "Phenomics Ontoogy Driven Data Management Project (PODD)" project.
 * PODD is a National e-Research Architecture Taskforce (NeAT) project
 * co-funded by ANDS and ARCS.
 *
 * PODD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PODD 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PODD.  If not, see <http://www.gnu.org/licenses/>.
 */

package podd.dataaccess.hibernate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.hibernate.HibernateException;

/**
 * An AOP-style class that's weaved by spring to retry certain methods on throwing of certain exceptions.
 * Methods retried are all in the {@link HibernateDAO} interface and annotated by the keyword
 * {@link podd.dataaccess.Idempotent}. Number of retries, exception to be matched and retry can
 * all be configured via setter methods.
 *
 * This method is primarily used to fix the issue of Hibernate classes occasionally throwing
 * {@link org.hibernate.exception.LockAcquisitionException}s in some method calls. 
 *
 * @author Yuan-Fang Li
 * @version $Id$
 */
public class DeadlockInterceptor {
    private Logger logger = LoggerFactory.getLogger(getClass());

    private int retry;
    private long delay;
    private Class<Exception> exceptionClass;

    public void setRetry(int retry) {
        this.retry = retry;
    }

    public void setDelay(long delay) {
        this.delay = delay;
    }

    public void setExceptionClass(Class<Exception> exceptionClass) {
        this.exceptionClass = exceptionClass;
    }

    public int getRetry() {
        return retry;
    }

    public long getDelay() {
        return delay;
    }

    public Class<Exception> getExceptionClass() {
        return exceptionClass;
    }

    /**
     * Invoked in an "around" advice, this method intercepts and inspects the result of the ecapsulated method call.
     * If the method call throws an exception that matches the configured exception of the interceptor, the method
     * call is retried for {@link #retry} numbers, each time with an linearly increasing delay, with the initial
     * being {@link #delay}.
     *
     * @param pjp The method call being intercepted and inspected.
     * @return The return value of the original method call.
     * @throws Throwable The exception thrown by the original method call, if any.
     */
    public Object retryDAOOperation(ProceedingJoinPoint pjp) throws Throwable {
        int numRetries = 0;
        Exception ex;
        do {
            numRetries++;
            try {
                return pjp.proceed();
            } catch (HibernateException throwable) {
                if (exceptionClass.isInstance(throwable)) {
                    logRetry(pjp, numRetries, throwable);
                    ex = throwable;
                    if (delay > 0) {
                        try {
                            Thread.sleep(delay * numRetries);
                        } catch (InterruptedException ignore) {
                        }
                    }
                } else {
                    throw throwable;
                }
            }
        } while (numRetries < retry);
        throw ex;
    }

    private void logRetry(ProceedingJoinPoint pjp, int numRetries, HibernateException throwable) {
        StringBuffer buffer = new StringBuffer();
        buffer.append("Operation ").append(pjp.toShortString()).append(" failed on ").append(throwable.getClass())
                .append(" with parameters: [");
        final Object[] arguments = pjp.getArgs();
        for (Object obj : arguments) {
            if (null != obj) {
                buffer.append(obj.toString()).append(", ");
            }
        }
        final int start = buffer.lastIndexOf(", ");
        if (start >= 0) {
            buffer.delete(start, start + 2);
        }
        buffer.append("]. Retrying ").append(numRetries).append(" out of ").append(retry).append(" times.");
        logger.warn(buffer.toString());
    }
}