1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.AutowireCapablePlugin;
4 import com.atlassian.plugin.ModuleDescriptor;
5 import com.atlassian.plugin.PluginException;
6 import com.atlassian.plugin.impl.AbstractPlugin;
7 import com.atlassian.plugin.impl.DynamicPlugin;
8 import com.atlassian.plugin.osgi.container.OsgiContainerException;
9 import org.apache.commons.lang.Validate;
10 import org.apache.commons.logging.Log;
11 import org.apache.commons.logging.LogFactory;
12 import org.osgi.framework.*;
13 import org.osgi.util.tracker.ServiceTracker;
14 import org.osgi.util.tracker.ServiceTrackerCustomizer;
15
16 import java.io.InputStream;
17 import java.lang.reflect.InvocationTargetException;
18 import java.lang.reflect.Method;
19 import java.net.URL;
20
21
22
23
24 public class OsgiPlugin extends AbstractPlugin implements AutowireCapablePlugin, DynamicPlugin
25 {
26 private final Bundle bundle;
27 private static final Log log = LogFactory.getLog(OsgiPlugin.class);
28 private boolean deletable = true;
29 private boolean bundled = false;
30 private Object nativeBeanFactory;
31 private Method nativeCreateBeanMethod;
32 private Method nativeAutowireBeanMethod;
33 private ServiceTracker moduleDescriptorTracker;
34
35 public OsgiPlugin(Bundle bundle)
36 {
37 Validate.notNull(bundle, "The bundle is required");
38 this.bundle = bundle;
39 }
40
41 public Bundle getBundle()
42 {
43 return bundle;
44 }
45
46 public Class loadClass(String clazz, Class callingClass) throws ClassNotFoundException
47 {
48 return BundleClassLoaderAccessor.loadClass(bundle, clazz, callingClass);
49 }
50
51 public boolean isUninstallable()
52 {
53 return true;
54 }
55
56 public URL getResource(String name)
57 {
58 return BundleClassLoaderAccessor.getResource(bundle, name);
59 }
60
61 public InputStream getResourceAsStream(String name)
62 {
63 return BundleClassLoaderAccessor.getResourceAsStream(bundle, name);
64 }
65
66 public ClassLoader getClassLoader()
67 {
68 return BundleClassLoaderAccessor.getClassLoader(bundle);
69 }
70
71
72
73
74
75 public boolean isDynamicallyLoaded()
76 {
77 return true;
78 }
79
80
81 public boolean isDeleteable()
82 {
83 return deletable;
84 }
85
86 public void setDeletable(boolean deletable)
87 {
88 this.deletable = deletable;
89 }
90
91 public boolean isBundledPlugin()
92 {
93 return bundled;
94 }
95
96 public void setBundled(boolean bundled)
97 {
98 this.bundled = bundled;
99 }
100
101 public synchronized boolean isEnabled()
102 {
103 return Bundle.ACTIVE == bundle.getState() && (!shouldHaveSpringContext() || ensureNativeBeanFactory());
104 }
105
106 public synchronized void setEnabled(boolean enabled) throws OsgiContainerException
107 {
108 if (enabled) enable(); else disable();
109 }
110
111 void enable() throws OsgiContainerException
112 {
113 try
114 {
115 if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.INSTALLED)
116 {
117 bundle.start();
118 if (bundle.getBundleContext() != null)
119 {
120 moduleDescriptorTracker = new ServiceTracker(bundle.getBundleContext(), ModuleDescriptor.class.getName(), new RegisteringServiceTrackerCustomizer());
121 moduleDescriptorTracker.open();
122 }
123 }
124 }
125 catch (BundleException e)
126 {
127 throw new OsgiContainerException("Cannot start plugin: "+getKey(), e);
128 }
129 }
130
131 void disable() throws OsgiContainerException
132 {
133 try
134 {
135 if (bundle.getState() == Bundle.ACTIVE)
136 {
137 if (moduleDescriptorTracker != null) moduleDescriptorTracker.close();
138 bundle.stop();
139 moduleDescriptorTracker = null;
140 nativeBeanFactory = null;
141 nativeCreateBeanMethod = null;
142 }
143 }
144 catch (BundleException e)
145 {
146 throw new OsgiContainerException("Cannot stop plugin: "+getKey(), e);
147 }
148 }
149
150 public synchronized void close() throws OsgiContainerException
151 {
152 try
153 {
154 if (bundle.getState() != Bundle.UNINSTALLED)
155 bundle.uninstall();
156 }
157 catch (BundleException e)
158 {
159 throw new OsgiContainerException("Cannot uninstall bundle " + bundle.getSymbolicName());
160 }
161 }
162
163 private boolean shouldHaveSpringContext()
164 {
165 return bundle.getHeaders().get("Spring-Context") != null;
166 }
167
168 public <T> T autowire(Class<T> clazz)
169 {
170 return autowire(clazz, AutowireStrategy.AUTOWIRE_AUTODETECT);
171 }
172
173 public synchronized <T> T autowire(Class<T> clazz, AutowireStrategy autowireStrategy)
174 {
175 if (!ensureNativeBeanFactory())
176 return null;
177
178 try
179 {
180 return (T) nativeCreateBeanMethod.invoke(nativeBeanFactory, clazz, autowireStrategy.ordinal(), false);
181 }
182 catch (IllegalAccessException e)
183 {
184
185 throw new PluginException("Unable to access createBean method", e);
186 }
187 catch (InvocationTargetException e)
188 {
189 handleSpringMethodInvocationError(e);
190 return null;
191 }
192 }
193
194 public void autowire(Object instance)
195 {
196 autowire(instance, AutowireStrategy.AUTOWIRE_AUTODETECT);
197 }
198
199 public void autowire(Object instance, AutowireStrategy autowireStrategy)
200 {
201 if (!ensureNativeBeanFactory())
202 return;
203
204 try
205 {
206 nativeAutowireBeanMethod.invoke(nativeBeanFactory, instance, autowireStrategy.ordinal(), false);
207 }
208 catch (IllegalAccessException e)
209 {
210
211 throw new PluginException("Unable to access createBean method", e);
212 }
213 catch (InvocationTargetException e)
214 {
215 handleSpringMethodInvocationError(e);
216 }
217 }
218
219 private boolean ensureNativeBeanFactory()
220 {
221 if (nativeBeanFactory == null)
222 {
223
224 try
225 {
226 BundleContext ctx = bundle.getBundleContext();
227 if (ctx == null)
228 {
229 log.warn("no bundle context - we are screwed");
230 return false;
231 }
232 ServiceReference[] services = ctx.getServiceReferences("org.springframework.context.ApplicationContext", "(org.springframework.context.service.name="+bundle.getSymbolicName()+")");
233 if (services == null || services.length == 0)
234 {
235 log.debug("No spring bean factory found...yet");
236 return false;
237 }
238
239 Object applicationContext = ctx.getService(services[0]);
240 try
241 {
242 Method m = applicationContext.getClass().getMethod("getAutowireCapableBeanFactory");
243 nativeBeanFactory = m.invoke(applicationContext);
244 } catch (NoSuchMethodException e)
245 {
246
247 throw new PluginException("Cannot find createBean method on registered bean factory: "+nativeBeanFactory, e);
248 } catch (IllegalAccessException e)
249 {
250
251 throw new PluginException("Cannot access createBean method", e);
252 } catch (InvocationTargetException e)
253 {
254 handleSpringMethodInvocationError(e);
255 return false;
256 }
257 } catch (InvalidSyntaxException e)
258 {
259 throw new OsgiContainerException("Invalid LDAP filter", e);
260 }
261
262
263 try
264 {
265 nativeCreateBeanMethod = nativeBeanFactory.getClass().getMethod("createBean", Class.class, int.class, boolean.class);
266 nativeAutowireBeanMethod = nativeBeanFactory.getClass().getMethod("autowireBeanProperties", Object.class, int.class, boolean.class);
267 } catch (NoSuchMethodException e)
268 {
269
270 throw new PluginException("Cannot find createBean method on registered bean factory: "+nativeBeanFactory, e);
271 }
272 }
273 return nativeBeanFactory != null && nativeCreateBeanMethod != null && nativeAutowireBeanMethod != null;
274 }
275
276 private void handleSpringMethodInvocationError(InvocationTargetException e)
277 {
278 if (e.getCause() instanceof Error)
279 throw (Error) e.getCause();
280 else if (e.getCause() instanceof RuntimeException)
281 throw (RuntimeException) e.getCause();
282 else
283 {
284
285 throw new PluginException("Unable to invoke createBean", e.getCause());
286 }
287 }
288
289 public String toString()
290 {
291 return getKey();
292 }
293
294
295
296
297 private class RegisteringServiceTrackerCustomizer implements ServiceTrackerCustomizer
298 {
299
300 public Object addingService(ServiceReference serviceReference)
301 {
302 if (serviceReference.getBundle() == bundle)
303 {
304 ModuleDescriptor descriptor = (ModuleDescriptor) bundle.getBundleContext().getService(serviceReference);
305 addModuleDescriptor(descriptor);
306 return descriptor;
307 }
308 return null;
309 }
310
311 public void modifiedService(ServiceReference serviceReference, Object o)
312 {
313 if (serviceReference.getBundle() == bundle)
314 {
315 ModuleDescriptor descriptor = (ModuleDescriptor) o;
316 addModuleDescriptor(descriptor);
317 }
318 }
319
320 public void removedService(ServiceReference serviceReference, Object o)
321 {
322 if (serviceReference.getBundle() == bundle)
323 {
324 ModuleDescriptor descriptor = (ModuleDescriptor) o;
325 removeModuleDescriptor(descriptor.getKey());
326 }
327 }
328 }
329 }