1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.context.annotation;
18
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.LinkedHashMap;
28 import java.util.LinkedHashSet;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Set;
33 import java.util.Stack;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 import org.springframework.beans.BeanUtils;
39 import org.springframework.beans.factory.Aware;
40 import org.springframework.beans.factory.BeanClassLoaderAware;
41 import org.springframework.beans.factory.BeanDefinitionStoreException;
42 import org.springframework.beans.factory.BeanFactory;
43 import org.springframework.beans.factory.BeanFactoryAware;
44 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
45 import org.springframework.beans.factory.config.BeanDefinition;
46 import org.springframework.beans.factory.config.BeanDefinitionHolder;
47 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
48 import org.springframework.beans.factory.parsing.Location;
49 import org.springframework.beans.factory.parsing.Problem;
50 import org.springframework.beans.factory.parsing.ProblemReporter;
51 import org.springframework.beans.factory.support.AbstractBeanDefinition;
52 import org.springframework.beans.factory.support.BeanDefinitionReader;
53 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
54 import org.springframework.beans.factory.support.BeanNameGenerator;
55 import org.springframework.context.EnvironmentAware;
56 import org.springframework.context.ResourceLoaderAware;
57 import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
58 import org.springframework.core.NestedIOException;
59 import org.springframework.core.annotation.AnnotationAttributes;
60 import org.springframework.core.annotation.AnnotationAwareOrderComparator;
61 import org.springframework.core.env.CompositePropertySource;
62 import org.springframework.core.env.ConfigurableEnvironment;
63 import org.springframework.core.env.Environment;
64 import org.springframework.core.env.MutablePropertySources;
65 import org.springframework.core.env.PropertySource;
66 import org.springframework.core.io.Resource;
67 import org.springframework.core.io.ResourceLoader;
68 import org.springframework.core.io.support.ResourcePropertySource;
69 import org.springframework.core.type.AnnotationMetadata;
70 import org.springframework.core.type.MethodMetadata;
71 import org.springframework.core.type.StandardAnnotationMetadata;
72 import org.springframework.core.type.classreading.MetadataReader;
73 import org.springframework.core.type.classreading.MetadataReaderFactory;
74 import org.springframework.core.type.filter.AssignableTypeFilter;
75 import org.springframework.util.Assert;
76 import org.springframework.util.LinkedMultiValueMap;
77 import org.springframework.util.MultiValueMap;
78 import org.springframework.util.StringUtils;
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 class ConfigurationClassParser {
101
102 private static final Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =
103 new Comparator<ConfigurationClassParser.DeferredImportSelectorHolder>() {
104 @Override
105 public int compare(DeferredImportSelectorHolder o1, DeferredImportSelectorHolder o2) {
106 return AnnotationAwareOrderComparator.INSTANCE.compare(o1.getImportSelector(), o2.getImportSelector());
107 }
108 };
109
110
111 private final Log logger = LogFactory.getLog(getClass());
112
113 private final MetadataReaderFactory metadataReaderFactory;
114
115 private final ProblemReporter problemReporter;
116
117 private final Environment environment;
118
119 private final ResourceLoader resourceLoader;
120
121 private final BeanDefinitionRegistry registry;
122
123 private final ComponentScanAnnotationParser componentScanParser;
124
125 private final ConditionEvaluator conditionEvaluator;
126
127 private final Map<ConfigurationClass, ConfigurationClass> configurationClasses =
128 new LinkedHashMap<ConfigurationClass, ConfigurationClass>();
129
130 private final Map<String, ConfigurationClass> knownSuperclasses = new HashMap<String, ConfigurationClass>();
131
132 private final List<String> propertySourceNames = new ArrayList<String>();
133
134 private final ImportStack importStack = new ImportStack();
135
136 private List<DeferredImportSelectorHolder> deferredImportSelectors;
137
138
139
140
141
142
143 public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
144 ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
145 BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
146
147 this.metadataReaderFactory = metadataReaderFactory;
148 this.problemReporter = problemReporter;
149 this.environment = environment;
150 this.resourceLoader = resourceLoader;
151 this.registry = registry;
152 this.componentScanParser = new ComponentScanAnnotationParser(
153 resourceLoader, environment, componentScanBeanNameGenerator, registry);
154 this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
155 }
156
157
158 public void parse(Set<BeanDefinitionHolder> configCandidates) {
159 this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
160
161 for (BeanDefinitionHolder holder : configCandidates) {
162 BeanDefinition bd = holder.getBeanDefinition();
163 try {
164 if (bd instanceof AnnotatedBeanDefinition) {
165 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
166 }
167 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
168 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
169 }
170 else {
171 parse(bd.getBeanClassName(), holder.getBeanName());
172 }
173 }
174 catch (BeanDefinitionStoreException ex) {
175 throw ex;
176 }
177 catch (Exception ex) {
178 throw new BeanDefinitionStoreException(
179 "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
180 }
181 }
182
183 processDeferredImportSelectors();
184 }
185
186 protected final void parse(String className, String beanName) throws IOException {
187 MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
188 processConfigurationClass(new ConfigurationClass(reader, beanName));
189 }
190
191 protected final void parse(Class<?> clazz, String beanName) throws IOException {
192 processConfigurationClass(new ConfigurationClass(clazz, beanName));
193 }
194
195 protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
196 processConfigurationClass(new ConfigurationClass(metadata, beanName));
197 }
198
199
200 protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
201 if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
202 return;
203 }
204
205 ConfigurationClass existingClass = this.configurationClasses.get(configClass);
206 if (existingClass != null) {
207 if (configClass.isImported()) {
208 if (existingClass.isImported()) {
209 existingClass.mergeImportedBy(configClass);
210 }
211
212 return;
213 }
214 else {
215
216
217 this.configurationClasses.remove(configClass);
218 for (Iterator<ConfigurationClass> it = this.knownSuperclasses.values().iterator(); it.hasNext(); ) {
219 if (configClass.equals(it.next())) {
220 it.remove();
221 }
222 }
223 }
224 }
225
226
227 SourceClass sourceClass = asSourceClass(configClass);
228 do {
229 sourceClass = doProcessConfigurationClass(configClass, sourceClass);
230 }
231 while (sourceClass != null);
232
233 this.configurationClasses.put(configClass, configClass);
234 }
235
236
237
238
239
240
241
242
243
244 protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
245
246 processMemberClasses(configClass, sourceClass);
247
248
249 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
250 sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) {
251 if (this.environment instanceof ConfigurableEnvironment) {
252 processPropertySource(propertySource);
253 }
254 else {
255 logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
256 "]. Reason: Environment must implement ConfigurableEnvironment");
257 }
258 }
259
260
261 AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
262 if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
263
264 Set<BeanDefinitionHolder> scannedBeanDefinitions =
265 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
266
267 for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
268 if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
269 parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
270 }
271 }
272 }
273
274
275 processImports(configClass, sourceClass, getImports(sourceClass), true);
276
277
278 if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
279 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
280 String[] resources = importResource.getStringArray("value");
281 Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
282 for (String resource : resources) {
283 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
284 configClass.addImportedResource(resolvedResource, readerClass);
285 }
286 }
287
288
289 Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
290 for (MethodMetadata methodMetadata : beanMethods) {
291 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
292 }
293
294
295 if (sourceClass.getMetadata().hasSuperClass()) {
296 String superclass = sourceClass.getMetadata().getSuperClassName();
297 if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
298 this.knownSuperclasses.put(superclass, configClass);
299
300 return sourceClass.getSuperClass();
301 }
302 }
303
304
305 return null;
306 }
307
308
309
310
311
312
313 private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
314 for (SourceClass memberClass : sourceClass.getMemberClasses()) {
315 if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
316 !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
317 processConfigurationClass(memberClass.asConfigClass(configClass));
318 }
319 }
320 }
321
322
323
324
325
326
327 private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
328 String name = propertySource.getString("name");
329 String[] locations = propertySource.getStringArray("value");
330 boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
331 Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
332 for (String location : locations) {
333 try {
334 String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
335 Resource resource = this.resourceLoader.getResource(resolvedLocation);
336 ResourcePropertySource rps = (StringUtils.hasText(name) ?
337 new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
338 addPropertySource(rps);
339 }
340 catch (IllegalArgumentException ex) {
341
342 if (!ignoreResourceNotFound) {
343 throw ex;
344 }
345 }
346 catch (FileNotFoundException ex) {
347
348 if (!ignoreResourceNotFound) {
349 throw ex;
350 }
351 }
352 }
353 }
354
355 private void addPropertySource(ResourcePropertySource propertySource) {
356 String name = propertySource.getName();
357 MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
358 if (propertySources.contains(name) && this.propertySourceNames.contains(name)) {
359
360 PropertySource<?> existing = propertySources.get(name);
361 if (existing instanceof CompositePropertySource) {
362 ((CompositePropertySource) existing).addFirstPropertySource(propertySource.withResourceName());
363 }
364 else {
365 if (existing instanceof ResourcePropertySource) {
366 existing = ((ResourcePropertySource) existing).withResourceName();
367 }
368 CompositePropertySource composite = new CompositePropertySource(name);
369 composite.addPropertySource(propertySource.withResourceName());
370 composite.addPropertySource(existing);
371 propertySources.replace(name, composite);
372 }
373 }
374 else {
375 if (this.propertySourceNames.isEmpty()) {
376 propertySources.addLast(propertySource);
377 }
378 else {
379 String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
380 propertySources.addBefore(firstProcessed, propertySource);
381 }
382 }
383 this.propertySourceNames.add(name);
384 }
385
386
387
388
389 private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
390 Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
391 Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
392 collectImports(sourceClass, imports, visited);
393 return imports;
394 }
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException {
410 if (visited.add(sourceClass)) {
411 for (SourceClass annotation : sourceClass.getAnnotations()) {
412 String annName = annotation.getMetadata().getClassName();
413 if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
414 collectImports(annotation, imports, visited);
415 }
416 }
417 imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
418 }
419 }
420
421 private void processDeferredImportSelectors() {
422 List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
423 this.deferredImportSelectors = null;
424 Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
425
426 for (DeferredImportSelectorHolder deferredImport : deferredImports) {
427 ConfigurationClass configClass = deferredImport.getConfigurationClass();
428 try {
429 String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
430 processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
431 }
432 catch (BeanDefinitionStoreException ex) {
433 throw ex;
434 }
435 catch (Exception ex) {
436 throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
437 configClass.getMetadata().getClassName() + "]", ex);
438 }
439 }
440 }
441
442 private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
443 Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
444
445 if (importCandidates.isEmpty()) {
446 return;
447 }
448
449 if (checkForCircularImports && this.importStack.contains(configClass)) {
450 this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
451 }
452 else {
453 this.importStack.push(configClass);
454 try {
455 for (SourceClass candidate : importCandidates) {
456 if (candidate.isAssignable(ImportSelector.class)) {
457
458 Class<?> candidateClass = candidate.loadClass();
459 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
460 invokeAwareMethods(selector);
461 if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
462 this.deferredImportSelectors.add(
463 new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
464 }
465 else {
466 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
467 Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
468 processImports(configClass, currentSourceClass, importSourceClasses, false);
469 }
470 }
471 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
472
473
474 Class<?> candidateClass = candidate.loadClass();
475 ImportBeanDefinitionRegistrar registrar =
476 BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
477 invokeAwareMethods(registrar);
478 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
479 }
480 else {
481
482
483 this.importStack.registerImport(
484 currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
485 processConfigurationClass(candidate.asConfigClass(configClass));
486 }
487 }
488 }
489 catch (BeanDefinitionStoreException ex) {
490 throw ex;
491 }
492 catch (Exception ex) {
493 throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
494 configClass.getMetadata().getClassName() + "]", ex);
495 }
496 finally {
497 this.importStack.pop();
498 }
499 }
500 }
501
502
503
504
505
506 private void invokeAwareMethods(Object importStrategyBean) {
507 if (importStrategyBean instanceof Aware) {
508 if (importStrategyBean instanceof EnvironmentAware) {
509 ((EnvironmentAware) importStrategyBean).setEnvironment(this.environment);
510 }
511 if (importStrategyBean instanceof ResourceLoaderAware) {
512 ((ResourceLoaderAware) importStrategyBean).setResourceLoader(this.resourceLoader);
513 }
514 if (importStrategyBean instanceof BeanClassLoaderAware) {
515 ClassLoader classLoader = (this.registry instanceof ConfigurableBeanFactory ?
516 ((ConfigurableBeanFactory) this.registry).getBeanClassLoader() :
517 this.resourceLoader.getClassLoader());
518 ((BeanClassLoaderAware) importStrategyBean).setBeanClassLoader(classLoader);
519 }
520 if (importStrategyBean instanceof BeanFactoryAware && this.registry instanceof BeanFactory) {
521 ((BeanFactoryAware) importStrategyBean).setBeanFactory((BeanFactory) this.registry);
522 }
523 }
524 }
525
526
527
528
529
530
531 public void validate() {
532 for (ConfigurationClass configClass : this.configurationClasses.keySet()) {
533 configClass.validate(this.problemReporter);
534 }
535 }
536
537 public Set<ConfigurationClass> getConfigurationClasses() {
538 return this.configurationClasses.keySet();
539 }
540
541
542 ImportRegistry getImportRegistry() {
543 return this.importStack;
544 }
545
546
547
548
549 public SourceClass asSourceClass(ConfigurationClass configurationClass) throws IOException {
550 AnnotationMetadata metadata = configurationClass.getMetadata();
551 if (metadata instanceof StandardAnnotationMetadata) {
552 return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
553 }
554 return asSourceClass(configurationClass.getMetadata().getClassName());
555 }
556
557
558
559
560 public SourceClass asSourceClass(Class<?> classType) throws IOException {
561 try {
562
563 classType.getAnnotations();
564 return new SourceClass(classType);
565 }
566 catch (Throwable ex) {
567
568 return asSourceClass(classType.getName());
569 }
570 }
571
572
573
574
575 public Collection<SourceClass> asSourceClasses(String[] classNames) throws IOException {
576 List<SourceClass> annotatedClasses = new ArrayList<SourceClass>();
577 for (String className : classNames) {
578 annotatedClasses.add(asSourceClass(className));
579 }
580 return annotatedClasses;
581 }
582
583
584
585
586 public SourceClass asSourceClass(String className) throws IOException {
587 if (className.startsWith("java")) {
588
589 try {
590 return new SourceClass(this.resourceLoader.getClassLoader().loadClass(className));
591 }
592 catch (ClassNotFoundException ex) {
593 throw new NestedIOException("Failed to load class [" + className + "]", ex);
594 }
595 }
596 return new SourceClass(this.metadataReaderFactory.getMetadataReader(className));
597 }
598
599
600 @SuppressWarnings("serial")
601 private static class ImportStack extends Stack<ConfigurationClass> implements ImportRegistry {
602
603 private final MultiValueMap<String, AnnotationMetadata> imports = new LinkedMultiValueMap<String, AnnotationMetadata>();
604
605 public void registerImport(AnnotationMetadata importingClass, String importedClass) {
606 this.imports.add(importedClass, importingClass);
607 }
608
609 @Override
610 public void removeImportingClassFor(String importedClass) {
611 for (List<AnnotationMetadata> list : this.imports.values()) {
612 for (Iterator<AnnotationMetadata> iterator = list.iterator(); iterator.hasNext();) {
613 if (iterator.next().getClassName().equals(importedClass)) {
614 iterator.remove();
615 }
616 }
617 }
618 }
619
620 @Override
621 public AnnotationMetadata getImportingClassFor(String importedClass) {
622 List<AnnotationMetadata> list = this.imports.get(importedClass);
623 return (list == null || list.isEmpty() ? null : list.get(list.size() - 1));
624 }
625
626
627
628
629
630
631 @Override
632 public boolean contains(Object elem) {
633 ConfigurationClass configClass = (ConfigurationClass) elem;
634 Comparator<ConfigurationClass> comparator = new Comparator<ConfigurationClass>() {
635 @Override
636 public int compare(ConfigurationClass first, ConfigurationClass second) {
637 return first.getMetadata().getClassName().equals(second.getMetadata().getClassName()) ? 0 : 1;
638 }
639 };
640 return (Collections.binarySearch(this, configClass, comparator) != -1);
641 }
642
643
644
645
646
647
648
649
650
651
652 @Override
653 public String toString() {
654 StringBuilder builder = new StringBuilder("ImportStack: [");
655 Iterator<ConfigurationClass> iterator = iterator();
656 while (iterator.hasNext()) {
657 builder.append(iterator.next().getSimpleName());
658 if (iterator.hasNext()) {
659 builder.append("->");
660 }
661 }
662 return builder.append(']').toString();
663 }
664 }
665
666
667 private static class DeferredImportSelectorHolder {
668
669 private final ConfigurationClass configurationClass;
670
671 private final DeferredImportSelector importSelector;
672
673 public DeferredImportSelectorHolder(ConfigurationClass configurationClass, DeferredImportSelector importSelector) {
674 this.configurationClass = configurationClass;
675 this.importSelector = importSelector;
676 }
677
678 public ConfigurationClass getConfigurationClass() {
679 return this.configurationClass;
680 }
681
682 public DeferredImportSelector getImportSelector() {
683 return this.importSelector;
684 }
685 }
686
687
688
689
690
691
692 private class SourceClass {
693
694 private final Object source;
695
696 private final AnnotationMetadata metadata;
697
698 public SourceClass(Object source) {
699 this.source = source;
700 if (source instanceof Class<?>) {
701 this.metadata = new StandardAnnotationMetadata((Class<?>) source, true);
702 }
703 else {
704 this.metadata = ((MetadataReader) source).getAnnotationMetadata();
705 }
706 }
707
708 public final AnnotationMetadata getMetadata() {
709 return this.metadata;
710 }
711
712 public Class<?> loadClass() throws ClassNotFoundException {
713 if (this.source instanceof Class<?>) {
714 return (Class<?>) this.source;
715 }
716 String className = ((MetadataReader) this.source).getClassMetadata().getClassName();
717 return resourceLoader.getClassLoader().loadClass(className);
718 }
719
720 public boolean isAssignable(Class<?> clazz) throws IOException {
721 if (this.source instanceof Class) {
722 return clazz.isAssignableFrom((Class<?>) this.source);
723 }
724 return new AssignableTypeFilter(clazz).match((MetadataReader) this.source, metadataReaderFactory);
725 }
726
727 public ConfigurationClass asConfigClass(ConfigurationClass importedBy) throws IOException {
728 if (this.source instanceof Class<?>) {
729 return new ConfigurationClass((Class<?>) this.source, importedBy);
730 }
731 return new ConfigurationClass((MetadataReader) this.source, importedBy);
732 }
733
734 public Collection<SourceClass> getMemberClasses() throws IOException {
735 Object sourceToProcess = this.source;
736 if (sourceToProcess instanceof Class<?>) {
737 Class<?> sourceClass = (Class<?>) sourceToProcess;
738 try {
739 Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();
740 List<SourceClass> members = new ArrayList<SourceClass>(declaredClasses.length);
741 for (Class<?> declaredClass : declaredClasses) {
742 members.add(asSourceClass(declaredClass));
743 }
744 return members;
745 }
746 catch (NoClassDefFoundError err) {
747
748
749 sourceToProcess = metadataReaderFactory.getMetadataReader(sourceClass.getName());
750 }
751 }
752
753
754 MetadataReader sourceReader = (MetadataReader) sourceToProcess;
755 String[] memberClassNames = sourceReader.getClassMetadata().getMemberClassNames();
756 List<SourceClass> members = new ArrayList<SourceClass>(memberClassNames.length);
757 for (String memberClassName : memberClassNames) {
758 members.add(asSourceClass(memberClassName));
759 }
760 return members;
761 }
762
763 public SourceClass getSuperClass() throws IOException {
764 if (this.source instanceof Class<?>) {
765 return asSourceClass(((Class<?>) this.source).getSuperclass());
766 }
767 return asSourceClass(((MetadataReader) this.source).getClassMetadata().getSuperClassName());
768 }
769
770 public Set<SourceClass> getAnnotations() throws IOException {
771 Set<SourceClass> result = new LinkedHashSet<SourceClass>();
772 for (String className : this.metadata.getAnnotationTypes()) {
773 try {
774 result.add(getRelated(className));
775 }
776 catch (Throwable ex) {
777
778
779 }
780 }
781 return result;
782 }
783
784 public Collection<SourceClass> getAnnotationAttributes(String annotationType, String attribute) throws IOException {
785 Map<String, Object> annotationAttributes = this.metadata.getAnnotationAttributes(annotationType, true);
786 if (annotationAttributes == null || !annotationAttributes.containsKey(attribute)) {
787 return Collections.emptySet();
788 }
789 String[] classNames = (String[]) annotationAttributes.get(attribute);
790 Set<SourceClass> result = new LinkedHashSet<SourceClass>();
791 for (String className : classNames) {
792 result.add(getRelated(className));
793 }
794 return result;
795 }
796
797 private SourceClass getRelated(String className) throws IOException {
798 if (this.source instanceof Class<?>) {
799 try {
800 Class<?> clazz = resourceLoader.getClassLoader().loadClass(className);
801 return asSourceClass(clazz);
802 }
803 catch (ClassNotFoundException ex) {
804
805 if (className.startsWith("java")) {
806 throw new NestedIOException("Failed to load class [" + className + "]", ex);
807 }
808 return new SourceClass(metadataReaderFactory.getMetadataReader(className));
809 }
810 }
811 return asSourceClass(className);
812 }
813
814 @Override
815 public boolean equals(Object other) {
816 return (this == other || (other instanceof SourceClass &&
817 this.metadata.getClassName().equals(((SourceClass) other).metadata.getClassName())));
818 }
819
820 @Override
821 public int hashCode() {
822 return this.metadata.getClassName().hashCode();
823 }
824
825 @Override
826 public String toString() {
827 return this.metadata.getClassName();
828 }
829 }
830
831
832
833
834
835 private static class CircularImportProblem extends Problem {
836
837 public CircularImportProblem(ConfigurationClass attemptedImport, Stack<ConfigurationClass> importStack, AnnotationMetadata metadata) {
838 super(String.format("A circular @Import has been detected: " +
839 "Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
840 "already present in the current import stack [%s]", importStack.peek().getSimpleName(),
841 attemptedImport.getSimpleName(), attemptedImport.getSimpleName(), importStack),
842 new Location(importStack.peek().getResource(), metadata));
843 }
844 }
845
846 }