Spring DeadLocks retries
authorGustavo Martin Morcuende <gu.martinm@gmail.com>
Tue, 14 Oct 2014 03:28:20 +0000 (05:28 +0200)
committerGustavo Martin Morcuende <gu.martinm@gmail.com>
Tue, 14 Oct 2014 03:28:20 +0000 (05:28 +0200)
SpringJava/DeadLocksSQL/src/main/java/de/example/sql/deadlocks/aspect/DeadlockRetryAspect.java
SpringJava/DeadLocksSQL/src/main/resources/spring-config.xml

index c90d9a1..211a061 100644 (file)
@@ -1,17 +1,16 @@
 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;
 
 /**
@@ -23,6 +22,8 @@ public class DeadlockRetryAspect implements Ordered {
        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 {
 
@@ -34,29 +35,29 @@ public class DeadlockRetryAspect implements Ordered {
         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();
@@ -65,7 +66,7 @@ public class DeadlockRetryAspect implements Ordered {
                }
        } 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
@@ -73,9 +74,13 @@ public class DeadlockRetryAspect implements Ordered {
                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);
index a3d6e2a..637353b 100644 (file)
@@ -4,6 +4,7 @@
        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 
@@ -11,7 +12,9 @@
                            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>