diff --git a/infrastructure/src/main/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptor.java b/infrastructure/src/main/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptor.java index 03b460afe..3bd510f18 100644 --- a/infrastructure/src/main/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptor.java +++ b/infrastructure/src/main/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptor.java @@ -18,6 +18,7 @@ package org.springframework.batch.repeat.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.framework.ReflectiveMethodInvocation; import org.springframework.batch.repeat.RepeatCallback; import org.springframework.batch.repeat.RepeatContext; import org.springframework.batch.repeat.RepeatOperations; @@ -50,14 +51,21 @@ public class RepeatOperationsInterceptor implements MethodInterceptor { * * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) */ - public Object invoke(final MethodInvocation methodInvocation) throws Throwable { + public Object invoke(final MethodInvocation invocation) throws Throwable { batchTempate.iterate(new RepeatCallback() { public ExitStatus doInIteration(RepeatContext context) throws Exception { try { + + MethodInvocation clone = invocation; + if (invocation instanceof ReflectiveMethodInvocation) { + clone = ((ReflectiveMethodInvocation) invocation) + .invocableClone(); + } + // N.B. discards return value if there is one - return new ExitStatus(methodInvocation.proceed() != null); + return new ExitStatus(clone.proceed() != null); } catch (Throwable e) { if (e instanceof Exception) { diff --git a/infrastructure/src/main/java/org/springframework/batch/retry/aop/RetryOperationsInterceptor.java b/infrastructure/src/main/java/org/springframework/batch/retry/aop/RetryOperationsInterceptor.java index cada48ec0..5adc8479b 100644 --- a/infrastructure/src/main/java/org/springframework/batch/retry/aop/RetryOperationsInterceptor.java +++ b/infrastructure/src/main/java/org/springframework/batch/retry/aop/RetryOperationsInterceptor.java @@ -18,6 +18,7 @@ package org.springframework.batch.retry.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.framework.ReflectiveMethodInvocation; import org.springframework.batch.retry.RetryCallback; import org.springframework.batch.retry.RetryContext; import org.springframework.batch.retry.RetryOperations; @@ -38,12 +39,26 @@ public class RetryOperationsInterceptor implements MethodInterceptor { this.retryTemplate = retryTemplate; } - public Object invoke(final MethodInvocation methodInvocation) throws Throwable { + public Object invoke(final MethodInvocation invocation) throws Throwable { // TODO: use the method name to initialise a statistics context return this.retryTemplate.execute(new RetryCallback() { public Object doWithRetry(RetryContext context) throws Throwable { - return methodInvocation.proceed(); + + /* + * If we don't copy the invocation carefully it won't keep a + * reference to the other interceptors in the chain. We don't + * have a choice here but to specialise to + * ReflectiveMethodInvocation (but how often would another + * implementation come along?). + */ + MethodInvocation clone = invocation; + if (invocation instanceof ReflectiveMethodInvocation) { + clone = ((ReflectiveMethodInvocation) invocation) + .invocableClone(); + } + + return clone.proceed(); } }); diff --git a/infrastructure/src/test/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptorTests.java b/infrastructure/src/test/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptorTests.java index 7ced248d6..baf04f5d7 100644 --- a/infrastructure/src/test/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptorTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/repeat/aop/RepeatOperationsInterceptorTests.java @@ -21,12 +21,16 @@ import java.util.List; import junit.framework.TestCase; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.ProxyFactory; import org.springframework.batch.repeat.RepeatCallback; import org.springframework.batch.repeat.RepeatOperations; import org.springframework.batch.repeat.ExitStatus; import org.springframework.batch.repeat.exception.RepeatException; +import org.springframework.batch.retry.policy.SimpleRetryPolicy; +import org.springframework.batch.retry.support.RetryTemplate; public class RepeatOperationsInterceptorTests extends TestCase { @@ -88,6 +92,22 @@ public class RepeatOperationsInterceptorTests extends TestCase { } } + public void testInterceptorChainWithRetry() throws Exception { + ((Advised) service).addAdvice(interceptor); + final List list = new ArrayList(); + ((Advised) service).addAdvice(new MethodInterceptor() { + public Object invoke(MethodInvocation invocation) throws Throwable { + list.add("chain"); + return invocation.proceed(); + } + }); + RetryTemplate template = new RetryTemplate(); + template.setRetryPolicy(new SimpleRetryPolicy(2)); + service.service(); + assertEquals(3, target.count); + assertEquals(3, list.size()); + } + private interface Service { Object service() throws Exception; diff --git a/infrastructure/src/test/java/org/springframework/batch/retry/aop/RetryOperationsInterceptorTests.java b/infrastructure/src/test/java/org/springframework/batch/retry/aop/RetryOperationsInterceptorTests.java index 9cad3a7a6..7cc4dcd82 100644 --- a/infrastructure/src/test/java/org/springframework/batch/retry/aop/RetryOperationsInterceptorTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/retry/aop/RetryOperationsInterceptorTests.java @@ -16,12 +16,18 @@ package org.springframework.batch.retry.aop; +import java.util.ArrayList; +import java.util.List; + import junit.framework.TestCase; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.target.SingletonTargetSource; import org.springframework.batch.retry.policy.NeverRetryPolicy; +import org.springframework.batch.retry.policy.SimpleRetryPolicy; import org.springframework.batch.retry.support.RetryTemplate; public class RetryOperationsInterceptorTests extends TestCase { @@ -45,6 +51,23 @@ public class RetryOperationsInterceptorTests extends TestCase { assertEquals(2, target.count); } + public void testInterceptorChainWithRetry() throws Exception { + ((Advised) service).addAdvice(interceptor); + final List list = new ArrayList(); + ((Advised) service).addAdvice(new MethodInterceptor() { + public Object invoke(MethodInvocation invocation) throws Throwable { + list.add("chain"); + return invocation.proceed(); + } + }); + RetryTemplate template = new RetryTemplate(); + template.setRetryPolicy(new SimpleRetryPolicy(2)); + interceptor.setRetryTemplate(template); + service.service(); + assertEquals(2, target.count); + assertEquals(2, list.size()); + } + public void testRetryExceptionAfterTooManyAttempts() throws Exception { ((Advised) service).addAdvice(interceptor); RetryTemplate template = new RetryTemplate();