Java tutorial
/* * 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()); } }