1 package org.apache.onami.cache;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import static java.lang.String.format;
23 import static java.util.Arrays.asList;
24 import static java.util.Collections.unmodifiableSet;
25
26 import java.lang.annotation.Annotation;
27 import java.lang.reflect.Method;
28 import java.util.ArrayList;
29 import java.util.LinkedHashSet;
30 import java.util.List;
31 import java.util.Set;
32
33 import javax.cache.annotation.CacheAnnotationConfigurationException;
34 import javax.cache.annotation.CacheDefaults;
35 import javax.cache.annotation.CacheInvocationContext;
36 import javax.cache.annotation.CacheInvocationParameter;
37 import javax.cache.annotation.CacheKeyGenerator;
38 import javax.cache.annotation.CacheKeyParam;
39 import javax.cache.annotation.CachePut;
40 import javax.cache.annotation.CacheRemoveAll;
41 import javax.cache.annotation.CacheRemoveEntry;
42 import javax.cache.annotation.CacheResolverFactory;
43 import javax.cache.annotation.CacheResult;
44 import javax.cache.annotation.CacheValue;
45 import javax.inject.Inject;
46
47 import org.aopalliance.intercept.MethodInterceptor;
48 import org.aopalliance.intercept.MethodInvocation;
49
50 import com.google.inject.Injector;
51
52 abstract class CacheInterceptor<A extends Annotation>
53 implements MethodInterceptor
54 {
55
56 protected static final Object NULL_PLACEHOLDER = new Object();
57
58 @Inject
59 private Injector injector;
60
61 @Inject
62 private CacheResolverFactory cacheResolverFactory;
63
64 @Inject
65 private CacheKeyGenerator cacheKeyGenerator;
66
67 public final void setInjector( Injector injector )
68 {
69 this.injector = injector;
70 }
71
72 public final void setCacheResolverFactory( CacheResolverFactory cacheResolverFactory )
73 {
74 this.cacheResolverFactory = cacheResolverFactory;
75 }
76
77 public final void setCacheKeyGenerator( CacheKeyGenerator cacheKeyGenerator )
78 {
79 this.cacheKeyGenerator = cacheKeyGenerator;
80 }
81
82 public abstract Class<A> getInterceptedAnnotationType();
83
84 public final Object invoke( MethodInvocation invocation )
85 throws Throwable
86 {
87 String cacheName = getCacheName( invocation.getMethod() );
88 Object target = invocation.getThis();
89 A annotation = invocation.getMethod().getAnnotation( getInterceptedAnnotationType() );
90 Set<Annotation> methodAnnotations = toAnnotationsSet( invocation.getMethod().getAnnotations() );
91
92 boolean cacheValueAllowed = CachePut.class == getInterceptedAnnotationType();
93
94 CacheInvocationParameter[] allParameters = new CacheInvocationParameter[invocation.getArguments().length];
95 List<CacheInvocationParameter> keyParametersList = new ArrayList<CacheInvocationParameter>( allParameters.length );
96 CacheInvocationParameter valueParameter = null;
97
98 for ( int i = 0; i < invocation.getArguments().length; i++ )
99 {
100 Class<?> parameterType = invocation.getMethod().getParameterTypes()[i];
101
102 CacheInvocationParameter parameter = new DefaultCacheInvocationParameter( parameterType,
103 invocation.getArguments()[i],
104 toAnnotationsSet( invocation.getMethod().getParameterAnnotations()[i] ),
105 i );
106
107 for ( Annotation parameterAnnotation : invocation.getMethod().getParameterAnnotations()[i] )
108 {
109 if ( CacheKeyParam.class == parameterAnnotation.annotationType() )
110 {
111 keyParametersList.add( parameter );
112 }
113 else if ( CacheValue.class == parameterAnnotation.annotationType() )
114 {
115 if ( !cacheValueAllowed )
116 {
117 throw new CacheAnnotationConfigurationException( format( "CacheValue parameter annotation is not allowed on %s",
118 invocation.getMethod() ) );
119 }
120 else if ( valueParameter != null )
121 {
122 throw new CacheAnnotationConfigurationException( format( "Multiple CacheValue parameter annotations are not allowed on %s",
123 invocation.getMethod() ) );
124 }
125 else
126 {
127 valueParameter = parameter;
128 }
129 }
130 }
131
132 allParameters[i] = parameter;
133 }
134
135 CacheInvocationParameter[] keyParameters;
136
137 if ( keyParametersList.isEmpty() )
138 {
139 keyParameters = allParameters;
140 }
141 else
142 {
143 keyParameters = keyParametersList.toArray( new CacheInvocationParameter[keyParametersList.size()] );
144 }
145
146 return invoke( new DefaultCacheKeyInvocationContext<A>( injector,
147 cacheName,
148 target,
149 invocation.getMethod(),
150 allParameters,
151 keyParameters,
152 valueParameter,
153 methodAnnotations,
154 annotation ),
155 invocation );
156 }
157
158 protected abstract Object invoke( CacheInvocationContext<A> context, MethodInvocation invocation )
159 throws Throwable;
160
161 protected final CacheResolverFactory getCacheResolverFactory( CacheInvocationContext<A> context )
162 {
163 Class<? extends CacheResolverFactory> cacheKeyGeneratorType = getCacheResolverFactoryType( context.getCacheAnnotation() );
164
165 if ( CacheResolverFactory.class != cacheKeyGeneratorType )
166 {
167 return injector.getInstance( cacheKeyGeneratorType );
168 }
169
170 CacheDefaults cacheDefaults = context.getTarget().getClass().getAnnotation( CacheDefaults.class );
171
172 if ( cacheDefaults != null && cacheDefaults.cacheKeyGenerator().isAssignableFrom( CacheResolverFactory.class ) )
173 {
174 return injector.getInstance( cacheKeyGeneratorType );
175 }
176
177 return cacheResolverFactory;
178 }
179
180 protected final CacheKeyGenerator getCacheKeyGenerator( CacheInvocationContext<A> context )
181 {
182 Class<? extends CacheKeyGenerator> cacheKeyGeneratorType = getCacheKeyGeneratorType( context.getCacheAnnotation() );
183
184 if ( CacheKeyGenerator.class != cacheKeyGeneratorType )
185 {
186 return injector.getInstance( cacheKeyGeneratorType );
187 }
188
189 CacheDefaults cacheDefaults = context.getTarget().getClass().getAnnotation( CacheDefaults.class );
190
191 if ( cacheDefaults != null && CacheKeyGenerator.class != cacheDefaults.cacheKeyGenerator() )
192 {
193 return injector.getInstance( cacheKeyGeneratorType );
194 }
195
196 return cacheKeyGenerator;
197 }
198
199 @SuppressWarnings( "unchecked" )
200 private static String getCacheName( Method method )
201 {
202 for ( Class<? extends Annotation> annotationType : asList( CachePut.class,
203 CacheRemoveAll.class,
204 CacheRemoveEntry.class,
205 CacheResult.class ) )
206 {
207 if ( method.isAnnotationPresent( annotationType ) )
208 {
209 Annotation annotation = method.getAnnotation( annotationType );
210 String cacheName;
211 try
212 {
213 cacheName = (String) annotationType.getMethod( "cacheName" ).invoke( annotation );
214 }
215 catch ( Exception e )
216 {
217
218 cacheName = null;
219 }
220
221 if ( !isEmpty( cacheName ) )
222 {
223 return cacheName;
224 }
225 }
226 }
227
228 if ( method.getDeclaringClass().isAnnotationPresent( CacheDefaults.class ) )
229 {
230 CacheDefaults cacheDefaults = method.getDeclaringClass().getAnnotation( CacheDefaults.class );
231 if ( !isEmpty( cacheDefaults.cacheName() ) )
232 {
233 return cacheDefaults.cacheName();
234 }
235 }
236
237 return method.toGenericString();
238 }
239
240 static <T extends Throwable> boolean include( T throwable,
241 Class<? extends T>[] includes,
242 Class<? extends T>[] excludes,
243 boolean includeBothEmpty )
244 {
245 boolean includedEmpty = isEmpty( includes );
246 boolean excludedEmpty = isEmpty( excludes );
247
248 if ( includedEmpty && excludedEmpty )
249 {
250 return includeBothEmpty;
251 }
252
253 boolean isAssignableFromIncludes = isAssignable( throwable, includes );
254 boolean isAssignableFromExcludes = isAssignable( throwable, excludes );
255
256 if ( includedEmpty )
257 {
258 return !isAssignableFromExcludes;
259 }
260
261 if ( excludedEmpty )
262 {
263 return isAssignableFromIncludes;
264 }
265
266 return isAssignableFromIncludes && !isAssignableFromExcludes;
267 }
268
269 private static <T extends Throwable> boolean isAssignable( T target, Class<? extends T>[] from )
270 {
271 for ( final Class<? extends T> throwable : from )
272 {
273 if ( throwable.isAssignableFrom( target.getClass() ) )
274 {
275 return true;
276 }
277 }
278
279 return false;
280 }
281
282 private static final <T extends Throwable> boolean isEmpty( Class<? extends T>...types )
283 {
284 return types == null || types.length == 0;
285 }
286
287 private static boolean isEmpty( String value )
288 {
289 return value == null || value.length() == 0;
290 }
291
292 private static Set<Annotation> toAnnotationsSet( Annotation...annotations )
293 {
294 return unmodifiableSet( new LinkedHashSet<Annotation>( asList( annotations ) ) );
295 }
296
297 private static <A extends Annotation> Class<? extends CacheKeyGenerator> getCacheKeyGeneratorType( A annotation )
298 {
299 if ( CachePut.class.isInstance( annotation ) )
300 {
301 return ( (CachePut) annotation).cacheKeyGenerator();
302 }
303 else if ( CacheRemoveEntry.class.isInstance( annotation ) )
304 {
305 return ( (CacheRemoveEntry) annotation).cacheKeyGenerator();
306 }
307 else if ( CacheResult.class.isInstance( annotation ) )
308 {
309 return ( (CacheResult) annotation).cacheKeyGenerator();
310 }
311
312
313 return null;
314 }
315
316 private static <A extends Annotation> Class<? extends CacheResolverFactory> getCacheResolverFactoryType( A annotation )
317 {
318 if ( CachePut.class.isInstance( annotation ) )
319 {
320 return ( (CachePut) annotation).cacheResolverFactory();
321 }
322 else if ( CacheRemoveAll.class.isInstance( annotation ) )
323 {
324 return ( (CacheRemoveAll) annotation).cacheResolverFactory();
325 }
326 else if ( CacheRemoveEntry.class.isInstance( annotation ) )
327 {
328 return ( (CacheRemoveEntry) annotation).cacheResolverFactory();
329 }
330 else if ( CacheResult.class.isInstance( annotation ) )
331 {
332 return ( (CacheResult) annotation).cacheResolverFactory();
333 }
334
335
336 return null;
337 }
338
339 }