1 package com.atlassian.plugin.spring.scanner.runtime.impl;
2
3 import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
4 import org.eclipse.gemini.blueprint.context.ConfigurableOsgiBundleApplicationContext;
5 import org.osgi.framework.Bundle;
6 import org.osgi.framework.BundleContext;
7 import org.osgi.framework.FrameworkUtil;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10 import org.springframework.beans.MutablePropertyValues;
11 import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
12 import org.springframework.beans.factory.config.BeanDefinition;
13 import org.springframework.beans.factory.config.BeanDefinitionHolder;
14 import org.springframework.beans.factory.parsing.BeanComponentDefinition;
15 import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
16 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
17 import org.springframework.beans.factory.support.RootBeanDefinition;
18 import org.springframework.beans.factory.xml.BeanDefinitionParser;
19 import org.springframework.beans.factory.xml.ParserContext;
20 import org.springframework.beans.factory.xml.XmlReaderContext;
21 import org.springframework.context.annotation.AnnotationConfigUtils;
22 import org.springframework.core.io.ResourceLoader;
23 import org.springframework.util.ClassUtils;
24 import org.w3c.dom.Element;
25
26 import java.util.HashMap;
27 import java.util.LinkedHashSet;
28 import java.util.Map;
29 import java.util.Set;
30
31 import static org.springframework.beans.factory.support.AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR;
32 import static org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.AUTOWIRE_ATTRIBUTE;
33
34
35
36
37
38 public class AtlassianScannerBeanDefinitionParser implements BeanDefinitionParser {
39 private static final String PROFILE_ATTRIBUTE = "profile";
40 public static final String JAVAX_INJECT_CLASSNAME = "javax.inject.Inject";
41
42 private static final Logger log = LoggerFactory.getLogger(AtlassianScannerBeanDefinitionParser.class);
43
44 @Override
45 public BeanDefinition parse(final Element element, final ParserContext parserContext) {
46 String profileName = null;
47 Integer autowireDefault = null;
48 if (element.hasAttribute(PROFILE_ATTRIBUTE)) {
49 profileName = element.getAttribute(PROFILE_ATTRIBUTE);
50 }
51 if (element.hasAttribute(AUTOWIRE_ATTRIBUTE)) {
52
53 autowireDefault = parserContext.getDelegate().getAutowireMode(element.getAttribute(AUTOWIRE_ATTRIBUTE));
54 }
55
56
57
58 final BundleContext targetPluginBundleContext = getBundleContext(parserContext);
59
60 checkScannerRuntimeIsNotEmbeddedInBundle(targetPluginBundleContext);
61
62 final ClassIndexBeanDefinitionScanner scanner = new ClassIndexBeanDefinitionScanner(
63 parserContext.getReaderContext().getRegistry(), profileName, autowireDefault, targetPluginBundleContext);
64 final Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan();
65
66 registerComponents(parserContext.getReaderContext(), beanDefinitions, element, profileName);
67
68 return null;
69 }
70
71
72
73
74
75
76
77
78
79
80 protected void registerComponents(
81 final XmlReaderContext readerContext,
82 final Set<BeanDefinitionHolder> beanDefinitions,
83 final Element element,
84 final String profileName) {
85 final Object source = readerContext.extractSource(element);
86 final CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
87
88
89 for (final BeanDefinitionHolder beanDefHolder : beanDefinitions) {
90 compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
91 }
92
93
94 final Set<BeanDefinitionHolder> processorDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
95 processorDefinitions.addAll(AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source));
96
97
98 final BeanDefinitionHolder javaxInject = getJavaxInjectPostProcessor(readerContext.getRegistry(), source);
99 if (null != javaxInject) {
100 processorDefinitions.add(javaxInject);
101 }
102 processorDefinitions.add(getComponentImportPostProcessor(readerContext.getRegistry(), source, profileName));
103 processorDefinitions.add(getServiceExportPostProcessor(readerContext.getRegistry(), source, profileName));
104 processorDefinitions.add(getDevModeBeanInitialisationLoggerPostProcessor(readerContext.getRegistry(), source));
105
106 for (final BeanDefinitionHolder processorDefinition : processorDefinitions) {
107 compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
108 }
109
110 readerContext.fireComponentRegistered(compositeDef);
111 }
112
113
114
115
116
117
118
119
120 private BundleContext getBundleContext(ParserContext parserContext) {
121 ResourceLoader resourceLoader = parserContext.getReaderContext().getResourceLoader();
122
123 if (!(resourceLoader instanceof ConfigurableOsgiBundleApplicationContext)) {
124 throw new IllegalStateException("Could not access BundleContext from ResourceLoader: "
125 + "expected resourceLoader to be an instance of "
126 + ConfigurableOsgiBundleApplicationContext.class.getName() + ": got "
127 + resourceLoader.getClass().getName());
128 }
129
130 final BundleContext bundleContext = ((ConfigurableOsgiBundleApplicationContext) resourceLoader).getBundleContext();
131 if (bundleContext == null) {
132 throw new IllegalStateException("Could not access BundleContext from ResourceLoader: "
133 + "ConfigurableOsgiBundleApplicationContext.getBundleContext returned null");
134 }
135
136 return bundleContext;
137 }
138
139
140
141
142
143
144
145
146 private void checkScannerRuntimeIsNotEmbeddedInBundle(BundleContext targetPluginBundleContext) {
147 final Bundle pluginInvokingScannerRuntime = targetPluginBundleContext.getBundle();
148 if (pluginInvokingScannerRuntime == null) {
149 throw new IllegalStateException("Cannot execute atlassian-spring-scanner-runtime from a plugin that is not in a valid state: "
150 + "bundleContext.getBundle() returned null for plugin bundle.");
151 }
152
153 final String howToFixScope = "Use 'mvn dependency:tree' and ensure the atlassian-spring-scanner-annotation "
154 + "dependency in your plugin has <scope>provided</scope>, not 'runtime' or 'compile', "
155 + "and you have NO dependency on atlassian-spring-scanner-runtime.";
156
157 final Bundle bundleContainingThisScannerRuntime = FrameworkUtil.getBundle(AtlassianScannerBeanDefinitionParser.class);
158 if (bundleContainingThisScannerRuntime == null) {
159 throw new IllegalStateException("Incorrect use of atlassian-spring-scanner-runtime: "
160 + "atlassian-spring-scanner-runtime classes do not appear to be coming from a bundle classloader. "
161 + howToFixScope);
162 }
163 final Bundle bundleContainingScannerAnnotationLibsAsSeenByRuntime = FrameworkUtil.getBundle(ComponentImport.class);
164 if (bundleContainingScannerAnnotationLibsAsSeenByRuntime == null) {
165 throw new IllegalStateException("Incorrect use of atlassian-spring-scanner-runtime: "
166 + "atlassian-spring-scanner-annotation classes do not appear to be coming from a bundle classloader. "
167 + howToFixScope);
168 }
169
170 final long invokingPluginBundleId = pluginInvokingScannerRuntime.getBundleId();
171 final long scannerRuntimeBundleId = bundleContainingThisScannerRuntime.getBundleId();
172 if (invokingPluginBundleId == scannerRuntimeBundleId) {
173 throw new IllegalStateException("Incorrect use of atlassian-spring-scanner-runtime: "
174 + "atlassian-spring-scanner-runtime classes are embedded inside the target plugin '"
175 + pluginInvokingScannerRuntime.getSymbolicName()
176 + "'; embedding scanner-runtime is not supported since scanner version 2.0. "
177 + howToFixScope);
178 }
179
180
181
182
183
184 Bundle bundleContainingScannerAnnotationLibsAsSeenByPlugin;
185 try {
186 bundleContainingScannerAnnotationLibsAsSeenByPlugin =
187 FrameworkUtil.getBundle(pluginInvokingScannerRuntime.loadClass(ComponentImport.class.getName()));
188 } catch (ClassNotFoundException e) {
189
190
191 return;
192 }
193 final long scannerAnnotationBundleId = bundleContainingScannerAnnotationLibsAsSeenByRuntime.getBundleId();
194 if (bundleContainingScannerAnnotationLibsAsSeenByPlugin == null
195 || bundleContainingScannerAnnotationLibsAsSeenByPlugin.getBundleId() != scannerAnnotationBundleId) {
196 throw new IllegalStateException("Cannot execute atlassian-spring-scanner-runtime: "
197 + "plugin has an extra copy of atlassian-spring-scanner-annotation classes, perhaps embedded inside the target plugin '"
198 + pluginInvokingScannerRuntime.getSymbolicName()
199 + "'; embedding scanner-annotations is not supported since scanner version 2.0. "
200 + howToFixScope);
201 }
202 }
203
204 private BeanDefinitionHolder getJavaxInjectPostProcessor(final BeanDefinitionRegistry registry, final Object source) {
205 if (ClassUtils.isPresent(JAVAX_INJECT_CLASSNAME, getClass().getClassLoader())) {
206 try {
207 final Class injectClass = getClass().getClassLoader().loadClass(JAVAX_INJECT_CLASSNAME);
208 final Map<String, Object> properties = new HashMap<String, Object>();
209 properties.put("autowiredAnnotationType", injectClass);
210
211 final RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
212 def.setSource(source);
213 def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
214 def.setPropertyValues(new MutablePropertyValues(properties));
215
216 return registerBeanPostProcessor(registry, def, "javaxInjectBeanPostProcessor");
217 } catch (final ClassNotFoundException e) {
218 log.error("Unable to load class '" + JAVAX_INJECT_CLASSNAME + "' for javax component purposes. Not sure how this is possible. Skipping...");
219 }
220 }
221
222 return null;
223 }
224
225
226
227
228
229
230
231
232
233 private BeanDefinitionHolder registerBeanPostProcessor(
234 final BeanDefinitionRegistry registry, final RootBeanDefinition definition, final String beanName) {
235 definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
236
237 registry.registerBeanDefinition(beanName, definition);
238 return new BeanDefinitionHolder(definition, beanName);
239 }
240
241 private BeanDefinitionHolder getComponentImportPostProcessor(
242 final BeanDefinitionRegistry registry, final Object source, final String profileName) {
243
244 final Map<String, Object> properties = new HashMap<String, Object>();
245 properties.put("profileName", profileName);
246
247 final RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(ComponentImportBeanFactoryPostProcessor.class);
248 rootBeanDefinition.setAutowireMode(AUTOWIRE_CONSTRUCTOR);
249 rootBeanDefinition.setSource(source);
250 rootBeanDefinition.setPropertyValues(new MutablePropertyValues(properties));
251
252 return registerBeanPostProcessor(registry, rootBeanDefinition, "componentImportBeanFactoryPostProcessor");
253 }
254
255 private BeanDefinitionHolder getServiceExportPostProcessor(
256 final BeanDefinitionRegistry registry, final Object source, final String profileName) {
257
258 final HashMap<String, Object> properties = new HashMap<String, Object>();
259 properties.put("profileName", profileName);
260
261 final RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(ServiceExporterBeanPostProcessor.class);
262 rootBeanDefinition.setSource(source);
263 rootBeanDefinition.setAutowireMode(AUTOWIRE_CONSTRUCTOR);
264 rootBeanDefinition.setPropertyValues(new MutablePropertyValues(properties));
265
266 return registerBeanPostProcessor(registry, rootBeanDefinition, "serviceExportBeanPostProcessor");
267 }
268
269 private BeanDefinitionHolder getDevModeBeanInitialisationLoggerPostProcessor(
270 final BeanDefinitionRegistry registry, final Object source) {
271 final RootBeanDefinition def = new RootBeanDefinition(DevModeBeanInitialisationLoggerBeanPostProcessor.class);
272 def.setSource(source);
273 def.setAutowireMode(AUTOWIRE_CONSTRUCTOR);
274
275 return registerBeanPostProcessor(registry, def, "devModeBeanInitialisationLoggerBeanPostProcessor");
276 }
277 }