1 package com.atlassian.plugin.spring.scanner.runtime.impl;
2
3 import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsDevService;
4 import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
5 import com.atlassian.plugin.spring.scanner.annotation.export.ModuleType;
6 import com.atlassian.plugin.spring.scanner.util.CommonConstants;
7 import com.google.common.collect.ImmutableList;
8 import com.google.common.collect.ImmutableMap;
9 import com.google.common.collect.Iterables;
10 import org.osgi.framework.Bundle;
11 import org.osgi.framework.BundleContext;
12 import org.osgi.framework.ServiceRegistration;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15 import org.springframework.aop.support.AopUtils;
16 import org.springframework.beans.BeansException;
17 import org.springframework.beans.factory.InitializingBean;
18 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
19 import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
20
21 import java.lang.annotation.Annotation;
22 import java.util.Hashtable;
23 import java.util.List;
24
25 import static com.atlassian.plugin.spring.scanner.runtime.impl.util.AnnotationIndexReader.getIndexFilesForProfiles;
26 import static com.atlassian.plugin.spring.scanner.runtime.impl.util.AnnotationIndexReader.readAllIndexFilesForProduct;
27 import static com.atlassian.plugin.spring.scanner.runtime.impl.util.AnnotationIndexReader.splitProfiles;
28
29
30
31
32
33
34
35
36
37 public class ServiceExporterBeanPostProcessor implements DestructionAwareBeanPostProcessor, InitializingBean {
38 public static final String OSGI_SERVICE_SUFFIX = "_osgiService";
39 static final String ATLASSIAN_DEV_MODE_PROP = "atlassian.dev.mode";
40
41 private static final Logger log = LoggerFactory.getLogger(ServiceExporterBeanPostProcessor.class);
42
43 private final boolean isDevMode = Boolean.parseBoolean(System.getProperty(ATLASSIAN_DEV_MODE_PROP, "false"));
44 private final BundleContext bundleContext;
45 private ConfigurableListableBeanFactory beanFactory;
46 private String profileName;
47 private final ExportedSeviceManager serviceManager;
48
49 private ImmutableMap<String, String[]> exports;
50
51 public ServiceExporterBeanPostProcessor(final BundleContext bundleContext, final ConfigurableListableBeanFactory beanFactory) {
52 this(bundleContext, beanFactory, new ExportedSeviceManager());
53 }
54
55 ServiceExporterBeanPostProcessor(final BundleContext bundleContext, final ConfigurableListableBeanFactory beanFactory,
56 ExportedSeviceManager serviceManager) {
57 this.bundleContext = bundleContext;
58 this.beanFactory = beanFactory;
59 this.profileName = null;
60 this.serviceManager = serviceManager;
61 }
62
63
64 @SuppressWarnings("UnusedDeclaration")
65 public void setProfileName(final String profileName) {
66 this.profileName = profileName;
67 }
68
69 @Override
70 public void afterPropertiesSet() throws Exception {
71 final ImmutableMap.Builder<String, String[]> exportBuilder = ImmutableMap.builder();
72
73 final Bundle bundle = bundleContext.getBundle();
74
75 final String[] profileNames = splitProfiles(profileName);
76 parseExportsForExportFile(exportBuilder, CommonConstants.COMPONENT_EXPORT_KEY, profileNames, bundle);
77 if (isDevMode) {
78 parseExportsForExportFile(exportBuilder, CommonConstants.COMPONENT_DEV_EXPORT_KEY, profileNames, bundle);
79 }
80 exports = exportBuilder.build();
81 }
82
83 private void parseExportsForExportFile(
84 final ImmutableMap.Builder<String, String[]> exportBuilder, final String exportFileName,
85 final String[] profileNames, final Bundle bundle) {
86 final String[] defaultInterfaces = {};
87 for (final String fileToRead : getIndexFilesForProfiles(profileNames, exportFileName)) {
88 final List<String> exportData = readAllIndexFilesForProduct(fileToRead, bundle, bundleContext);
89 for (final String export : exportData) {
90 final String[] targetAndInterfaces = export.split("#");
91 final String target = targetAndInterfaces[0];
92 final String[] interfaces = (targetAndInterfaces.length > 1)
93 ? targetAndInterfaces[1].split(",")
94 : defaultInterfaces;
95 exportBuilder.put(target, interfaces);
96 }
97 }
98 }
99
100 @Override
101 public void postProcessBeforeDestruction(final Object bean, final String beanName) throws BeansException {
102 if (serviceManager.hasService(bean)) {
103 serviceManager.unregisterService(bundleContext, bean);
104
105 final String serviceName = getServiceName(beanName);
106 if (beanFactory.containsBean(serviceName)) {
107 final Object serviceBean = beanFactory.getBean(serviceName);
108
109 if (null != serviceBean) {
110 beanFactory.destroyBean(serviceName, serviceBean);
111 }
112 }
113 }
114 }
115
116 @Override
117 public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
118 return bean;
119 }
120
121 @Override
122 public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
123 Class<?>[] interfaces = {};
124
125
126
127 final Class<?> beanTargetClass = AopUtils.getTargetClass(bean);
128 final String beanClassName = beanTargetClass.getName();
129
130 if (exports.containsKey(beanClassName) || isPublicComponent(beanTargetClass)) {
131 if (exports.containsKey(beanClassName)) {
132
133
134
135 final ImmutableList.Builder<Class<?>> interfaceBuilder = ImmutableList.builder();
136 final ClassLoader beanClassLoader = bean.getClass().getClassLoader();
137 final String[] interfaceNames = exports.get(beanClassName);
138 for (final String interfaceName : interfaceNames) {
139 try {
140 final Class interfaceClass = beanClassLoader.loadClass(interfaceName);
141 interfaceBuilder.add(interfaceClass);
142 } catch (final ClassNotFoundException ecnf) {
143 log.warn("Cannot find class for export '" + interfaceName + "' of bean '" + beanName + "': " + ecnf);
144
145 }
146 }
147 interfaces = Iterables.toArray(interfaceBuilder.build(), Class.class);
148 } else if (hasAnnotation(beanTargetClass, ModuleType.class)) {
149 interfaces = beanTargetClass.getAnnotation(ModuleType.class).value();
150 } else if (hasAnnotation(beanTargetClass, ExportAsService.class)) {
151 interfaces = beanTargetClass.getAnnotation(ExportAsService.class).value();
152 } else if (hasAnnotation(beanTargetClass, ExportAsDevService.class)) {
153 interfaces = beanTargetClass.getAnnotation(ExportAsDevService.class).value();
154 }
155
156
157 if (interfaces.length < 1) {
158
159
160 interfaces = beanTargetClass.getInterfaces();
161
162
163 if (interfaces.length < 1) {
164 interfaces = new Class<?>[]{beanTargetClass};
165 }
166 }
167
168 try {
169 final ServiceRegistration serviceRegistration = serviceManager.registerService(
170 bundleContext, bean, beanName, new Hashtable<String, Object>(), interfaces);
171 final String serviceName = getServiceName(beanName);
172 beanFactory.initializeBean(serviceRegistration, serviceName);
173 } catch (final Exception e) {
174 log.error("Unable to register bean '" + beanName + "' as an OSGi exported service", e);
175 }
176 }
177
178 return bean;
179 }
180
181 private boolean isPublicComponent(final Class beanTargetClass) {
182 return hasAnnotation(beanTargetClass, ModuleType.class)
183 || hasAnnotation(beanTargetClass, ExportAsService.class)
184 || (hasAnnotation(beanTargetClass, ExportAsDevService.class) && isDevMode);
185 }
186
187 private boolean hasAnnotation(final Class beanTargetClass, final Class<? extends Annotation> annotationClass) {
188 return beanTargetClass.isAnnotationPresent(annotationClass);
189 }
190
191 private String getServiceName(final String beanName) {
192 return beanName + OSGI_SERVICE_SUFFIX;
193 }
194
195 }