1 | /* |
2 | * Copyright 2006-2007 the original author or authors. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | package org.springframework.batch.retry.interceptor; |
18 | |
19 | import org.aopalliance.intercept.MethodInterceptor; |
20 | import org.aopalliance.intercept.MethodInvocation; |
21 | import org.springframework.aop.ProxyMethodInvocation; |
22 | import org.springframework.batch.retry.RetryCallback; |
23 | import org.springframework.batch.retry.RetryContext; |
24 | import org.springframework.batch.retry.RetryOperations; |
25 | import org.springframework.batch.retry.support.RetryTemplate; |
26 | import org.springframework.util.Assert; |
27 | |
28 | /** |
29 | * A {@link MethodInterceptor} that can be used to automatically retry calls to |
30 | * a method on a service if it fails. The injected {@link RetryOperations} is |
31 | * used to control the number of retries. By default it will retry a fixed |
32 | * number of times, according to the defaults in {@link RetryTemplate}.<br/> |
33 | * |
34 | * Hint about transaction boundaries. If you want to retry a failed transaction |
35 | * you need to make sure that the transaction boundary is inside the retry, |
36 | * otherwise the successful attempt will roll back with the whole transaction. |
37 | * If the method being intercepted is also transactional, then use the ordering |
38 | * hints in the advice declarations to ensure that this one is before the |
39 | * transaction interceptor in the advice chain. |
40 | * |
41 | * @author Rob Harrop |
42 | * @author Dave Syer |
43 | */ |
44 | public class RetryOperationsInterceptor implements MethodInterceptor { |
45 | |
46 | private RetryOperations retryOperations = new RetryTemplate(); |
47 | |
48 | public void setRetryTemplate(RetryOperations retryTemplate) { |
49 | Assert.notNull(retryTemplate, "'retryOperations' cannot be null."); |
50 | this.retryOperations = retryTemplate; |
51 | } |
52 | |
53 | public Object invoke(final MethodInvocation invocation) throws Throwable { |
54 | |
55 | return this.retryOperations.execute(new RetryCallback() { |
56 | |
57 | public Object doWithRetry(RetryContext context) throws Throwable { |
58 | |
59 | /* |
60 | * If we don't copy the invocation carefully it won't keep a |
61 | * reference to the other interceptors in the chain. We don't |
62 | * have a choice here but to specialise to |
63 | * ReflectiveMethodInvocation (but how often would another |
64 | * implementation come along?). |
65 | */ |
66 | if (invocation instanceof ProxyMethodInvocation) { |
67 | return ((ProxyMethodInvocation) invocation) |
68 | .invocableClone().proceed(); |
69 | } else { |
70 | throw new IllegalStateException( |
71 | "MethodInvocation of the wrong type detected - this should not happen with Spring AOP, so please raise an issue if you see this exception"); |
72 | } |
73 | } |
74 | |
75 | }); |
76 | } |
77 | } |