package de.example.sql.deadlocks.aspect;
import java.lang.reflect.Method;
+import java.util.Collection;
import org.aspectj.lang.ProceedingJoinPoint;
-import org.aspectj.lang.annotation.Aspect;
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.core.Ordered;
-import com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException;
-
import de.example.sql.deadlocks.annotation.DeadlockRetry;
/**
private static final Logger logger = LoggerFactory.getLogger(DeadlockRetryAspect.class);
private static final int ORDER = 99;
+ private Collection<Class<? extends Throwable>> retryableExceptionClasses;
+
@Around(value = "@annotation(deadlockRetry)", argNames = "deadlockRetry")
public Object doAround(final ProceedingJoinPoint pjp, final DeadlockRetry deadlockRetry) throws Throwable {
final Method method = signature.getMethod();
int count = 0;
- Throwable deadLockException = null;
+ Throwable lastException = null;
do {
try {
count++;
logger.info("Attempting to invoke method " + method.getName() + " on " + target.getClass() + " count " + count);
//Calling real method
- Object result = pjp.proceed();
+ final Object result = pjp.proceed();
logger.info("Completed invocation of method " + method.getName() + " on " + target.getClass());
return result;
- } catch (final Throwable e1) {
+ } catch (final Throwable ex) {
- if (!isDeadLock(e1)) {
- throw e1;
+ if (!isDeadLock(ex)) {
+ throw ex;
}
- deadLockException = e1;
+ lastException = ex;
if (interval > 0) {
try {
Thread.sleep(interval);
- } catch (final InterruptedException e2) {
- logger.warn("Deadlock retry thread interrupt", e2);
+ } catch (final InterruptedException exi) {
+ logger.warn("Deadlock retry thread interrupt", exi);
// Restore interrupt status.
Thread.currentThread().interrupt();
}
} while (count < maxTries);
- throw new RuntimeException("DeadlockRetry failed, deadlock in all retry attempts.", deadLockException);
+ throw new RuntimeException("DeadlockRetry failed, deadlock in all retry attempts.", lastException);
}
@Override
return ORDER;
}
+ public final void setRetryableExceptionClasses(final Collection<Class<? extends Throwable>> retryableExceptionClasses) {
+ this.retryableExceptionClasses = retryableExceptionClasses;
+ }
+
private boolean isDeadLock(Throwable ex) {
do {
- if (ex instanceof MySQLTransactionRollbackException) {
+ if (this.retryableExceptionClasses.contains(ex.getClass())) {
return true;
}
} while ((ex = ex.getCause()) != null);
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
+ xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd">
+ http://www.springframework.org/schema/tx/spring-tx.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd">
<!--
************* WE HAVE TO ENABLE THE ASPECTJ SUPPORT!!!!!! *************
Spring detects if the bean has an interface implementation. If it has one,
Spring will use J2SE (J2SE needs classes with interfaces) in other case it
will use CGLIB. We could force the CGLIB use in classes with interfaces.
+
+ I AM GOING TO FORCE THE USE OF CGLIB BECAUSE OF THIS:
+ See: http://docs.spring.io/spring/docs/3.1.0.M2/spring-framework-reference/html/aop.html#aop-proxying
+ "If you want to force the use of CGLIB proxying (for example, to proxy every method defined
+ for the target object, not just those implemented by its interfaces)"
+
+ ATTENTION!!!! :O
+ See: http://docs.spring.io/spring/docs/3.1.0.M2/spring-framework-reference/html/aop.html#aop-proxying
+ Multiple <aop:config/> sections are collapsed into a single unified auto-proxy creator at runtime, which
+ applies the strongest proxy settings that any of the <aop:config/> sections (typically from different XML
+ bean definition files) specified. This also applies to the <tx:annotation-driven/> and <aop:aspectj-autoproxy/> elements.
+
+ To be clear: using 'proxy-target-class="true"' on <tx:annotation-driven/>, <aop:aspectj-autoproxy/> or <aop:config/>
+ elements will force the use of CGLIB proxies for all three of them.
-->
- <aop:aspectj-autoproxy/>
+ <aop:aspectj-autoproxy proxy-target-class="true"/>
need here that feature.
I do not really need the component-scan thing but I hope, it will improve performance (not sure)
- -->
+
+ I DO NOT THINK component-scan IS INTENDED TO BE USED IN THIS WAY!!!!!
<context:component-scan base-package="de.example.sql.deadlocks" use-default-filters="false">
<context:include-filter
type="annotation"
expression="de.example.sql.deadlocks.annotation.DeadlockRetry" />
</context:component-scan>
+ -->
<bean id="deadlockRetryAspect" class="de.example.sql.deadlocks.aspect.DeadlockRetryAspect">
+ <property name="retryableExceptionClasses">
+ <list value-type="java.lang.String">
+ <value>com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException</value>
+ </list>
+ </property>
</bean>