1 package com.atlassian.plugin.spring;
2
3 import com.atlassian.plugin.osgi.hostcomponents.ContextClassLoaderStrategy;
4 import com.google.common.annotations.VisibleForTesting;
5 import org.slf4j.Logger;
6 import org.springframework.beans.BeansException;
7 import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
8 import org.springframework.beans.factory.config.BeanDefinition;
9 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
10 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
11 import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
12 import org.springframework.core.type.MethodMetadata;
13
14 import java.util.Map;
15 import java.util.function.Function;
16
17 import static org.slf4j.LoggerFactory.getLogger;
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 public class AvailableToPluginsBeanDefinitionRegistryProcessor implements BeanDefinitionRegistryPostProcessor {
35 private static final Logger log = getLogger(AvailableToPluginsBeanDefinitionRegistryProcessor.class);
36
37 private final Function<BeanDefinitionRegistry, PluginBeanDefinitionRegistry> registryFactory;
38
39 public AvailableToPluginsBeanDefinitionRegistryProcessor() {
40 this(PluginBeanDefinitionRegistry::new);
41 }
42
43 @VisibleForTesting
44 AvailableToPluginsBeanDefinitionRegistryProcessor(Function<BeanDefinitionRegistry, PluginBeanDefinitionRegistry> registryFactory) {
45 this.registryFactory = registryFactory;
46 }
47
48 @Override
49 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
50 log.debug("Scanning al;l bean definitions for plugin-available @Bean methods");
51 final PluginBeanDefinitionRegistry pluginBeanDefinitionRegistry = registryFactory.apply(registry);
52 for (String beanName : registry.getBeanDefinitionNames()) {
53 final BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
54 if (beanDefinition instanceof AnnotatedBeanDefinition) {
55 final MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) beanDefinition).getFactoryMethodMetadata();
56 if (factoryMethodMetadata != null) {
57 final boolean isPluginAvailable = factoryMethodMetadata.isAnnotated(AvailableToPlugins.class.getName());
58 if (isPluginAvailable) {
59 if (beanDefinition.isSingleton()) {
60 log.debug("Registering bean '{}' as plugin-available", beanName);
61 registerPluginAvailableBean(beanName, factoryMethodMetadata, pluginBeanDefinitionRegistry);
62 } else {
63 log.warn("Bean '{}' is not singleton-scoped, and cannot be made available to plugins", beanName);
64 }
65 }
66 }
67 }
68 }
69 }
70
71
72
73
74
75
76
77 private void registerPluginAvailableBean(String beanName, MethodMetadata methodMetadata, PluginBeanDefinitionRegistry registry) {
78 registry.addBeanName(beanName);
79 final Map<String, Object> annotationAttributes = methodMetadata.getAnnotationAttributes(AvailableToPlugins.class.getName(), true);
80 final String valueAttribute = (String) annotationAttributes.get("value");
81 if (!valueAttribute.equals(Void.class.getName())) {
82 registry.addBeanInterface(beanName, valueAttribute);
83 }
84 final String[] interfacesAttribute = (String[]) annotationAttributes.get("interfaces");
85 for (String interfaceName : interfacesAttribute) {
86 registry.addBeanInterface(beanName, interfaceName);
87 }
88
89 registry.addContextClassLoaderStrategy(beanName, (ContextClassLoaderStrategy) annotationAttributes.get("contextClassLoaderStrategy"));
90 final boolean trackBundle = (Boolean) annotationAttributes.get("trackBundle");
91 if (trackBundle)
92 registry.addBundleTrackingBean(beanName);
93 }
94
95 @Override
96 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
97
98 }
99 }