1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.jitr;
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Field;
21 import java.lang.reflect.Modifier;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.LinkedList;
25
26 import javax.servlet.ServletContext;
27
28 import org.apache.log4j.Logger;
29 import org.jitr.annotation.BaseUri;
30 import org.jitr.annotation.JitrConfiguration;
31 import org.jitr.annotation.Port;
32 import org.jitr.core.ConfigurationModel;
33 import org.jitr.core.Container;
34 import org.jitr.core.JitrException;
35 import org.jitr.core.OperationalMode;
36 import org.junit.internal.runners.ClassRoadie;
37 import org.junit.internal.runners.InitializationError;
38 import org.junit.internal.runners.JUnit4ClassRunner;
39 import org.junit.runner.notification.RunNotifier;
40 import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
41 import org.springframework.context.ApplicationContext;
42 import org.springframework.context.support.ClassPathXmlApplicationContext;
43 import org.springframework.core.annotation.AnnotationUtils;
44 import org.springframework.test.context.ContextConfiguration;
45 import org.springframework.test.context.ContextLoader;
46 import org.springframework.web.context.WebApplicationContext;
47 import org.springframework.web.context.support.WebApplicationContextUtils;
48
49
50
51
52
53
54
55 @JitrConfiguration
56 public final class Jitr extends JUnit4ClassRunner {
57
58 private static final Logger LOG = Logger.getLogger(Jitr.class);
59
60 private ConfigurationModel config;
61
62 private Container<?> container;
63
64 private AutowireCapableBeanFactory autowireCapableBeanFactory;
65
66 public Jitr(final Class<?> clazz) throws InitializationError {
67 super(clazz);
68 }
69
70 @Override
71 public void run(final RunNotifier notifier) {
72
73 if (LOG.isInfoEnabled()) {
74 LOG.info("Jitr loaded");
75 }
76
77
78 JitrConfiguration annot =
79 AnnotationUtils.findAnnotation(getTestClass().getJavaClass(),
80 JitrConfiguration.class);
81
82 if (LOG.isInfoEnabled() && annot == null) {
83 LOG.info("No @JitrConfiguration found on test class");
84 }
85
86 config = JitrConfigurationFactory.createConfigurationFromAnnotation(annot);
87
88 if (config == null) {
89 throw new JitrException("Configuration could not be created.");
90 }
91
92 final ServletContext servletContext = initAndStartContainerIfNeeded();
93
94
95 setSpringAutowireCapableBeanFactory(servletContext);
96
97
98 final ClassRoadie roadie =
99 new ClassRoadie(notifier, getTestClass(), getDescription(), new Runnable() {
100 public void run() {
101 runMethods(notifier);
102 }
103 });
104
105 if (LOG.isInfoEnabled()) {
106 LOG.info("Running tests");
107 }
108
109
110 roadie.runProtected();
111
112 if (config.getMode().isContainerEnabled()) {
113
114 if (LOG.isInfoEnabled()) {
115 LOG.info("Stopping container: " + container.getName());
116 }
117
118 container.stop();
119 }
120 }
121
122 @Override
123 protected Object createTest() throws Exception {
124 Object testInstance = super.createTest();
125
126
127 if (autowireCapableBeanFactory != null) {
128 autowireCapableBeanFactory.autowireBean(testInstance);
129 }
130
131
132 final Field portField =
133 JitrUtils.getMostSpecificAnnotatedDeclaredField(testInstance.getClass(), Port.class);
134
135 if (portField != null) {
136
137 validateModifiersAndSetAccessible(portField, "Port");
138 portField.setInt(testInstance, config.getPort());
139 }
140
141
142 final Field baseUriField =
143 JitrUtils.getMostSpecificAnnotatedDeclaredField(testInstance.getClass(),
144 BaseUri.class);
145
146 if (baseUriField != null) {
147
148 validateModifiersAndSetAccessible(baseUriField, "BaseUri");
149 baseUriField.set(testInstance, config.getBaseUri());
150 }
151
152 return testInstance;
153 }
154
155 private ServletContext initAndStartContainerIfNeeded() {
156
157
158 if (!config.getMode().isContainerEnabled()) {
159 return null;
160 }
161
162 final Class<?> containerClass = config.getContainerClass();
163
164
165 try {
166 Constructor<?> containerConstructor = containerClass.getConstructor();
167 container = (Container<?>) containerConstructor.newInstance();
168
169 } catch (final Exception e) {
170 throw new JitrException("Could not instantiate Container implementation class: "
171 + containerClass.toString(), e);
172 }
173
174 if (LOG.isInfoEnabled()) {
175 LOG.info("Initializing container: " + container.getName());
176 }
177
178 ServletContext servletContext =
179 container.initialize(config, getTestClass().getJavaClass().getAnnotations());
180
181 if (LOG.isInfoEnabled()) {
182 LOG.info("Starting container: " + container.getName());
183 }
184
185 container.start();
186
187 return servletContext;
188 }
189
190 private void setSpringAutowireCapableBeanFactory(final ServletContext servletContext) {
191
192
193 if (config.getMode() == OperationalMode.INTERNAL) {
194
195
196 WebApplicationContext webApplicationContext =
197 org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext();
198
199
200 if (webApplicationContext == null && servletContext == null) {
201 throw new JitrException(
202 "Servlet context was null and no Spring web application context could"
203 + " be found in the current Thread.");
204 }
205
206 if (webApplicationContext == null) {
207 webApplicationContext =
208 WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
209 }
210
211
212 if (webApplicationContext == null) {
213 LOG.warn("No Spring web application context was found and Jitr is not configured "
214 + "to use an external Spring application context configuration.");
215 return;
216 }
217
218 autowireCapableBeanFactory = webApplicationContext.getAutowireCapableBeanFactory();
219
220 return;
221 }
222
223
224 ContextConfiguration contextConfiguration =
225 AnnotationUtils.findAnnotation(getTestClass().getJavaClass(),
226 ContextConfiguration.class);
227
228
229 if (contextConfiguration == null) {
230 throw new JitrException(
231 "Jitr is configured to use an external Spring application context but none"
232 + " has been configured. Use @ContextConfiguration on your test class"
233 + " to define what Spring application context configurations should"
234 + " be loaded.");
235 }
236
237
238
239
240
241 Class<? extends ContextLoader> contextLoaderClass = contextConfiguration.loader();
242 String[] locations = contextConfiguration.locations();
243 ApplicationContext applicationContext;
244
245
246 if (contextLoaderClass != null && contextLoaderClass != ContextLoader.class) {
247 try {
248 Constructor<? extends ContextLoader> constructor =
249 contextLoaderClass.getConstructor();
250 ContextLoader contextLoader = constructor.newInstance();
251 String[] processedLocations =
252 contextLoader.processLocations(getTestClass().getJavaClass(), locations);
253 applicationContext = contextLoader.loadContext(processedLocations);
254
255 } catch (final Exception e) {
256 throw new JitrException("Error loading Spring application contexts.", e);
257 }
258 } else {
259
260 applicationContext = new ClassPathXmlApplicationContext(locations);
261 }
262
263 autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
264 }
265
266 private void validateModifiersAndSetAccessible(final Field field, final String annotationName) {
267
268 final int modifiers = field.getModifiers();
269 final Collection<String> problemModifiers = new LinkedList<String>();
270
271 if (Modifier.isStatic(modifiers)) {
272 problemModifiers.add("static");
273 }
274
275 if (Modifier.isFinal(modifiers)) {
276 problemModifiers.add("final");
277 }
278
279 if (!problemModifiers.isEmpty()) {
280
281 final Iterator<String> iterator = problemModifiers.iterator();
282 final StringBuilder stringBuilder = new StringBuilder();
283
284 while (iterator.hasNext()) {
285 stringBuilder.append(iterator.next());
286
287 if (iterator.hasNext()) {
288 stringBuilder.append(", ");
289 }
290 }
291
292 throw new JitrException("Field annotated with @" + annotationName + " cannot be: "
293 + stringBuilder.toString());
294 }
295
296 field.setAccessible(true);
297 }
298 }