View Javadoc
1   /*
2    * Copyright 2011 FatWire Corporation. All Rights Reserved.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.fatwire.gst.foundation.controller.action;
18  
19  import java.lang.annotation.Annotation;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.InvocationTargetException;
22  import java.lang.reflect.Method;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.springframework.beans.BeanUtils;
30  
31  import com.fatwire.gst.foundation.DebugHelper;
32  import com.fatwire.gst.foundation.controller.annotation.InjectForRequest;
33  
34  /**
35   * Helper to inject dependencies into Object based on annotated fields and
36   * methods.
37   * 
38   * @author Dolf Dijkstra
39   * @since Mar 26, 2011
40   */
41  public final class AnnotationInjector {
42      protected static final Log LOG = LogFactory.getLog(AnnotationInjector.class.getPackage().getName());
43      protected static final Log LOG_TIME = LogFactory.getLog(AnnotationInjector.class.getPackage().getName() + ".time");
44  
45      /**
46       * Inject ICS runtime objects into the object. Objects flagged with the
47       * {@link InjectForRequest} annotation will be populated by this method by
48       * retrieving the value from the {@link Factory#getObject(String,Class)}
49       * method.
50       * 
51       * @param object the object to inject into
52       * @param factory the factory that created the objects that need to be
53       *            injected.
54       */
55      public static final void inject(final Object object, final Factory factory) {
56          if (object == null) {
57              throw new IllegalArgumentException("object cannot be null.");
58          }
59          if (factory == null) {
60              throw new IllegalArgumentException("factory cannot be null.");
61          }
62          final long start = LOG_TIME.isDebugEnabled() ? System.nanoTime() : 0L;
63          try {
64              Class<?> c = object.getClass();
65              // first to all annotated public setter methods.
66              for (final Method method : c.getMethods()) {
67                  if (method.isAnnotationPresent(InjectForRequest.class)) {
68                      injectIntoMethod(object, factory, method);
69                  }
70              }
71              // and then all annotated fields.
72              while (c != Object.class && c != null) {
73                  for (final Field field : c.getDeclaredFields()) {
74                      if (field.isAnnotationPresent(InjectForRequest.class)) {
75                          injectIntoField(object, factory, field);
76                      }
77  
78                  }
79  
80                  c = c.getSuperclass();
81              }
82          } finally {
83              DebugHelper.printTime(LOG_TIME, "inject model for " + object.getClass().getName(), start);
84          }
85      }
86  
87      /**
88       * Finds the fields in the class or super class that are annotated with the
89       * <tt>annnotationClass</tt> annotation.
90       * 
91       * @param object the object to inspect.
92       * @param annnotationClass the annotation to find.
93       * @return the array of fields with the annotation, never null.
94       */
95      public static final Field[] findFieldsWithAnnotation(final Object object,
96              final Class<? extends Annotation> annnotationClass) {
97          if (object == null) {
98              throw new IllegalArgumentException("object must not be null.");
99          }
100         if (annnotationClass == null) {
101             throw new IllegalArgumentException("clazz must not be null.");
102         }
103         final List<Field> x = new ArrayList<Field>();
104         Class<?> c = object.getClass();
105         while (c != Object.class && c != null) {
106             for (final Field field : c.getDeclaredFields()) {
107                 // LOG.trace("Found field: "+field.getName());
108                 if (field.isAnnotationPresent(annnotationClass)) {
109                     x.add(field);
110                 }
111 
112             }
113 
114             c = c.getSuperclass();
115         }
116         return x.toArray(new Field[x.size()]);
117     }
118 
119     /**
120      * @param object
121      * @param factory
122      * @param field
123      * @throws SecurityException
124      */
125     public static void injectIntoField(final Object object, final Factory factory, final Field field)
126             throws SecurityException {
127 
128         final InjectForRequest ifr = field.getAnnotation(InjectForRequest.class);
129 
130         String name = ifr.value();
131         if (StringUtils.isBlank(name)) {
132             name = field.getName();
133         }
134         final Object injectionValue = factory.getObject(name, field.getType());
135         if (injectionValue == null) {
136             throw new InjectionException(factory.getClass().getName() + " does not know how to inject '"
137                     + field.getType().getName() + "' into the field '" + field.getName() + "' for an action.");
138         }
139         field.setAccessible(true); // make private fields accessible
140         if (LOG.isDebugEnabled()) {
141             LOG.debug("Injecting " + injectionValue.getClass().getName() + " into field " + field.getName() + " for "
142                     + object.getClass().getName());
143         }
144         try {
145             field.set(object, injectionValue);
146         } catch (final IllegalArgumentException e) {
147             throw new InjectionException("IllegalArgumentException injecting " + injectionValue + " into field "
148                     + field.getName(), e);
149         } catch (final IllegalAccessException e) {
150             throw new InjectionException("IllegalAccessException injecting " + injectionValue + " into field "
151                     + field.getName(), e);
152         }
153     }
154 
155     /**
156      * @param object
157      * @param factory
158      * @param method
159      * @throws SecurityException
160      */
161     public static void injectIntoMethod(final Object object, final Factory factory, final Method method)
162             throws SecurityException {
163         // LOG.trace("Found annotated field: "+field.getName());
164         final InjectForRequest ifr = method.getAnnotation(InjectForRequest.class);
165 
166         String name = ifr.value();
167         if (StringUtils.isBlank(name)) {
168             name = BeanUtils.findPropertyForMethod(method).getName();
169         }
170 
171         final Class<?> type = method.getParameterTypes()[0];
172         final Object injectionValue = factory.getObject(name, type);
173         if (injectionValue == null) {
174             throw new InjectionException(factory.getClass().getName() + " does not know how to inject '" + type.getName()
175                     + "' into the field '" + method.getName() + "' for an action.");
176         }
177 
178         // accessible
179         if (LOG.isDebugEnabled()) {
180             LOG.debug("Injecting " + injectionValue.getClass().getName() + " into field " + method.getName() + " for "
181                     + object.getClass().getName());
182         }
183         try {
184             method.invoke(object, injectionValue);
185         } catch (final IllegalArgumentException e) {
186             throw new InjectionException("IllegalArgumentException injecting " + injectionValue + " into method "
187                     + method.getName(), e);
188         } catch (final IllegalAccessException e) {
189             throw new InjectionException("IllegalAccessException injecting " + injectionValue + " into method "
190                     + method.getName(), e);
191         } catch (final InvocationTargetException e) {
192             throw new InjectionException("InvocationTargetException injecting " + injectionValue
193                     + " into method " + method.getName(), e);
194         }
195     }
196 
197 }