View Javadoc
1   package com.atlassian.plugin.spring.scanner.runtime.impl;
2   
3   import com.atlassian.plugin.spring.scanner.util.CommonConstants;
4   import org.osgi.framework.Bundle;
5   import org.osgi.framework.BundleContext;
6   import org.slf4j.Logger;
7   import org.slf4j.LoggerFactory;
8   import org.springframework.beans.factory.config.BeanDefinition;
9   import org.springframework.beans.factory.config.BeanDefinitionHolder;
10  import org.springframework.beans.factory.support.AbstractBeanDefinition;
11  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
12  import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
13  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
14  import org.springframework.util.ClassUtils;
15  
16  import java.beans.Introspector;
17  import java.util.HashMap;
18  import java.util.HashSet;
19  import java.util.LinkedHashSet;
20  import java.util.Map;
21  import java.util.Set;
22  import java.util.TreeSet;
23  
24  import static com.atlassian.plugin.spring.scanner.runtime.impl.util.AnnotationIndexReader.getIndexFilesForProfiles;
25  import static com.atlassian.plugin.spring.scanner.runtime.impl.util.AnnotationIndexReader.readAllIndexFilesForProduct;
26  import static com.atlassian.plugin.spring.scanner.runtime.impl.util.AnnotationIndexReader.splitProfiles;
27  import static com.atlassian.plugin.spring.scanner.runtime.impl.util.BeanDefinitionChecker.needToRegister;
28  
29  /**
30   * This class is responsible for reading the class index files and generating bean definitions from them. We assume all
31   * of the proper type checks/visibility checks have been done by the annotation processors. This means that if a class
32   * is listed in an index, it qualifies as a bean candidate.
33   */
34  public class ClassIndexBeanDefinitionScanner {
35      private static final Logger log = LoggerFactory.getLogger(ClassIndexBeanDefinitionScanner.class);
36  
37      private final BeanDefinitionRegistry registry;
38      private final String profileName;
39      /**
40       * A constant for {@link AbstractBeanDefinition#setAutowireMode(int)} used for beans created from the index,
41       * or null if the autowire mode should not be set.
42       */
43      private final Integer autowireDefault;
44      private final BundleContext bundleContext;
45  
46      public ClassIndexBeanDefinitionScanner(
47              final BeanDefinitionRegistry registry,
48              final String profileName,
49              final Integer autowireDefault,
50              final BundleContext bundleContext) {
51          this.registry = registry;
52          this.profileName = profileName;
53          this.autowireDefault = autowireDefault;
54          this.bundleContext = bundleContext;
55      }
56  
57      /**
58       * Gets the map of {@code beanName -> beanDefinition} and returns a set of bean definition holders
59       *
60       * @return a set of bean def holders
61       */
62      protected Set<BeanDefinitionHolder> doScan() {
63  
64          final Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
65          final Map<String, BeanDefinition> namesAndDefinitions = findCandidateComponents();
66  
67          for (final Map.Entry<String, BeanDefinition> nameAndDefinition : namesAndDefinitions.entrySet()) {
68  
69              if (needToRegister(nameAndDefinition.getKey(), nameAndDefinition.getValue(), registry)) {
70                  final BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(
71                          nameAndDefinition.getValue(), nameAndDefinition.getKey());
72                  beanDefinitions.add(definitionHolder);
73                  registerBeanDefinition(definitionHolder, registry);
74              }
75          }
76          return beanDefinitions;
77      }
78  
79      /**
80       * Reads the components from the index file(s) and generates a map of {@code beanName -> beanDefinitions} for them
81       *
82       * @return a Map of bean names to bean definitions
83       */
84      public Map<String, BeanDefinition> findCandidateComponents() {
85          final Map<String, BeanDefinition> candidates = new HashMap<String, BeanDefinition>();
86  
87          final Bundle bundle = bundleContext.getBundle();
88  
89          final Set<String> beanTypeAndNames = new TreeSet<String>();
90  
91          final String[] profileNames = splitProfiles(profileName);
92          for (final String fileToRead : getIndexFilesForProfiles(profileNames, CommonConstants.COMPONENT_KEY)) {
93              beanTypeAndNames.addAll(readAllIndexFilesForProduct(fileToRead, bundle, bundleContext));
94          }
95          final Set<String> primaryComponentTypeAndNames = findPrimaryComponentTypeAndNames(profileNames, bundle);
96  
97          for (final String beanTypeAndName : beanTypeAndNames) {
98              final String[] typeAndName = beanTypeAndName.split("#");
99  
100             final String beanClassName = typeAndName[0];
101             String beanName = "";
102 
103             if (typeAndName.length > 1) {
104                 beanName = typeAndName[1];
105             }
106 
107             if (beanName.isEmpty()) {
108                 beanName = Introspector.decapitalize(ClassUtils.getShortName(beanClassName));
109             }
110 
111             if (log.isDebugEnabled()) {
112                 log.debug(String.format("Found candidate bean '%s' from class '%s'", beanName, beanClassName));
113             }
114 
115             final BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(beanClassName);
116             if (null != autowireDefault) {
117                 beanDefinitionBuilder.setAutowireMode(autowireDefault);
118             }
119 
120             final BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
121             if (primaryComponentTypeAndNames.contains(beanTypeAndName)) {
122                 beanDefinition.setPrimary(true);
123             }
124             candidates.put(beanName, beanDefinition);
125         }
126 
127         return candidates;
128     }
129 
130     /**
131      * Reads the components from the primary-component index file(s)
132      *
133      * @return a Set containing all of the beanTypeAndNames
134      * @param profileNames
135      * @param bundle
136      */
137     private Set<String> findPrimaryComponentTypeAndNames(String[] profileNames, Bundle bundle) {
138         final Set<String> beanTypeAndNames = new HashSet<>();
139         for (final String fileToRead : getIndexFilesForProfiles(profileNames, CommonConstants.COMPONENT_PRIMARY_KEY)) {
140             beanTypeAndNames.addAll(readAllIndexFilesForProduct(fileToRead, bundle, bundleContext));
141         }
142         return beanTypeAndNames;
143     }
144 
145     /**
146      * copyPasta from spring-context:component-scan classes
147      */
148     protected void registerBeanDefinition(final BeanDefinitionHolder definitionHolder, final BeanDefinitionRegistry registry) {
149         BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
150     }
151 }