1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.AutowireCapablePlugin;
4 import com.atlassian.plugin.IllegalPluginStateException;
5 import com.atlassian.plugin.PluginException;
6 import com.atlassian.plugin.osgi.container.OsgiContainerException;
7 import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
8 import com.atlassian.plugin.osgi.util.BundleClassLoaderAccessor;
9 import com.atlassian.plugin.util.resource.AlternativeDirectoryResourceLoader;
10
11 import org.apache.commons.lang.Validate;
12 import org.osgi.framework.Bundle;
13 import org.osgi.framework.Constants;
14 import org.osgi.service.packageadmin.ExportedPackage;
15 import org.osgi.service.packageadmin.PackageAdmin;
16 import org.osgi.util.tracker.ServiceTracker;
17
18 import java.io.InputStream;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.net.URL;
22 import java.util.HashSet;
23 import java.util.Set;
24
25
26
27
28
29
30 class OsgiPluginInstalledHelper implements OsgiPluginHelper
31 {
32 private final ClassLoader bundleClassLoader;
33 private final Bundle bundle;
34 private final PackageAdmin packageAdmin;
35 private final boolean requireSpring;
36
37 private volatile SpringContextAccessor springContextAccessor;
38 private ServiceTracker[] serviceTrackers;
39
40
41
42
43
44
45 public OsgiPluginInstalledHelper(final Bundle bundle, final PackageAdmin packageAdmin, final boolean requireSpring)
46 {
47 Validate.notNull(bundle);
48 Validate.notNull(packageAdmin);
49 this.bundle = bundle;
50 bundleClassLoader = BundleClassLoaderAccessor.getClassLoader(bundle, new AlternativeDirectoryResourceLoader());
51 this.packageAdmin = packageAdmin;
52 this.requireSpring = requireSpring;
53 }
54
55 public Bundle getBundle()
56 {
57 return bundle;
58 }
59
60 public <T> Class<T> loadClass(final String clazz, final Class<?> callingClass) throws ClassNotFoundException
61 {
62 return BundleClassLoaderAccessor.loadClass(getBundle(), clazz);
63 }
64
65 public URL getResource(final String name)
66 {
67 return bundleClassLoader.getResource(name);
68 }
69
70 public InputStream getResourceAsStream(final String name)
71 {
72 return bundleClassLoader.getResourceAsStream(name);
73 }
74
75 public ClassLoader getClassLoader()
76 {
77 return bundleClassLoader;
78 }
79
80 public Bundle install()
81 {
82 throw new IllegalPluginStateException("Plugin '" + bundle.getSymbolicName() + "' has already been installed");
83 }
84
85 public void onEnable(final ServiceTracker... serviceTrackers) throws OsgiContainerException
86 {
87 Validate.notNull(serviceTrackers);
88 this.serviceTrackers = serviceTrackers;
89 for (final ServiceTracker svc : serviceTrackers)
90 {
91 svc.open();
92 }
93 }
94
95 public void onDisable() throws OsgiContainerException
96 {
97 for (final ServiceTracker svc : serviceTrackers)
98 {
99 svc.close();
100 }
101 serviceTrackers = null;
102 setPluginContainer(null);
103 }
104
105 public void onUninstall() throws OsgiContainerException
106 {
107 }
108
109
110
111
112
113
114
115
116
117
118
119 public <T> T autowire(final Class<T> clazz, final AutowireCapablePlugin.AutowireStrategy autowireStrategy) throws IllegalPluginStateException
120 {
121 if (requireSpring)
122 {
123 assertSpringContextAvailable();
124 return springContextAccessor.createBean(clazz, autowireStrategy);
125 }
126 else
127 {
128 try
129 {
130 return clazz.newInstance();
131 }
132 catch (final InstantiationException e)
133 {
134 throw new PluginException("Unable to instantiate " + clazz, e);
135 }
136 catch (final IllegalAccessException e)
137 {
138 throw new PluginException("Unable to access " + clazz, e);
139 }
140 }
141 }
142
143
144
145
146
147
148
149
150
151
152 public void autowire(final Object instance, final AutowireCapablePlugin.AutowireStrategy autowireStrategy) throws IllegalPluginStateException
153 {
154
155 if (requireSpring)
156 {
157 assertSpringContextAvailable();
158 springContextAccessor.createBean(instance, autowireStrategy);
159 }
160 }
161
162 public Set<String> getRequiredPlugins()
163 {
164 final Set<String> keys = new HashSet<String>();
165
166
167 final Set<String> imports = OsgiHeaderUtil.parseHeader((String) getBundle().getHeaders().get(Constants.IMPORT_PACKAGE)).keySet();
168
169
170 for (final String imp : imports)
171 {
172
173 final ExportedPackage[] exports = packageAdmin.getExportedPackages(imp);
174 if (exports != null)
175 {
176
177 for (final ExportedPackage export : exports)
178 {
179
180 final Bundle[] importingBundles = export.getImportingBundles();
181 if (importingBundles != null)
182 {
183
184 for (final Bundle importingBundle : importingBundles)
185 {
186
187 if (getBundle() == importingBundle)
188 {
189 keys.add(OsgiHeaderUtil.getPluginKey(export.getExportingBundle()));
190 break;
191 }
192 }
193 }
194 }
195 }
196 }
197 return keys;
198 }
199
200 public void setPluginContainer(final Object container)
201 {
202 if (container == null)
203 {
204 springContextAccessor = null;
205 }
206 else
207 {
208 springContextAccessor = new SpringContextAccessor(container);
209 }
210 }
211
212
213
214
215 private void assertSpringContextAvailable() throws IllegalPluginStateException
216 {
217 if (springContextAccessor == null)
218 {
219 throw new IllegalStateException("Cannot autowire object because the Spring context is unavailable. " +
220 "Ensure your OSGi bundle contains the 'Spring-Context' header.");
221 }
222 }
223
224
225
226
227
228
229 private static final class SpringContextAccessor
230 {
231 private final Object nativeBeanFactory;
232 private final Method nativeCreateBeanMethod;
233 private final Method nativeAutowireBeanMethod;
234
235 public SpringContextAccessor(final Object applicationContext)
236 {
237 Object beanFactory = null;
238 try
239 {
240 final Method m = applicationContext.getClass().getMethod("getAutowireCapableBeanFactory");
241 beanFactory = m.invoke(applicationContext);
242 }
243 catch (final NoSuchMethodException e)
244 {
245
246 throw new PluginException("Cannot find createBean method on registered bean factory: " + beanFactory, e);
247 }
248 catch (final IllegalAccessException e)
249 {
250
251 throw new PluginException("Cannot access createBean method", e);
252 }
253 catch (final InvocationTargetException e)
254 {
255 handleSpringMethodInvocationError(e);
256 }
257
258 nativeBeanFactory = beanFactory;
259 try
260 {
261 nativeCreateBeanMethod = beanFactory.getClass().getMethod("createBean", Class.class, int.class, boolean.class);
262 nativeAutowireBeanMethod = beanFactory.getClass().getMethod("autowireBeanProperties", Object.class, int.class, boolean.class);
263 }
264 catch (final NoSuchMethodException e)
265 {
266
267 throw new PluginException("Cannot find createBean method on registered bean factory: " + nativeBeanFactory, e);
268 }
269 }
270
271 private void handleSpringMethodInvocationError(final InvocationTargetException e)
272 {
273 if (e.getCause() instanceof Error)
274 {
275 throw (Error) e.getCause();
276 }
277 else if (e.getCause() instanceof RuntimeException)
278 {
279 throw (RuntimeException) e.getCause();
280 }
281 else
282 {
283
284 throw new PluginException("Unable to invoke createBean", e.getCause());
285 }
286 }
287
288 public <T> T createBean(final Class<T> clazz, final AutowireCapablePlugin.AutowireStrategy autowireStrategy)
289 {
290 if (nativeBeanFactory == null)
291 {
292 return null;
293 }
294
295 try
296 {
297 return clazz.cast(nativeCreateBeanMethod.invoke(nativeBeanFactory, clazz, autowireStrategy.ordinal(), false));
298 }
299 catch (final IllegalAccessException e)
300 {
301
302 throw new PluginException("Unable to access createBean method", e);
303 }
304 catch (final InvocationTargetException e)
305 {
306 handleSpringMethodInvocationError(e);
307 return null;
308 }
309 }
310
311 public void createBean(final Object instance, final AutowireCapablePlugin.AutowireStrategy autowireStrategy)
312 {
313 if (nativeBeanFactory == null)
314 {
315 return;
316 }
317
318 try
319 {
320 nativeAutowireBeanMethod.invoke(nativeBeanFactory, instance, autowireStrategy.ordinal(), false);
321 }
322 catch (final IllegalAccessException e)
323 {
324
325 throw new PluginException("Unable to access createBean method", e);
326 }
327 catch (final InvocationTargetException e)
328 {
329 handleSpringMethodInvocationError(e);
330 }
331 }
332 }
333
334 }