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 == null) {
109             return Collections.emptyList();
110         }
111 
112         if (testCase instanceof TestModuleAware) {
113             return ((TestModuleAwaretestCase).modules();
114         else {
115             Method method = null;
116             try {
117                 method = testCase.getClass().getDeclaredMethod(METHOD_MODULES);
118                 method.setAccessible(true);
119             catch (NoSuchMethodException e) {
120                 return Collections.emptyList();
121             }
122 
123             try {
124                 return (List<Module>method.invoke(testCase);
125             catch (Exception e) {
126                 throw new IllegalArgumentException("An error occurred while initializing modules from " + testCase.getClass().getName() "." + METHOD_MODULES, e);
127             }
128         }
129     }
130 
131     @SuppressWarnings("unchecked")
132     private void doCollectOverridingModules(final @Nonnull Collection<Module> modules) {
133         if (testCase == null) {
134             return;
135         }
136 
137         if (testCase instanceof TestModuleAware) {
138             List<Module> overrides = ((TestModuleAwaretestCase).moduleOverrides();
139             modules.addAll(overrides);
140         else {
141             Method method = null;
142             try {
143                 method = testCase.getClass().getDeclaredMethod(METHOD_MODULE_OVERRIDES);
144                 method.setAccessible(true);
145             catch (NoSuchMethodException e) {
146                 return;
147             }
148 
149             try {
150                 List<Module> overrides = (List<Module>method.invoke(testCase);
151                 modules.addAll(overrides);
152             catch (Exception e) {
153                 throw new IllegalArgumentException("An error occurred while initializing modules from " + testCase.getClass().getName() "." + METHOD_MODULE_OVERRIDES, e);
154             }
155         }
156     }
157 
158     private void doCollectModulesFromInnerClasses(final @Nonnull Collection<Module> modules) {
159         if (testCase != null) {
160             modules.add(new InnerClassesModule());
161         }
162     }
163 
164     private void doCollectModulesFromFields(final @Nonnull Collection<Module> modules) {
165         if (testCase != null) {
166             modules.add(new FieldsModule());
167         }
168     }
169 
170     @Nonnull
171     protected List<Annotation> harvestQualifiers(@Nonnull Class<?> clazz) {
172         List<Annotation> list = new ArrayList<>();
173         Annotation[] annotations = clazz.getAnnotations();
174         for (Annotation annotation : annotations) {
175             if (AnnotationUtils.isAnnotatedWith(annotation, Qualifier.class)) {
176                 if (BindTo.class.isAssignableFrom(annotation.getClass())) {
177                     continue;
178                 }
179 
180                 // special case for @Named
181                 if (Named.class.isAssignableFrom(annotation.getClass())) {
182                     Named named = (Namedannotation;
183                     if (isBlank(named.value())) {
184                         list.add(named(getPropertyName(clazz)));
185                         continue;
186                     }
187                 }
188                 list.add(annotation);
189             }
190         }
191         return list;
192     }
193 
194     @Nonnull
195     protected List<Annotation> harvestQualifiers(@Nonnull Field field) {
196         List<Annotation> list = new ArrayList<>();
197         Annotation[] annotations = field.getAnnotations();
198         for (Annotation annotation : annotations) {
199             if (AnnotationUtils.isAnnotatedWith(annotation, Qualifier.class)) {
200                 if (BindTo.class.isAssignableFrom(annotation.getClass())) {
201                     continue;
202                 }
203 
204                 // special case for @Named
205                 if (Named.class.isAssignableFrom(annotation.getClass())) {
206                     Named named = (Namedannotation;
207                     if (isBlank(named.value())) {
208                         list.add(named(getPropertyName(field.getName())));
209                         continue;
210                     }
211                 }
212                 list.add(annotation);
213             }
214         }
215         return list;
216     }
217 
218     private class InnerClassesModule extends AbstractTestingModule {
219         @Override
220         @SuppressWarnings("unchecked")
221         protected void doConfigure() {
222             for (Class<?> clazz : testCase.getClass().getDeclaredClasses()) {
223                 BindTo bindTo = clazz.getAnnotation(BindTo.class);
224                 if (bindTo == nullcontinue;
225                 List<Annotation> qualifiers = harvestQualifiers(clazz);
226                 Annotation classifier = qualifiers.isEmpty() null : qualifiers.get(0);
227                 boolean isSingleton = clazz.getAnnotation(Singleton.class!= null;
228 
229                 AnnotatedBindingBuilder<?> abuilder = bind(bindTo.value());
230                 if (classifier != null) {
231                     LinkedBindingBuilder<?> lbuilder = abuilder.withClassifier(classifier);
232                     if (Provider.class.isAssignableFrom(clazz)) {
233                         SingletonBindingBuilder<?> sbuilder = lbuilder.toProvider((Classclazz);
234                         if (isSingletonsbuilder.asSingleton();
235                     else {
236                         SingletonBindingBuilder<?> sbuilder = lbuilder.to((Classclazz);
237                         if (isSingletonsbuilder.asSingleton();
238                     }
239                 else {
240                     if (Provider.class.isAssignableFrom(clazz)) {
241                         SingletonBindingBuilder<?> sbuilder = abuilder.toProvider((Classclazz);
242                         if (isSingletonsbuilder.asSingleton();
243                     else {
244                         SingletonBindingBuilder<?> sbuilder = abuilder.to((Classclazz);
245                         if (isSingletonsbuilder.asSingleton();
246                     }
247                 }
248             }
249         }
250     }
251 
252     private class FieldsModule extends AbstractTestingModule {
253         @Override
254         @SuppressWarnings("unchecked")
255         protected void doConfigure() {
256             for (Field field : testCase.getClass().getDeclaredFields()) {
257                 BindTo bindTo = field.getAnnotation(BindTo.class);
258                 if (bindTo == nullcontinue;
259                 List<Annotation> qualifiers = harvestQualifiers(field);
260                 Annotation classifier = qualifiers.isEmpty() null : qualifiers.get(0);
261                 boolean isSingleton = field.getAnnotation(Singleton.class!= null;
262 
263                 field.setAccessible(true);
264                 Object instance = null;
265                 try {
266                     instance = field.get(testCase);
267                 catch (IllegalAccessException e) {
268                     throw new IllegalArgumentException(e);
269                 }
270 
271                 if (instance != null) {
272                     AnnotatedBindingBuilder<Object> abuilder = (AnnotatedBindingBuilder<Object>bind(bindTo.value());
273                     if (classifier != null) {
274                         if (Provider.class.isAssignableFrom(instance.getClass())) {
275                             SingletonBindingBuilder<?> sbuilder = abuilder
276                                 .withClassifier(classifier)
277                                 .toProvider((Provider<Object>instance);
278                             if (isSingletonsbuilder.asSingleton();
279                         else {
280                             abuilder.withClassifier(classifier).toInstance(instance);
281                         }
282                     else if (Provider.class.isAssignableFrom(instance.getClass())) {
283                         SingletonBindingBuilder<?> sbuilder = abuilder.toProvider((Provider<Object>instance);
284                         if (isSingletonsbuilder.asSingleton();
285                     else {
286                         abuilder.toInstance(instance);
287                     }
288                 else {
289                     AnnotatedBindingBuilder<?> abuilder = bind(bindTo.value());
290                     if (classifier != null) {
291                         LinkedBindingBuilder<?> lbuilder = abuilder.withClassifier(classifier);
292                         if (Provider.class.isAssignableFrom(field.getType())) {
293                             SingletonBindingBuilder<?> sbuilder = lbuilder.toProvider((Classfield.getType());
294                             if (isSingletonsbuilder.asSingleton();
295                         else {
296                             SingletonBindingBuilder<?> sbuilder = lbuilder.to((Classfield.getType());
297                             if (isSingletonsbuilder.asSingleton();
298                         }
299                     else {
300                         if (Provider.class.isAssignableFrom(field.getType())) {
301                             SingletonBindingBuilder<?> sbuilder = abuilder.toProvider((Classfield.getType());
302                             if (isSingletonsbuilder.asSingleton();
303                         else {
304                             SingletonBindingBuilder<?> sbuilder = abuilder.to((Classfield.getType());
305                             if (isSingletonsbuilder.asSingleton();
306                         }
307                     }
308                 }
309             }
310         }
311     }
312 }