Coverage Report - org.apache.turbine.annotation.AnnotationProcessor
 
Classes in this File Line Coverage Branch Coverage Complexity
AnnotationProcessor
63%
118/185
57%
59/102
11,286
AnnotationProcessor$1
100%
1/1
N/A
11,286
AnnotationProcessor$ConditionType
100%
2/2
N/A
11,286
 
 1  
 package org.apache.turbine.annotation;
 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 java.lang.annotation.Annotation;
 23  
 import java.lang.reflect.AccessibleObject;
 24  
 import java.lang.reflect.Field;
 25  
 import java.util.List;
 26  
 import java.util.concurrent.ConcurrentHashMap;
 27  
 import java.util.concurrent.ConcurrentMap;
 28  
 
 29  
 import org.apache.commons.configuration2.Configuration;
 30  
 import org.apache.commons.lang3.StringUtils;
 31  
 import org.apache.fulcrum.security.model.turbine.TurbineAccessControlList;
 32  
 import org.apache.logging.log4j.LogManager;
 33  
 import org.apache.logging.log4j.Logger;
 34  
 import org.apache.turbine.Turbine;
 35  
 import org.apache.turbine.modules.Loader;
 36  
 import org.apache.turbine.services.ServiceManager;
 37  
 import org.apache.turbine.services.TurbineServices;
 38  
 import org.apache.turbine.services.assemblerbroker.AssemblerBrokerService;
 39  
 import org.apache.turbine.util.TurbineException;
 40  
 
 41  
 /**
 42  
  * AnnotationProcessor contains static helper methods that handle the
 43  
  * Turbine annotations for objects
 44  
  *
 45  
  * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
 46  
  * @version $Id: TurbineAssemblerBrokerService.java 1521103 2013-09-09 13:38:07Z tv $
 47  
  */
 48  0
 public class AnnotationProcessor
 49  
 {
 50  
     /** Logging */
 51  102
     private static Logger log = LogManager.getLogger(AnnotationProcessor.class);
 52  
 
 53  
     /** Annotation cache */
 54  102
     private static ConcurrentMap<String, Annotation[]> annotationCache = new ConcurrentHashMap<String, Annotation[]>();
 55  
 
 56  
     /**
 57  
      * Get cached annotations for field, class or method
 58  
      *
 59  
      * @param object a field, class or method
 60  
      *
 61  
      * @return the declared annotations for the object
 62  
      */
 63  
     public static Annotation[] getAnnotations(AccessibleObject object)
 64  
     {
 65  6695
         String key = object.getClass() + object.toString();
 66  6695
         Annotation[] annotations = annotationCache.get(key);
 67  6695
         if (annotations == null)
 68  
         {
 69  3930
             Annotation[] newAnnotations = object.getDeclaredAnnotations();
 70  3930
             annotations = annotationCache.putIfAbsent(key, newAnnotations);
 71  3930
             if (annotations == null)
 72  
             {
 73  3930
                 annotations = newAnnotations;
 74  
             }
 75  
         }
 76  6695
         return annotations;
 77  
     }
 78  
 
 79  278
     public enum ConditionType
 80  
     {
 81  41
         COMPOUND, ANY;
 82  
     }
 83  
 
 84  
     /**
 85  
      * Check if the object given is authorized to be executed based on its annotations
 86  
      *
 87  
      * The method will return false if one of the annotations denies execution
 88  
      *
 89  
      * @see #isAuthorized(AccessibleObject, TurbineAccessControlList, ConditionType)
 90  
      *
 91  
      * @param <A> ACL instance
 92  
      * @param object accessible object to test
 93  
      * @param acl access control list
 94  
      * @return true if the execution is allowed
 95  
      */
 96  
     public static <A extends TurbineAccessControlList<?>> boolean isAuthorized(AccessibleObject object, A acl)
 97  
     {
 98  0
         return isAuthorized( object, acl, ConditionType.COMPOUND );
 99  
     }
 100  
 
 101  
     /**
 102  
      * Check if the object given is authorized to be executed based on its annotations
 103  
      * The method's return value depends on the conditonType, refer to the ConditionType
 104  
      *
 105  
      * @param <A> ACL instance
 106  
      * @param object the object
 107  
      * @param acl access control list
 108  
      * @param conditonType either {@link ConditionType#COMPOUND}: The method will return false if one of the annotations denies execution
 109  
      *                     or {@link ConditionType#ANY} : The method will return true if one of the annotations allows execution
 110  
      * @return true if the execution is allowed
 111  
      */
 112  
     public static <A extends TurbineAccessControlList<?>> boolean isAuthorized(AccessibleObject object, A acl, ConditionType conditonType)
 113  
     {
 114  12
         Annotation[] annotations = getAnnotations(object);
 115  
 
 116  12
         for (Annotation annotation : annotations)
 117  
         {
 118  9
             if (annotation instanceof TurbineRequiredRole)
 119  
             {
 120  9
                 TurbineRequiredRole trr = (TurbineRequiredRole) annotation;
 121  9
                 String[] roleNames = trr.value();
 122  9
                 String group = trr.group();
 123  
 
 124  9
                 if (StringUtils.isEmpty(group)) // global group
 125  
                 {
 126  15
                     for (String roleName : roleNames)
 127  
                     {
 128  3
                         switch ( conditonType ) {
 129  
                             case COMPOUND: default:
 130  6
                                 if (!acl.hasRole(roleName))
 131  
                                 {
 132  3
                                     return false;
 133  
                                 }
 134  
                                 break;
 135  
                             case ANY:
 136  6
                                 if (acl.hasRole(roleName))
 137  
                                 {
 138  3
                                     return true;
 139  
                                 }
 140  
                                 break;
 141  
                         }
 142  
                     }
 143  3
                     if (conditonType == ConditionType.ANY) { // nothing matched
 144  3
                         return false;
 145  
                     }
 146  
                 }
 147  
                 else
 148  
                 {
 149  0
                     for (String roleName : roleNames)
 150  
                     {
 151  0
                         switch ( conditonType ) {
 152  
                             case COMPOUND: default:
 153  0
                                 if (!acl.hasRole(roleName, group))
 154  
                                 {
 155  0
                                     return false;
 156  
                                 }
 157  
                                 break;
 158  
                             case ANY:
 159  0
                                 if (acl.hasRole(roleName, group))
 160  
                                 {
 161  0
                                     return true;
 162  
                                 }
 163  
                                 break;
 164  
                         }
 165  
                     }
 166  
                 }
 167  0
             }
 168  0
             else if (annotation instanceof TurbineRequiredPermission)
 169  
             {
 170  0
                 TurbineRequiredPermission trp = (TurbineRequiredPermission) annotation;
 171  0
                 String[] permissionNames = trp.value();
 172  0
                 String group = trp.group();
 173  
 
 174  0
                 if (StringUtils.isEmpty(group)) // global group
 175  
                 {
 176  0
                     for (String permissionName : permissionNames)
 177  
                     {
 178  0
                         switch ( conditonType ) {
 179  
                             case COMPOUND: default:
 180  0
                                 if (!acl.hasPermission(permissionName))
 181  
                                 {
 182  0
                                     return false;
 183  
                                 }
 184  
                                 break;
 185  
                             case ANY:
 186  0
                                 if (acl.hasPermission(permissionName))
 187  
                                 {
 188  0
                                     return true;
 189  
                                 }
 190  
                                 break;
 191  
                         }
 192  
                     }
 193  
                 }
 194  
                 else
 195  
                 {
 196  0
                     for (String permissionName : permissionNames)
 197  
                     {
 198  0
                         switch ( conditonType ) {
 199  
                             case COMPOUND: default:
 200  0
                                 if (!acl.hasPermission(permissionName, group))
 201  
                                 {
 202  0
                                     return false;
 203  
                                 }
 204  
                                 break;
 205  
                             case ANY:
 206  0
                                 if (acl.hasPermission(permissionName, group))
 207  
                                 {
 208  0
                                     return true;
 209  
                                 }
 210  
                                 break;
 211  
                         }
 212  
 
 213  
                     }
 214  
                 }
 215  
             }
 216  
         }
 217  
 
 218  3
         return true;
 219  
     }
 220  
 
 221  
     /**
 222  
      * Search for annotated fields of the object and inject the appropriate
 223  
      * objects
 224  
      *
 225  
      * @param object the object
 226  
      * @throws TurbineException if the objects could not be injected
 227  
      */
 228  
     public static void process(Object object) throws TurbineException
 229  
     {
 230  1764
         ServiceManager manager = null;
 231  1764
         Configuration config = null;
 232  1764
         AssemblerBrokerService assembler = null;
 233  1764
         Class<?> clazz = object.getClass();
 234  
 
 235  5529
         while (clazz != null)
 236  
         {
 237  3765
             Field[] fields = clazz.getDeclaredFields();
 238  
 
 239  10050
             for (Field field : fields)
 240  
             {
 241  6285
                 Annotation[] annotations = getAnnotations(field);
 242  
 
 243  8487
                 for (Annotation a : annotations)
 244  
                 {
 245  2202
                     if (a instanceof TurbineService)
 246  
                     {
 247  474
                         if (manager == null)
 248  
                         {
 249  378
                             manager = TurbineServices.getInstance();
 250  
                         }
 251  474
                         injectTurbineService(object, manager, field, (TurbineService) a);
 252  
                     }
 253  1728
                     else if (a instanceof TurbineConfiguration)
 254  
                     {
 255  1080
                         if (config == null)
 256  
                         {
 257  798
                             config = Turbine.getConfiguration();
 258  
                         }
 259  1080
                         injectTurbineConfiguration(object, config, field, (TurbineConfiguration) a);
 260  
                     }
 261  648
                     else if (a instanceof TurbineLoader)
 262  
                     {
 263  648
                         if (assembler == null)
 264  
                         {
 265  624
                             assembler = (AssemblerBrokerService) TurbineServices.getInstance().
 266  624
                                 getService(AssemblerBrokerService.SERVICE_NAME);
 267  
                         }
 268  648
                         injectTurbineLoader(object, assembler, field, (TurbineLoader) a);
 269  
                     }
 270  
                 }
 271  
             }
 272  
 
 273  3765
             clazz = clazz.getSuperclass();
 274  3765
         }
 275  1764
     }
 276  
 
 277  
     /**
 278  
      * Inject Turbine configuration into field of object
 279  
      *
 280  
      * @param object the object to process
 281  
      * @param assembler AssemblerBrokerService, provides the loader
 282  
      * @param field the field
 283  
      * @param annotation the value of the annotation
 284  
      *
 285  
      * @throws TurbineException if loader cannot be set
 286  
      */
 287  
     private static void injectTurbineLoader(Object object, AssemblerBrokerService assembler, Field field, TurbineLoader annotation) throws TurbineException
 288  
     {
 289  648
         Loader<?> loader = assembler.getLoader(annotation.value());
 290  648
         field.setAccessible(true);
 291  
 
 292  
         try
 293  
         {
 294  648
             log.debug("Injection of {} into object {}", loader, object);
 295  
 
 296  648
             field.set(object, loader);
 297  
         }
 298  0
         catch (IllegalArgumentException | IllegalAccessException e)
 299  
         {
 300  0
             throw new TurbineException("Could not inject loader "
 301  
                     + loader + " into object " + object, e);
 302  648
         }
 303  648
     }
 304  
 
 305  
     /**
 306  
      * Inject Turbine configuration into field of object
 307  
      *
 308  
      * @param object the object to process
 309  
      * @param conf the configuration to use
 310  
      * @param field the field
 311  
      * @param annotation the value of the annotation
 312  
      *
 313  
      * @throws TurbineException if configuration cannot be set
 314  
      */
 315  
     @SuppressWarnings("boxing")
 316  
     private static void injectTurbineConfiguration(Object object, Configuration conf, Field field, TurbineConfiguration annotation) throws TurbineException
 317  
     {
 318  1080
         Class<?> type = field.getType();
 319  1080
         String key = annotation.value();
 320  
 
 321  
         try
 322  
         {
 323  1080
             if (Configuration.class.isAssignableFrom(type))
 324  
             {
 325  
                 final Configuration injectConfiguration;
 326  
                 // Check for annotation value
 327  6
                 if (StringUtils.isNotEmpty(key))
 328  
                 {
 329  3
                     injectConfiguration = conf.subset(key);
 330  
                 }
 331  
                 else
 332  
                 {
 333  3
                     injectConfiguration = conf;
 334  
                 }
 335  
 
 336  6
                 log.debug("Injection of {} into object {}", injectConfiguration, object);
 337  
 
 338  6
                 field.setAccessible(true);
 339  6
                 field.set(object, injectConfiguration);
 340  6
             }
 341  1074
             else if (conf.containsKey(key))
 342  
             {
 343  483
                 if ( String.class.isAssignableFrom( type ) )
 344  
                 {
 345  414
                     String value = conf.getString(key);
 346  414
                     log.debug("Injection of {} into object {}", value, object);
 347  
 
 348  414
                     field.setAccessible(true);
 349  414
                     field.set(object, value);
 350  414
                 }
 351  69
                 else if ( Boolean.TYPE.isAssignableFrom( type ) )
 352  
                 {
 353  57
                     boolean value = conf.getBoolean(key);
 354  57
                     log.debug("Injection of {} into object {}", value, object);
 355  
 
 356  57
                     field.setAccessible(true);
 357  57
                     field.setBoolean(object, value);
 358  57
                 }
 359  12
                 else if ( Integer.TYPE.isAssignableFrom( type ) )
 360  
                 {
 361  9
                     int value = conf.getInt(key);
 362  9
                     log.debug("Injection of {} into object {}", value, object);
 363  
 
 364  9
                     field.setAccessible(true);
 365  9
                     field.setInt(object, value);
 366  9
                 }
 367  3
                 else if ( Long.TYPE.isAssignableFrom( type ) )
 368  
                 {
 369  0
                     long value = conf.getLong(key);
 370  0
                     log.debug("Injection of {} into object {}", value, object);
 371  
 
 372  0
                     field.setAccessible(true);
 373  0
                     field.setLong(object, value);
 374  0
                 }
 375  3
                 else if ( Short.TYPE.isAssignableFrom( type ) )
 376  
                 {
 377  0
                     short value = conf.getShort(key);
 378  0
                     log.debug("Injection of {} into object {}", value, object);
 379  
 
 380  0
                     field.setAccessible(true);
 381  0
                     field.setShort(object, value);
 382  0
                 }
 383  3
                 else if ( Long.TYPE.isAssignableFrom( type ) )
 384  
                 {
 385  0
                     long value = conf.getLong(key);
 386  0
                     log.debug("Injection of {} into object {}", value, object);
 387  
 
 388  0
                     field.setAccessible(true);
 389  0
                     field.setLong(object, value);
 390  0
                 }
 391  3
                 else if ( Float.TYPE.isAssignableFrom( type ) )
 392  
                 {
 393  0
                     float value = conf.getFloat(key);
 394  0
                     log.debug("Injection of {} into object {}", value, object);
 395  
 
 396  0
                     field.setAccessible(true);
 397  0
                     field.setFloat(object, value);
 398  0
                 }
 399  3
                 else if ( Double.TYPE.isAssignableFrom( type ) )
 400  
                 {
 401  0
                     double value = conf.getDouble(key);
 402  0
                     log.debug("Injection of {} into object {}", value, object);
 403  
 
 404  0
                     field.setAccessible(true);
 405  0
                     field.setDouble(object, value);
 406  0
                 }
 407  3
                 else if ( Byte.TYPE.isAssignableFrom( type ) )
 408  
                 {
 409  0
                     byte value = conf.getByte(key);
 410  0
                     log.debug("Injection of {} into object {}", value, object);
 411  
 
 412  0
                     field.setAccessible(true);
 413  0
                     field.setByte(object, value);
 414  0
                 }
 415  3
                 else if ( List.class.isAssignableFrom( type ) )
 416  
                 {
 417  3
                     List<Object> values = conf.getList(key);
 418  3
                     log.debug("Injection of {} into object {}", values, object);
 419  
 
 420  3
                     field.setAccessible(true);
 421  3
                     field.set(object, values);
 422  
                 }
 423  
             }
 424  
         }
 425  0
         catch (IllegalArgumentException | IllegalAccessException e)
 426  
         {
 427  0
             throw new TurbineException("Could not inject configuration "
 428  
                     + conf + " into object " + object, e);
 429  1080
         }
 430  1080
     }
 431  
 
 432  
     /**
 433  
      * Inject Turbine service into field of object
 434  
      *
 435  
      * @param object the object to process
 436  
      * @param manager the service manager
 437  
      * @param field the field
 438  
      * @param annotation the value of the annotation
 439  
      *
 440  
      * @throws TurbineException if service is not available
 441  
      */
 442  
     private static void injectTurbineService(Object object, ServiceManager manager, Field field, TurbineService annotation) throws TurbineException
 443  
     {
 444  474
         String serviceName = null;
 445  
         // Check for annotation value
 446  474
         if (StringUtils.isNotEmpty(annotation.value()))
 447  
         {
 448  3
             serviceName = annotation.value();
 449  
         }
 450  
         // Check for fields SERVICE_NAME and ROLE
 451  
         else
 452  
         {
 453  471
             Field[] typeFields = field.getType().getFields();
 454  471
             for (Field f : typeFields)
 455  
             {
 456  471
                 if (TurbineService.SERVICE_NAME.equals(f.getName()))
 457  
                 {
 458  
                     try
 459  
                     {
 460  420
                         serviceName = (String)f.get(null);
 461  
                     }
 462  0
                     catch (IllegalArgumentException | IllegalAccessException e)
 463  
                     {
 464  0
                         continue;
 465  420
                     }
 466  
                     break;
 467  
                 }
 468  51
                 else if (TurbineService.ROLE.equals(f.getName()))
 469  
                 {
 470  
                     try
 471  
                     {
 472  51
                         serviceName = (String)f.get(null);
 473  
                     }
 474  0
                     catch (IllegalArgumentException | IllegalAccessException e)
 475  
                     {
 476  0
                         continue;
 477  51
                     }
 478  
                     break;
 479  
                 }
 480  
             }
 481  
         }
 482  
 
 483  474
         if (StringUtils.isEmpty(serviceName))
 484  
         {
 485  
             // Try interface class name
 486  0
             serviceName = field.getType().getName();
 487  
         }
 488  
 
 489  474
         log.debug("Looking up service for injection: {} for object {}", serviceName, object);
 490  
 
 491  474
         Object service = manager.getService(serviceName); // throws Exception on unknown service
 492  474
         field.setAccessible(true);
 493  
 
 494  
         try
 495  
         {
 496  474
             log.debug("Injection of {} into object {}", serviceName, object);
 497  
 
 498  474
             field.set(object, service);
 499  
         }
 500  0
         catch (IllegalArgumentException | IllegalAccessException e)
 501  
         {
 502  0
             throw new TurbineException("Could not inject service "
 503  
                     + serviceName + " into object " + object, e);
 504  474
         }
 505  474
     }
 506  
 }