1
16
17 package org.springframework.retry.annotation;
18
19 import java.lang.annotation.Annotation;
20 import java.lang.reflect.Method;
21 import java.util.LinkedHashSet;
22 import java.util.List;
23 import java.util.Set;
24 import java.util.concurrent.atomic.AtomicBoolean;
25
26 import javax.annotation.PostConstruct;
27
28 import org.aopalliance.aop.Advice;
29 import org.springframework.aop.ClassFilter;
30 import org.springframework.aop.IntroductionAdvisor;
31 import org.springframework.aop.MethodMatcher;
32 import org.springframework.aop.Pointcut;
33 import org.springframework.aop.support.AbstractPointcutAdvisor;
34 import org.springframework.aop.support.ComposablePointcut;
35 import org.springframework.aop.support.StaticMethodMatcherPointcut;
36 import org.springframework.aop.support.annotation.AnnotationClassFilter;
37 import org.springframework.aop.support.annotation.AnnotationMethodMatcher;
38 import org.springframework.beans.factory.BeanFactory;
39 import org.springframework.beans.factory.BeanFactoryAware;
40 import org.springframework.beans.factory.annotation.Autowired;
41 import org.springframework.context.annotation.Configuration;
42 import org.springframework.core.annotation.AnnotationUtils;
43 import org.springframework.retry.RetryListener;
44 import org.springframework.retry.backoff.Sleeper;
45 import org.springframework.retry.interceptor.MethodArgumentsKeyGenerator;
46 import org.springframework.retry.interceptor.NewMethodArgumentsIdentifier;
47 import org.springframework.retry.policy.RetryContextCache;
48 import org.springframework.util.ObjectUtils;
49 import org.springframework.util.ReflectionUtils;
50 import org.springframework.util.ReflectionUtils.MethodCallback;
51
52
63 @SuppressWarnings("serial")
64 @Configuration
65 public class RetryConfiguration extends AbstractPointcutAdvisor implements IntroductionAdvisor, BeanFactoryAware {
66
67 private Advice advice;
68
69 private Pointcut pointcut;
70
71 @Autowired(required = false)
72 private RetryContextCache retryContextCache;
73
74 @Autowired(required = false)
75 private List<RetryListener> retryListeners;
76
77 @Autowired(required = false)
78 private MethodArgumentsKeyGenerator methodArgumentsKeyGenerator;
79
80 @Autowired(required = false)
81 private NewMethodArgumentsIdentifier newMethodArgumentsIdentifier;
82
83 @Autowired(required = false)
84 private Sleeper sleeper;
85
86 private BeanFactory beanFactory;
87
88 @PostConstruct
89 public void init() {
90 Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(1);
91 retryableAnnotationTypes.add(Retryable.class);
92 this.pointcut = buildPointcut(retryableAnnotationTypes);
93 this.advice = buildAdvice();
94 if (this.advice instanceof BeanFactoryAware) {
95 ((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
96 }
97 }
98
99
102 @Override
103 public void setBeanFactory(BeanFactory beanFactory) {
104 this.beanFactory = beanFactory;
105 }
106
107 @Override
108 public ClassFilter getClassFilter() {
109 return pointcut.getClassFilter();
110 }
111
112 @Override
113 public Class<?>[] getInterfaces() {
114 return new Class[] { org.springframework.retry.interceptor.Retryable.class };
115 }
116
117 @Override
118 public void validateInterfaces() throws IllegalArgumentException {
119 }
120
121 @Override
122 public Advice getAdvice() {
123 return this.advice;
124 }
125
126 @Override
127 public Pointcut getPointcut() {
128 return this.pointcut;
129 }
130
131 protected Advice buildAdvice() {
132 AnnotationAwareRetryOperationsInterceptor interceptor = new AnnotationAwareRetryOperationsInterceptor();
133 if (retryContextCache != null) {
134 interceptor.setRetryContextCache(retryContextCache);
135 }
136 if (retryListeners != null) {
137 interceptor.setListeners(retryListeners);
138 }
139 if (methodArgumentsKeyGenerator != null) {
140 interceptor.setKeyGenerator(methodArgumentsKeyGenerator);
141 }
142 if (newMethodArgumentsIdentifier != null) {
143 interceptor.setNewItemIdentifier(newMethodArgumentsIdentifier);
144 }
145 if (sleeper != null) {
146 interceptor.setSleeper(sleeper);
147 }
148 return interceptor;
149 }
150
151
157 protected Pointcut buildPointcut(Set<Class<? extends Annotation>> retryAnnotationTypes) {
158 ComposablePointcut result = null;
159 for (Class<? extends Annotation> retryAnnotationType : retryAnnotationTypes) {
160 Pointcut filter = new AnnotationClassOrMethodPointcut(retryAnnotationType);
161 if (result == null) {
162 result = new ComposablePointcut(filter);
163 }
164 else {
165 result.union(filter);
166 }
167 }
168 return result;
169 }
170
171 private final class AnnotationClassOrMethodPointcut extends StaticMethodMatcherPointcut {
172
173 private final MethodMatcher methodResolver;
174
175 AnnotationClassOrMethodPointcut(Class<? extends Annotation> annotationType) {
176 this.methodResolver = new AnnotationMethodMatcher(annotationType);
177 setClassFilter(new AnnotationClassOrMethodFilter(annotationType));
178 }
179
180 @Override
181 public boolean matches(Method method, Class<?> targetClass) {
182 return getClassFilter().matches(targetClass) || this.methodResolver.matches(method, targetClass);
183 }
184
185 @Override
186 public boolean equals(Object other) {
187 if (this == other) {
188 return true;
189 }
190 if (!(other instanceof AnnotationClassOrMethodPointcut)) {
191 return false;
192 }
193 AnnotationClassOrMethodPointcut otherAdvisor = (AnnotationClassOrMethodPointcut) other;
194 return ObjectUtils.nullSafeEquals(this.methodResolver, otherAdvisor.methodResolver);
195 }
196
197 }
198
199 private final class AnnotationClassOrMethodFilter extends AnnotationClassFilter {
200
201 private final AnnotationMethodsResolver methodResolver;
202
203 AnnotationClassOrMethodFilter(Class<? extends Annotation> annotationType) {
204 super(annotationType, true);
205 this.methodResolver = new AnnotationMethodsResolver(annotationType);
206 }
207
208 @Override
209 public boolean matches(Class<?> clazz) {
210 return super.matches(clazz) || this.methodResolver.hasAnnotatedMethods(clazz);
211 }
212
213 }
214
215 private static class AnnotationMethodsResolver {
216
217 private Class<? extends Annotation> annotationType;
218
219 public AnnotationMethodsResolver(Class<? extends Annotation> annotationType) {
220 this.annotationType = annotationType;
221 }
222
223 public boolean hasAnnotatedMethods(Class<?> clazz) {
224 final AtomicBoolean found = new AtomicBoolean(false);
225 ReflectionUtils.doWithMethods(clazz,
226 new MethodCallback() {
227 @Override
228 public void doWith(Method method) throws IllegalArgumentException,
229 IllegalAccessException {
230 if (found.get()) {
231 return;
232 }
233 Annotation annotation = AnnotationUtils.findAnnotation(method,
234 annotationType);
235 if (annotation != null) { found.set(true); }
236 }
237 });
238 return found.get();
239 }
240
241 }
242
243 }
244