View Javadoc
1   /*
2    * Copyright 2016 Function1. 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  package tools.gsf.config.inject;
17  
18  import org.apache.commons.lang3.StringUtils;
19  import org.slf4j.Logger;
20  import org.slf4j.LoggerFactory;
21  import org.springframework.beans.BeanUtils;
22  import tools.gsf.config.Factory;
23  
24  import java.lang.reflect.Field;
25  import java.lang.reflect.InvocationTargetException;
26  import java.lang.reflect.Method;
27  
28  /**
29   * @author Tony Field
30   * @since 2016-07-21
31   */
32  public final class InjectForRequestInjector implements Injector {
33      private static final Logger LOG = LoggerFactory.getLogger("tools.gsf.config.inject.InjectForRequestInjector");
34  
35      private final Factory factory;
36  
37      public InjectForRequestInjector(Factory factory) {
38          this.factory = factory;
39      }
40  
41      /**
42       * Inject ICS runtime objects into the object. Objects flagged with the
43       * {@link InjectForRequest} annotation will be populated by this method by
44       * retrieving the value from the {@link Factory#getObject(String, Class)}
45       * method.
46       *
47       * @param dependent the object to inject into
48       */
49      public void inject(final Object dependent) {
50          if (dependent == null) {
51              throw new IllegalArgumentException("dependent cannot be null.");
52          }
53  
54          Class<?> c = dependent.getClass();
55          // first to all annotated public setter methods.
56          for (final Method method : c.getMethods()) {
57              if (method.isAnnotationPresent(InjectForRequest.class)) {
58                  injectIntoMethod(dependent, factory, method);
59              }
60          }
61          // and then all annotated fields.
62          while (c != Object.class && c != null) {
63              for (final Field field : c.getDeclaredFields()) {
64                  if (field.isAnnotationPresent(InjectForRequest.class)) {
65                      injectIntoField(dependent, factory, field);
66                  }
67              }
68              c = c.getSuperclass();
69          }
70      }
71  
72      /**
73       * @param object  the object to inject into
74       * @param factory the factory that created the objects that need to be
75       *                injected.
76       * @param field   field to inject into
77       * @throws SecurityException security exception injecting values into field
78       */
79      private static void injectIntoField(final Object object, final Factory factory, final Field field) throws SecurityException {
80  
81          final InjectForRequest ifr = field.getAnnotation(InjectForRequest.class);
82  
83          String name = ifr.value();
84          if (StringUtils.isBlank(name)) {
85              name = field.getName();
86          }
87          final Object injectionValue = factory.getObject(name, field.getType());
88          if (injectionValue == null) {
89              throw new InjectionException(factory.getClass().getName() + " does not know how to inject '" + field.getType().getName() + "' into the field '" + field.getName() + "' of object " + object.hashCode() + " (" + object.getClass().getName() + ").");
90          }
91          field.setAccessible(true); // make private fields accessible
92          LOG.debug("Injecting {} into field {} for {}", injectionValue.getClass().getName(), field.getName(), object.getClass().getName());
93          try {
94              field.set(object, injectionValue);
95          } catch (final IllegalArgumentException e) {
96              throw new InjectionException("IllegalArgumentException injecting " + injectionValue + " into field " + field.getName(), e);
97          } catch (final IllegalAccessException e) {
98              throw new InjectionException("IllegalAccessException injecting " + injectionValue + " into field " + field.getName(), e);
99          }
100     }
101 
102     /**
103      * @param object  the object to inject into
104      * @param factory the factory that created the objects that need to be
105      *                injected.
106      * @param method  the method to inject into
107      * @throws SecurityException security exception when injecting value into field
108      */
109     private static void injectIntoMethod(final Object object, final Factory factory, final Method method) throws SecurityException {
110         final InjectForRequest ifr = method.getAnnotation(InjectForRequest.class);
111 
112         String name = ifr.value();
113         if (StringUtils.isBlank(name)) {
114             name = BeanUtils.findPropertyForMethod(method).getName();
115         }
116 
117         final Class<?> type = method.getParameterTypes()[0];
118         final Object injectionValue = factory.getObject(name, type);
119         if (injectionValue == null) {
120             throw new InjectionException(factory.getClass().getName() + " does not know how to inject '" + type.getName() + "' into the method '" + method.getName() + "' of object " + object.hashCode() + " (" + object.getClass().getName() + ").");
121         }
122 
123         // accessible
124         LOG.debug("Injecting {} into field {} for {}", injectionValue.getClass().getName(), method.getName(), object.getClass().getName());
125         try {
126             method.invoke(object, injectionValue);
127         } catch (final IllegalArgumentException e) {
128             throw new InjectionException("IllegalArgumentException injecting " + injectionValue + " into method " + method.getName(), e);
129         } catch (final IllegalAccessException e) {
130             throw new InjectionException("IllegalAccessException injecting " + injectionValue + " into method " + method.getName(), e);
131         } catch (final InvocationTargetException e) {
132             throw new InjectionException("InvocationTargetException injecting " + injectionValue + " into method " + method.getName(), e);
133         }
134     }
135 }