    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}