View Javadoc
1   package com.atlassian.plugin.spring.scanner.runtime.impl;
2   
3   import com.atlassian.plugin.osgi.factory.OsgiPlugin;
4   import com.atlassian.plugin.spring.scanner.util.CommonConstants;
5   import org.eclipse.gemini.blueprint.service.importer.support.OsgiServiceProxyFactoryBean;
6   import org.osgi.framework.Bundle;
7   import org.osgi.framework.BundleContext;
8   import org.slf4j.Logger;
9   import org.slf4j.LoggerFactory;
10  import org.springframework.beans.BeansException;
11  import org.springframework.beans.factory.config.BeanDefinition;
12  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
13  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
14  import org.springframework.beans.factory.support.AbstractBeanDefinition;
15  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
16  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
17  
18  import java.beans.Introspector;
19  import java.net.URL;
20  import java.util.Properties;
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.readPropertiesFile;
27  import static com.atlassian.plugin.spring.scanner.runtime.impl.util.AnnotationIndexReader.splitProfiles;
28  
29  /**
30   * This class is run after all of the bean definitions have been gathered for the current bundle. It looks for any of
31   * the *Import annotations and registers the proper OSGi imports. This is a BeanFactoryPostProcessor because it needs to
32   * run before the beans are created so that the services are available when spring wires up the beans.
33   */
34  @SuppressWarnings("UnusedDeclaration")
35  public class ComponentImportBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
36      private final BundleContext bundleContext;
37      private String profileName;
38  
39      private static final Logger log = LoggerFactory.getLogger(ComponentImportBeanFactoryPostProcessor.class);
40  
41      public ComponentImportBeanFactoryPostProcessor(final BundleContext bundleContext) {
42          this.bundleContext = bundleContext;
43          this.profileName = null;
44      }
45  
46      /**
47       * Reads the component import index file(s) and registers the bean wrappers that represent OSGi import services
48       *
49       * @param beanFactory the bean factory
50       * @throws BeansException if bad bean business happens
51       */
52      @Override
53      public void postProcessBeanFactory(final ConfigurableListableBeanFactory beanFactory) throws BeansException {
54          final BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
55  
56          final Bundle bundle = bundleContext.getBundle();
57  
58          final String[] profileNames = splitProfiles(profileName);
59          final Set<String> classNames = new TreeSet<String>();
60          for (final String fileToRead : getIndexFilesForProfiles(profileNames, CommonConstants.COMPONENT_IMPORT_KEY)) {
61              classNames.addAll(readAllIndexFilesForProduct(fileToRead, bundle, bundleContext));
62          }
63          for (final String className : classNames) {
64              final String[] typeAndName = className.split("#");
65              final String beanType = typeAndName[0];
66              final String beanName = (typeAndName.length > 1) ? typeAndName[1] : "";
67  
68              try {
69                  final Class beanClass = beanFactory.getBeanClassLoader().loadClass(beanType);
70                  registerComponentImportBean(registry, beanClass, beanName);
71              } catch (final ClassNotFoundException e) {
72                  log.error("Unable to load class '" + beanType + "' for component importation purposes.  Skipping...");
73              }
74          }
75  
76          processMetaData(registry, profileNames);
77      }
78  
79      private void processMetaData(final BeanDefinitionRegistry registry, final String[] profileNames) {
80          for (final String fileToRead : getIndexFilesForProfiles(profileNames, CommonConstants.METADATA_FILE)) {
81              final URL url = bundleContext.getBundle().getEntry(fileToRead);
82              processMetaDataProperties(registry, readPropertiesFile(url));
83          }
84      }
85  
86      private void processMetaDataProperties(final BeanDefinitionRegistry registry, final Properties properties) {
87          //
88          // we don't have any metadata properties support yet but we might one day.  So this code is left in
89          // which I know is against the spirit of YAGNI but then again cliches are just that - cliche!
90      }
91  
92      /**
93       * Figures out the proper bean name for the service and registers it
94       *
95       * @param registry  the registry of beans
96       * @param beanClass the class of the bean to import
97       * @param beanName  the name of the bean to import
98       */
99      private void registerComponentImportBean(final BeanDefinitionRegistry registry, final Class beanClass, final String beanName) {
100         String serviceBeanName = beanName;
101 
102         if ("".equals(serviceBeanName)) {
103             serviceBeanName = Introspector.decapitalize(beanClass.getSimpleName());
104         }
105 
106         registerBeanDefinition(registry, serviceBeanName, "(objectClass=" + beanClass.getName() + ")", beanClass);
107     }
108 
109     /**
110      * Creates an OsgiServiceProxyFactoryBean for the requested import type.
111      *
112      * @param registry   the bean registry
113      * @param beanName   the name of the bean
114      * @param filter     the OSGI filter to apply
115      * @param interfaces the interface of the imported type
116      */
117     private void registerBeanDefinition(
118             final BeanDefinitionRegistry registry, final String beanName, final String filter, final Class interfaces) {
119         // This class is explicitly mentioned here (Rather than using Class.forName() to make sure
120         // BND will generate a package import for this class!
121         final Class osgiServiceProxyFactoryBeanClass = OsgiServiceProxyFactoryBean.class;
122         final BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(osgiServiceProxyFactoryBeanClass);
123         builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
124         builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
125         //builder.setLazyInit(true);
126 
127         if (filter != null && !filter.trim().isEmpty()) {
128             builder.addPropertyValue("filter", filter);
129         }
130 
131         builder.addPropertyValue("interfaces", new Class[]{interfaces});
132         builder.addPropertyValue("beanClassLoader", OsgiPlugin.class.getClassLoader());
133 
134         final BeanDefinition newDefinition = builder.getBeanDefinition();
135 
136         registry.registerBeanDefinition(beanName, newDefinition);
137     }
138 
139     public void setProfileName(final String profileName) {
140         this.profileName = profileName;
141     }
142 }