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