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}