TestApplicationBootstrapper.java
001 /*
002  * Copyright 2008-2014 the original author or authors.
003  *
004  * Licensed under the Apache License, Version 2.0 (the "License");
005  * you may not use this file except in compliance with the License.
006  * You may obtain a copy of the License at
007  *
008  *     http://www.apache.org/licenses/LICENSE-2.0
009  *
010  * Unless required by applicable law or agreed to in writing, software
011  * distributed under the License is distributed on an "AS IS" BASIS,
012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013  * See the License for the specific language governing permissions and
014  * limitations under the License.
015  */
016 package org.codehaus.griffon.runtime.core;
017 
018 import griffon.core.GriffonApplication;
019 import griffon.core.injection.Module;
020 import griffon.core.injection.TestingModule;
021 import griffon.core.test.TestCaseAware;
022 import griffon.inject.BindTo;
023 import griffon.util.AnnotationUtils;
024 import org.codehaus.griffon.runtime.core.injection.AbstractTestingModule;
025 import org.codehaus.griffon.runtime.core.injection.AnnotatedBindingBuilder;
026 import org.codehaus.griffon.runtime.core.injection.LinkedBindingBuilder;
027 import org.codehaus.griffon.runtime.core.injection.SingletonBindingBuilder;
028 import org.slf4j.Logger;
029 import org.slf4j.LoggerFactory;
030 
031 import javax.annotation.Nonnull;
032 import javax.inject.Named;
033 import javax.inject.Provider;
034 import javax.inject.Qualifier;
035 import javax.inject.Singleton;
036 import java.lang.annotation.Annotation;
037 import java.lang.reflect.Field;
038 import java.lang.reflect.Method;
039 import java.util.ArrayList;
040 import java.util.Collection;
041 import java.util.Collections;
042 import java.util.LinkedHashMap;
043 import java.util.List;
044 import java.util.Map;
045 
046 import static griffon.util.AnnotationUtils.named;
047 import static griffon.util.GriffonNameUtils.getPropertyName;
048 import static griffon.util.GriffonNameUtils.isBlank;
049 
050 /**
051  @author Andres Almiray
052  @since 2.0.0
053  */
054 public class TestApplicationBootstrapper extends DefaultApplicationBootstrapper implements TestCaseAware {
055     private static final Logger LOG = LoggerFactory.getLogger(TestApplicationBootstrapper.class);
056 
057     private static final String METHOD_MODULES = "modules";
058     private static final String METHOD_MODULE_OVERRIDES = "moduleOverrides";
059     private Object testCase;
060 
061     public TestApplicationBootstrapper(@Nonnull GriffonApplication application) {
062         super(application);
063     }
064 
065     public void setTestCase(@Nonnull Object testCase) {
066         this.testCase = testCase;
067     }
068 
069     @Nonnull
070     @Override
071     protected List<Module> loadModules() {
072         List<Module> modules = doCollectModulesFromMethod();
073         if (!modules.isEmpty()) {
074             return modules;
075         }
076         modules = super.loadModules();
077         doCollectOverridingModules(modules);
078         doCollectModulesFromInnerClasses(modules);
079         doCollectModulesFromFields(modules);
080         return modules;
081     }
082 
083     @Nonnull
084     @Override
085     protected Map<String, Module> sortModules(@Nonnull List<Module> moduleInstances) {
086         Map<String, Module> sortedModules = super.sortModules(moduleInstances);
087         // move all `TestingModules` at the end
088         // turns out the map is of type LinkedHashMap so insertion order is retained
089         Map<String, Module> testingModules = new LinkedHashMap<>();
090         for (Map.Entry<String, Module> e : sortedModules.entrySet()) {
091             if (e.getValue() instanceof TestingModule) {
092                 testingModules.put(e.getKey(), e.getValue());
093             }
094         }
095         for (String key : testingModules.keySet()) {
096             sortedModules.remove(key);
097         }
098         sortedModules.putAll(testingModules);
099 
100         LOG.debug("computed {} order is {}""Module", sortedModules.keySet());
101 
102         return sortedModules;
103     }
104 
105     @SuppressWarnings("unchecked")
106     private List<Module> doCollectModulesFromMethod() {
107         Method method = null;
108         try {
109             method = testCase.getClass().getDeclaredMethod(METHOD_MODULES);
110             method.setAccessible(true);
111         catch (NoSuchMethodException e) {
112             return Collections.emptyList();
113         }
114 
115         try {
116             return (List<Module>method.invoke(testCase);
117         catch (Exception e) {
118             throw new IllegalArgumentException("An error occurred while initializing modules from " + testCase.getClass().getName() "." + METHOD_MODULES, e);
119         }
120     }
121 
122     @SuppressWarnings("unchecked")
123     private void doCollectOverridingModules(final @Nonnull Collection<Module> modules) {
124         Method method = null;
125         try {
126             method = testCase.getClass().getDeclaredMethod(METHOD_MODULE_OVERRIDES);
127             method.setAccessible(true);
128         catch (NoSuchMethodException e) {
129             return;
130         }
131 
132         try {
133             List<Module> overrides = (List<Module>method.invoke(testCase);
134             modules.addAll(overrides);
135         catch (Exception e) {
136             throw new IllegalArgumentException("An error occurred while initializing modules from " + testCase.getClass().getName() "." + METHOD_MODULE_OVERRIDES, e);
137         }
138     }
139 
140     private void doCollectModulesFromInnerClasses(final @Nonnull Collection<Module> modules) {
141         modules.add(new InnerClassesModule());
142     }
143 
144     private void doCollectModulesFromFields(final @Nonnull Collection<Module> modules) {
145         modules.add(new FieldsModule());
146     }
147 
148     @Nonnull
149     protected List<Annotation> harvestQualifiers(@Nonnull Class<?> clazz) {
150         List<Annotation> list = new ArrayList<>();
151         Annotation[] annotations = clazz.getAnnotations();
152         for (Annotation annotation : annotations) {
153             if (AnnotationUtils.isAnnotatedWith(annotation, Qualifier.class)) {
154                 if (BindTo.class.isAssignableFrom(annotation.getClass())) {
155                     continue;
156                 }
157 
158                 // special case for @Named
159                 if (Named.class.isAssignableFrom(annotation.getClass())) {
160                     Named named = (Namedannotation;
161                     if (isBlank(named.value())) {
162                         list.add(named(getPropertyName(clazz)));
163                         continue;
164                     }
165                 }
166                 list.add(annotation);
167             }
168         }
169         return list;
170     }
171 
172     @Nonnull
173     protected List<Annotation> harvestQualifiers(@Nonnull Field field) {
174         List<Annotation> list = new ArrayList<>();
175         Annotation[] annotations = field.getAnnotations();
176         for (Annotation annotation : annotations) {
177             if (AnnotationUtils.isAnnotatedWith(annotation, Qualifier.class)) {
178                 if (BindTo.class.isAssignableFrom(annotation.getClass())) {
179                     continue;
180                 }
181 
182                 // special case for @Named
183                 if (Named.class.isAssignableFrom(annotation.getClass())) {
184                     Named named = (Namedannotation;
185                     if (isBlank(named.value())) {
186                         list.add(named(getPropertyName(field.getName())));
187                         continue;
188                     }
189                 }
190                 list.add(annotation);
191             }
192         }
193         return list;
194     }
195 
196     private class InnerClassesModule extends AbstractTestingModule {
197         @Override
198         @SuppressWarnings("unchecked")
199         protected void doConfigure() {
200             for (Class<?> clazz : testCase.getClass().getDeclaredClasses()) {
201                 BindTo bindTo = clazz.getAnnotation(BindTo.class);
202                 if (bindTo == nullcontinue;
203                 List<Annotation> qualifiers = harvestQualifiers(clazz);
204                 Annotation classifier = qualifiers.isEmpty() null : qualifiers.get(0);
205                 boolean isSingleton = clazz.getAnnotation(Singleton.class!= null;
206 
207                 AnnotatedBindingBuilder<?> abuilder = bind(bindTo.value());
208                 if (classifier != null) {
209                     LinkedBindingBuilder<?> lbuilder = abuilder.withClassifier(classifier);
210                     if (Provider.class.isAssignableFrom(clazz)) {
211                         SingletonBindingBuilder<?> sbuilder = lbuilder.toProvider((Classclazz);
212                         if (isSingletonsbuilder.asSingleton();
213                     else {
214                         SingletonBindingBuilder<?> sbuilder = lbuilder.to((Classclazz);
215                         if (isSingletonsbuilder.asSingleton();
216                     }
217                 else {
218                     if (Provider.class.isAssignableFrom(clazz)) {
219                         SingletonBindingBuilder<?> sbuilder = abuilder.toProvider((Classclazz);
220                         if (isSingletonsbuilder.asSingleton();
221                     else {
222                         SingletonBindingBuilder<?> sbuilder = abuilder.to((Classclazz);
223                         if (isSingletonsbuilder.asSingleton();
224                     }
225                 }
226             }
227         }
228     }
229 
230     private class FieldsModule extends AbstractTestingModule {
231         @Override
232         @SuppressWarnings("unchecked")
233         protected void doConfigure() {
234             for (Field field : testCase.getClass().getDeclaredFields()) {
235                 BindTo bindTo = field.getAnnotation(BindTo.class);
236                 if (bindTo == nullcontinue;
237                 List<Annotation> qualifiers = harvestQualifiers(field);
238                 Annotation classifier = qualifiers.isEmpty() null : qualifiers.get(0);
239                 boolean isSingleton = field.getAnnotation(Singleton.class!= null;
240 
241                 field.setAccessible(true);
242                 Object instance = null;
243                 try {
244                     instance = field.get(testCase);
245                 catch (IllegalAccessException e) {
246                     throw new IllegalArgumentException(e);
247                 }
248 
249                 if (instance != null) {
250                     AnnotatedBindingBuilder<Object> abuilder = (AnnotatedBindingBuilder<Object>bind(bindTo.value());
251                     if (classifier != null) {
252                         if (Provider.class.isAssignableFrom(instance.getClass())) {
253                             SingletonBindingBuilder<?> sbuilder = abuilder
254                                 .withClassifier(classifier)
255                                 .toProvider((Provider<Object>instance);
256                             if (isSingletonsbuilder.asSingleton();
257                         else {
258                             abuilder.withClassifier(classifier).toInstance(instance);
259                         }
260                     else if (Provider.class.isAssignableFrom(instance.getClass())) {
261                         SingletonBindingBuilder<?> sbuilder = abuilder.toProvider((Provider<Object>instance);
262                         if (isSingletonsbuilder.asSingleton();
263                     else {
264                         abuilder.toInstance(instance);
265                     }
266                 else {
267                     AnnotatedBindingBuilder<?> abuilder = bind(bindTo.value());
268                     if (classifier != null) {
269                         LinkedBindingBuilder<?> lbuilder = abuilder.withClassifier(classifier);
270                         if (Provider.class.isAssignableFrom(field.getType())) {
271                             SingletonBindingBuilder<?> sbuilder = lbuilder.toProvider((Classfield.getType());
272                             if (isSingletonsbuilder.asSingleton();
273                         else {
274                             SingletonBindingBuilder<?> sbuilder = lbuilder.to((Classfield.getType());
275                             if (isSingletonsbuilder.asSingleton();
276                         }
277                     else {
278                         if (Provider.class.isAssignableFrom(field.getType())) {
279                             SingletonBindingBuilder<?> sbuilder = abuilder.toProvider((Classfield.getType());
280                             if (isSingletonsbuilder.asSingleton();
281                         else {
282                             SingletonBindingBuilder<?> sbuilder = abuilder.to((Classfield.getType());
283                             if (isSingletonsbuilder.asSingleton();
284                         }
285                     }
286                 }
287             }
288         }
289     }
290 }