Coverage Report - org.apache.onami.cache.CacheInterceptor
 
Classes in this File Line Coverage Branch Coverage Complexity
CacheInterceptor
0%
0/96
0%
0/78
4.375
 
 1  
 package org.apache.onami.cache;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *   http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 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  0
 abstract class CacheInterceptor<A extends Annotation>
 53  
     implements MethodInterceptor
 54  
 {
 55  
 
 56  0
     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  0
         this.injector = injector;
 70  0
     }
 71  
 
 72  
     public final void setCacheResolverFactory( CacheResolverFactory cacheResolverFactory )
 73  
     {
 74  0
         this.cacheResolverFactory = cacheResolverFactory;
 75  0
     }
 76  
 
 77  
     public final void setCacheKeyGenerator( CacheKeyGenerator cacheKeyGenerator )
 78  
     {
 79  0
         this.cacheKeyGenerator = cacheKeyGenerator;
 80  0
     }
 81  
 
 82  
     public abstract Class<A> getInterceptedAnnotationType();
 83  
 
 84  
     public final Object invoke( MethodInvocation invocation )
 85  
         throws Throwable
 86  
     {
 87  0
         String cacheName = getCacheName( invocation.getMethod() );
 88  0
         Object target = invocation.getThis();
 89  0
         A annotation = invocation.getMethod().getAnnotation( getInterceptedAnnotationType() );
 90  0
         Set<Annotation> methodAnnotations = toAnnotationsSet( invocation.getMethod().getAnnotations() );
 91  
 
 92  0
         boolean cacheValueAllowed = CachePut.class == getInterceptedAnnotationType();
 93  
 
 94  0
         CacheInvocationParameter[] allParameters = new CacheInvocationParameter[invocation.getArguments().length];
 95  0
         List<CacheInvocationParameter> keyParametersList = new ArrayList<CacheInvocationParameter>( allParameters.length );
 96  0
         CacheInvocationParameter valueParameter = null;
 97  
 
 98  0
         for ( int i = 0; i < invocation.getArguments().length; i++ )
 99  
         {
 100  0
             Class<?> parameterType = invocation.getMethod().getParameterTypes()[i];
 101  
 
 102  0
             CacheInvocationParameter parameter = new DefaultCacheInvocationParameter( parameterType,
 103  
                                                                                       invocation.getArguments()[i],
 104  
                                                                                       toAnnotationsSet( invocation.getMethod().getParameterAnnotations()[i] ),
 105  
                                                                                       i );
 106  
 
 107  0
             for ( Annotation parameterAnnotation : invocation.getMethod().getParameterAnnotations()[i] )
 108  
             {
 109  0
                 if ( CacheKeyParam.class == parameterAnnotation.annotationType() )
 110  
                 {
 111  0
                     keyParametersList.add( parameter );
 112  
                 }
 113  0
                 else if ( CacheValue.class == parameterAnnotation.annotationType() )
 114  
                 {
 115  0
                     if ( !cacheValueAllowed )
 116  
                     {
 117  0
                         throw new CacheAnnotationConfigurationException( format( "CacheValue parameter annotation is not allowed on %s",
 118  
                                                                                  invocation.getMethod() ) );
 119  
                     }
 120  0
                     else if ( valueParameter != null )
 121  
                     {
 122  0
                         throw new CacheAnnotationConfigurationException( format( "Multiple CacheValue parameter annotations are not allowed on %s",
 123  
                                                                                  invocation.getMethod() ) );
 124  
                     }
 125  
                     else
 126  
                     {
 127  0
                         valueParameter = parameter;
 128  
                     }
 129  
                 }
 130  
             }
 131  
 
 132  0
             allParameters[i] = parameter;
 133  
         }
 134  
 
 135  
         CacheInvocationParameter[] keyParameters;
 136  
 
 137  0
         if ( keyParametersList.isEmpty() )
 138  
         {
 139  0
             keyParameters = allParameters;
 140  
         }
 141  
         else
 142  
         {
 143  0
             keyParameters = keyParametersList.toArray( new CacheInvocationParameter[keyParametersList.size()] );
 144  
         }
 145  
 
 146  0
         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  0
         Class<? extends CacheResolverFactory> cacheKeyGeneratorType = getCacheResolverFactoryType( context.getCacheAnnotation() );
 164  
 
 165  0
         if ( CacheResolverFactory.class != cacheKeyGeneratorType )
 166  
         {
 167  0
             return injector.getInstance( cacheKeyGeneratorType );
 168  
         }
 169  
 
 170  0
         CacheDefaults cacheDefaults = context.getTarget().getClass().getAnnotation( CacheDefaults.class );
 171  
 
 172  0
         if ( cacheDefaults != null && cacheDefaults.cacheKeyGenerator().isAssignableFrom( CacheResolverFactory.class ) )
 173  
         {
 174  0
             return injector.getInstance( cacheKeyGeneratorType );
 175  
         }
 176  
 
 177  0
         return cacheResolverFactory;
 178  
     }
 179  
 
 180  
     protected final CacheKeyGenerator getCacheKeyGenerator( CacheInvocationContext<A> context )
 181  
     {
 182  0
         Class<? extends CacheKeyGenerator> cacheKeyGeneratorType = getCacheKeyGeneratorType( context.getCacheAnnotation() );
 183  
 
 184  0
         if ( CacheKeyGenerator.class != cacheKeyGeneratorType )
 185  
         {
 186  0
             return injector.getInstance( cacheKeyGeneratorType );
 187  
         }
 188  
 
 189  0
         CacheDefaults cacheDefaults = context.getTarget().getClass().getAnnotation( CacheDefaults.class );
 190  
 
 191  0
         if ( cacheDefaults != null && CacheKeyGenerator.class != cacheDefaults.cacheKeyGenerator() )
 192  
         {
 193  0
             return injector.getInstance( cacheKeyGeneratorType );
 194  
         }
 195  
 
 196  0
         return cacheKeyGenerator;
 197  
     }
 198  
 
 199  
     @SuppressWarnings( "unchecked" )
 200  
     private static String getCacheName( Method method )
 201  
     {
 202  0
         for ( Class<? extends Annotation> annotationType : asList( CachePut.class,
 203  
                                                                    CacheRemoveAll.class,
 204  
                                                                    CacheRemoveEntry.class,
 205  
                                                                    CacheResult.class ) )
 206  
         {
 207  0
             if ( method.isAnnotationPresent( annotationType ) )
 208  
             {
 209  0
                 Annotation annotation = method.getAnnotation( annotationType );
 210  
                 String cacheName;
 211  
                 try
 212  
                 {
 213  0
                     cacheName = (String) annotationType.getMethod( "cacheName" ).invoke( annotation );
 214  
                 }
 215  0
                 catch ( Exception e )
 216  
                 {
 217  
                     // should not happen, all enlisted annotations have "cacheName()" method
 218  0
                     cacheName = null;
 219  0
                 }
 220  
 
 221  0
                 if ( !isEmpty( cacheName ) )
 222  
                 {
 223  0
                     return cacheName;
 224  
                 }
 225  0
             }
 226  
         }
 227  
 
 228  0
         if ( method.getDeclaringClass().isAnnotationPresent( CacheDefaults.class ) )
 229  
         {
 230  0
             CacheDefaults cacheDefaults = method.getDeclaringClass().getAnnotation( CacheDefaults.class );
 231  0
             if ( !isEmpty( cacheDefaults.cacheName() ) )
 232  
             {
 233  0
                 return cacheDefaults.cacheName();
 234  
             }
 235  
         }
 236  
 
 237  0
         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  0
         boolean includedEmpty = isEmpty( includes );
 246  0
         boolean excludedEmpty = isEmpty( excludes );
 247  
 
 248  0
         if ( includedEmpty && excludedEmpty )
 249  
         {
 250  0
             return includeBothEmpty;
 251  
         }
 252  
 
 253  0
         boolean isAssignableFromIncludes = isAssignable( throwable, includes );
 254  0
         boolean isAssignableFromExcludes = isAssignable( throwable, excludes );
 255  
 
 256  0
         if ( includedEmpty )
 257  
         {
 258  0
             return !isAssignableFromExcludes;
 259  
         }
 260  
 
 261  0
         if ( excludedEmpty )
 262  
         {
 263  0
             return isAssignableFromIncludes;
 264  
         }
 265  
 
 266  0
         return isAssignableFromIncludes && !isAssignableFromExcludes;
 267  
     }
 268  
 
 269  
     private static <T extends Throwable> boolean isAssignable( T target, Class<? extends T>[] from )
 270  
     {
 271  0
         for ( final Class<? extends T> throwable : from )
 272  
         {
 273  0
             if ( throwable.isAssignableFrom( target.getClass() ) )
 274  
             {
 275  0
                 return true;
 276  
             }
 277  
         }
 278  
 
 279  0
         return false;
 280  
     }
 281  
 
 282  
     private static final <T extends Throwable> boolean isEmpty( Class<? extends T>...types )
 283  
     {
 284  0
         return types == null || types.length == 0;
 285  
     }
 286  
 
 287  
     private static boolean isEmpty( String value )
 288  
     {
 289  0
         return value == null || value.length() == 0;
 290  
     }
 291  
 
 292  
     private static Set<Annotation> toAnnotationsSet( Annotation...annotations )
 293  
     {
 294  0
         return unmodifiableSet( new LinkedHashSet<Annotation>( asList( annotations ) ) );
 295  
     }
 296  
 
 297  
     private static <A extends Annotation> Class<? extends CacheKeyGenerator> getCacheKeyGeneratorType( A annotation )
 298  
     {
 299  0
         if ( CachePut.class.isInstance( annotation ) )
 300  
         {
 301  0
             return ( (CachePut) annotation).cacheKeyGenerator();
 302  
         }
 303  0
         else if ( CacheRemoveEntry.class.isInstance( annotation ) )
 304  
         {
 305  0
             return ( (CacheRemoveEntry) annotation).cacheKeyGenerator();
 306  
         }
 307  0
         else if ( CacheResult.class.isInstance( annotation ) )
 308  
         {
 309  0
             return ( (CacheResult) annotation).cacheKeyGenerator();
 310  
         }
 311  
 
 312  
         // doesn't happen
 313  0
         return null;
 314  
     }
 315  
 
 316  
     private static <A extends Annotation> Class<? extends CacheResolverFactory> getCacheResolverFactoryType( A annotation )
 317  
     {
 318  0
         if ( CachePut.class.isInstance( annotation ) )
 319  
         {
 320  0
             return ( (CachePut) annotation).cacheResolverFactory();
 321  
         }
 322  0
         else if ( CacheRemoveAll.class.isInstance( annotation ) )
 323  
         {
 324  0
             return ( (CacheRemoveAll) annotation).cacheResolverFactory();
 325  
         }
 326  0
         else if ( CacheRemoveEntry.class.isInstance( annotation ) )
 327  
         {
 328  0
             return ( (CacheRemoveEntry) annotation).cacheResolverFactory();
 329  
         }
 330  0
         else if ( CacheResult.class.isInstance( annotation ) )
 331  
         {
 332  0
             return ( (CacheResult) annotation).cacheResolverFactory();
 333  
         }
 334  
 
 335  
         // doesn't happen
 336  0
         return null;
 337  
     }
 338  
 
 339  
 }