001package org.apache.turbine.annotation; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.lang.annotation.Annotation; 023import java.lang.reflect.AccessibleObject; 024import java.lang.reflect.Field; 025import java.util.List; 026import java.util.concurrent.ConcurrentHashMap; 027import java.util.concurrent.ConcurrentMap; 028 029import org.apache.commons.configuration.Configuration; 030import org.apache.commons.lang.StringUtils; 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.apache.turbine.Turbine; 034import org.apache.turbine.modules.Loader; 035import org.apache.turbine.services.ServiceManager; 036import org.apache.turbine.services.TurbineServices; 037import org.apache.turbine.services.assemblerbroker.AssemblerBrokerService; 038import org.apache.turbine.util.TurbineException; 039 040/** 041 * AnnotationProcessor contains static helper methods that handle the 042 * Turbine annotations for objects 043 * 044 * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a> 045 * @version $Id: TurbineAssemblerBrokerService.java 1521103 2013-09-09 13:38:07Z tv $ 046 */ 047public class AnnotationProcessor 048{ 049 /** Logging */ 050 private static Log log = LogFactory.getLog(AnnotationProcessor.class); 051 052 /** Annotation cache */ 053 private static ConcurrentMap<String, Annotation[]> annotationCache = new ConcurrentHashMap<String, Annotation[]>(); 054 055 /** 056 * Get cached annotations for field, class or method 057 * 058 * @param object a field, class or method 059 * 060 * @return the declared annotations for the object 061 */ 062 public static Annotation[] getAnnotations(AccessibleObject object) 063 { 064 String key = object.getClass() + object.toString(); 065 Annotation[] annotations = annotationCache.get(key); 066 if (annotations == null) 067 { 068 Annotation[] newAnnotations = object.getDeclaredAnnotations(); 069 annotations = annotationCache.putIfAbsent(key, newAnnotations); 070 if (annotations == null) 071 { 072 annotations = newAnnotations; 073 } 074 } 075 return annotations; 076 } 077 078 /** 079 * Search for annotated fields of the object and inject the appropriate 080 * objects 081 * 082 * @param object the object 083 * @throws TurbineException if the objects could not be injected 084 */ 085 public static void process(Object object) throws TurbineException 086 { 087 ServiceManager manager = null; 088 Configuration config = null; 089 AssemblerBrokerService assembler = null; 090 Class<?> clazz = object.getClass(); 091 092 while (clazz != null) 093 { 094 Field[] fields = clazz.getDeclaredFields(); 095 096 for (Field field : fields) 097 { 098 Annotation[] annotations = getAnnotations(field); 099 100 for (Annotation a : annotations) 101 { 102 if (a instanceof TurbineService) 103 { 104 if (manager == null) 105 { 106 manager = TurbineServices.getInstance(); 107 } 108 injectTurbineService(object, manager, field, (TurbineService) a); 109 } 110 else if (a instanceof TurbineConfiguration) 111 { 112 if (config == null) 113 { 114 config = Turbine.getConfiguration(); 115 } 116 injectTurbineConfiguration(object, config, field, (TurbineConfiguration) a); 117 } 118 else if (a instanceof TurbineLoader) 119 { 120 if (assembler == null) 121 { 122 assembler = (AssemblerBrokerService) TurbineServices.getInstance(). 123 getService(AssemblerBrokerService.SERVICE_NAME); 124 } 125 injectTurbineLoader(object, assembler, field, (TurbineLoader) a); 126 } 127 } 128 } 129 130 clazz = clazz.getSuperclass(); 131 } 132 } 133 134 /** 135 * Inject Turbine configuration into field of object 136 * 137 * @param object the object to process 138 * @param assembler AssemblerBrokerService, provides the loader 139 * @param field the field 140 * @param annotation the value of the annotation 141 * 142 * @throws TurbineException if loader cannot be set 143 */ 144 private static void injectTurbineLoader(Object object, AssemblerBrokerService assembler, Field field, TurbineLoader annotation) throws TurbineException 145 { 146 Loader<?> loader = assembler.getLoader(annotation.value()); 147 field.setAccessible(true); 148 149 try 150 { 151 if (log.isDebugEnabled()) 152 { 153 log.debug("Injection of " + loader + " into object " + object); 154 } 155 156 field.set(object, loader); 157 } 158 catch (IllegalArgumentException e) 159 { 160 throw new TurbineException("Could not inject loader " 161 + loader + " into object " + object, e); 162 } 163 catch (IllegalAccessException e) 164 { 165 throw new TurbineException("Could not inject loader " 166 + loader + " into object " + object, e); 167 } 168 } 169 170 /** 171 * Inject Turbine configuration into field of object 172 * 173 * @param object the object to process 174 * @param conf the configuration to use 175 * @param field the field 176 * @param annotation the value of the annotation 177 * 178 * @throws TurbineException if configuration cannot be set 179 */ 180 private static void injectTurbineConfiguration(Object object, Configuration conf, Field field, TurbineConfiguration annotation) throws TurbineException 181 { 182 Class<?> type = field.getType(); 183 String key = annotation.value(); 184 185 try 186 { 187 if (Configuration.class.isAssignableFrom(type)) 188 { 189 // Check for annotation value 190 if (StringUtils.isNotEmpty(key)) 191 { 192 conf = conf.subset(key); 193 } 194 195 if (log.isDebugEnabled()) 196 { 197 log.debug("Injection of " + conf + " into object " + object); 198 } 199 200 field.setAccessible(true); 201 field.set(object, conf); 202 } 203 else if (conf.containsKey(key)) 204 { 205 if ( String.class.isAssignableFrom( type ) ) 206 { 207 String value = conf.getString(key); 208 if (log.isDebugEnabled()) 209 { 210 log.debug("Injection of " + value + " into object " + object); 211 } 212 213 field.setAccessible(true); 214 field.set(object, value); 215 } 216 else if ( Boolean.TYPE.isAssignableFrom( type ) ) 217 { 218 boolean value = conf.getBoolean(key); 219 if (log.isDebugEnabled()) 220 { 221 log.debug("Injection of " + value + " into object " + object); 222 } 223 224 field.setAccessible(true); 225 field.setBoolean(object, value); 226 } 227 else if ( Integer.TYPE.isAssignableFrom( type ) ) 228 { 229 int value = conf.getInt(key); 230 if (log.isDebugEnabled()) 231 { 232 log.debug("Injection of " + value + " into object " + object); 233 } 234 235 field.setAccessible(true); 236 field.setInt(object, value); 237 } 238 else if ( Long.TYPE.isAssignableFrom( type ) ) 239 { 240 long value = conf.getLong(key); 241 if (log.isDebugEnabled()) 242 { 243 log.debug("Injection of " + value + " into object " + object); 244 } 245 246 field.setAccessible(true); 247 field.setLong(object, value); 248 } 249 else if ( Short.TYPE.isAssignableFrom( type ) ) 250 { 251 short value = conf.getShort(key); 252 if (log.isDebugEnabled()) 253 { 254 log.debug("Injection of " + value + " into object " + object); 255 } 256 257 field.setAccessible(true); 258 field.setShort(object, value); 259 } 260 else if ( Long.TYPE.isAssignableFrom( type ) ) 261 { 262 long value = conf.getLong(key); 263 if (log.isDebugEnabled()) 264 { 265 log.debug("Injection of " + value + " into object " + object); 266 } 267 268 field.setAccessible(true); 269 field.setLong(object, value); 270 } 271 else if ( Float.TYPE.isAssignableFrom( type ) ) 272 { 273 float value = conf.getFloat(key); 274 if (log.isDebugEnabled()) 275 { 276 log.debug("Injection of " + value + " into object " + object); 277 } 278 279 field.setAccessible(true); 280 field.setFloat(object, value); 281 } 282 else if ( Double.TYPE.isAssignableFrom( type ) ) 283 { 284 double value = conf.getDouble(key); 285 if (log.isDebugEnabled()) 286 { 287 log.debug("Injection of " + value + " into object " + object); 288 } 289 290 field.setAccessible(true); 291 field.setDouble(object, value); 292 } 293 else if ( Byte.TYPE.isAssignableFrom( type ) ) 294 { 295 byte value = conf.getByte(key); 296 if (log.isDebugEnabled()) 297 { 298 log.debug("Injection of " + value + " into object " + object); 299 } 300 301 field.setAccessible(true); 302 field.setByte(object, value); 303 } 304 else if ( List.class.isAssignableFrom( type ) ) 305 { 306 List<Object> values = conf.getList(key); 307 if (log.isDebugEnabled()) 308 { 309 log.debug("Injection of " + values + " into object " + object); 310 } 311 312 field.setAccessible(true); 313 field.set(object, values); 314 } 315 } 316 } 317 catch (IllegalArgumentException e) 318 { 319 throw new TurbineException("Could not inject configuration " 320 + conf + " into object " + object, e); 321 } 322 catch (IllegalAccessException e) 323 { 324 throw new TurbineException("Could not inject configuration " 325 + conf + " into object " + object, e); 326 } 327 } 328 329 /** 330 * Inject Turbine service into field of object 331 * 332 * @param object the object to process 333 * @param manager the service manager 334 * @param field the field 335 * @param annotation the value of the annotation 336 * 337 * @throws TurbineException if service is not available 338 */ 339 private static void injectTurbineService(Object object, ServiceManager manager, Field field, TurbineService annotation) throws TurbineException 340 { 341 String serviceName = null; 342 // Check for annotation value 343 if (StringUtils.isNotEmpty(annotation.value())) 344 { 345 serviceName = annotation.value(); 346 } 347 // Check for fields SERVICE_NAME and ROLE 348 else 349 { 350 Field[] typeFields = field.getType().getFields(); 351 for (Field f : typeFields) 352 { 353 if (TurbineService.SERVICE_NAME.equals(f.getName())) 354 { 355 try 356 { 357 serviceName = (String)f.get(null); 358 } 359 catch (Exception e) 360 { 361 continue; 362 } 363 break; 364 } 365 else if (TurbineService.ROLE.equals(f.getName())) 366 { 367 try 368 { 369 serviceName = (String)f.get(null); 370 } 371 catch (Exception e) 372 { 373 continue; 374 } 375 break; 376 } 377 } 378 } 379 380 if (StringUtils.isEmpty(serviceName)) 381 { 382 // Try interface class name 383 serviceName = field.getType().getName(); 384 } 385 386 if (log.isDebugEnabled()) 387 { 388 log.debug("Looking up service for injection: " + serviceName + " for object " + object); 389 } 390 391 Object service = manager.getService(serviceName); // throws Exception on unknown service 392 field.setAccessible(true); 393 394 try 395 { 396 if (log.isDebugEnabled()) 397 { 398 log.debug("Injection of " + serviceName + " into object " + object); 399 } 400 401 field.set(object, service); 402 } 403 catch (IllegalArgumentException e) 404 { 405 throw new TurbineException("Could not inject service " 406 + serviceName + " into object " + object, e); 407 } 408 catch (IllegalAccessException e) 409 { 410 throw new TurbineException("Could not inject service " 411 + serviceName + " into object " + object, e); 412 } 413 } 414}