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