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