1 package com.atlassian.plugin.osgi.bridge.external;
2
3 import com.atlassian.plugin.PluginException;
4 import org.apache.commons.lang.Validate;
5 import org.osgi.framework.BundleContext;
6 import org.osgi.framework.InvalidSyntaxException;
7 import org.osgi.framework.ServiceEvent;
8 import static org.osgi.framework.ServiceEvent.REGISTERED;
9 import org.osgi.framework.ServiceListener;
10 import org.osgi.framework.ServiceReference;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13 import org.springframework.beans.factory.FactoryBean;
14 import org.springframework.beans.factory.InitializingBean;
15 import org.springframework.osgi.context.BundleContextAware;
16 import org.springframework.osgi.service.ServiceUnavailableException;
17
18 import java.lang.reflect.InvocationHandler;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Proxy;
22
23
24
25
26
27
28
29 public class HostComponentFactoryBean implements FactoryBean, BundleContextAware, InitializingBean
30 {
31 private BundleContext bundleContext;
32 private String filter;
33 private Object service;
34 private Class<?>[] interfaces;
35
36 public Object getObject() throws Exception
37 {
38 return findService();
39 }
40
41 public Class getObjectType()
42 {
43 return (findService() != null ? findService().getClass() : null);
44 }
45
46 public boolean isSingleton()
47 {
48 return true;
49 }
50
51 public void setBundleContext(BundleContext bundleContext)
52 {
53 this.bundleContext = bundleContext;
54 }
55
56
57
58
59
60
61 public void setFilter(String filter)
62 {
63 this.filter = filter;
64 }
65
66 public void setInterfaces(Class<?>[] interfaces)
67 {
68 this.interfaces = interfaces;
69 }
70
71
72
73
74
75
76
77
78 private Object findService() throws PluginException
79 {
80 return service;
81 }
82
83
84
85
86
87
88 private Object createHostComponentProxy()
89 {
90
91 return Proxy.newProxyInstance(bundleContext.getClass().getClassLoader(), interfaces, new DynamicServiceInvocationHandler(
92 bundleContext, filter));
93 }
94
95 public void afterPropertiesSet() throws Exception
96 {
97 Validate.notNull(bundleContext);
98 Validate.notNull(interfaces);
99 service = createHostComponentProxy();
100 }
101
102
103
104
105
106 static class DynamicServiceInvocationHandler implements InvocationHandler
107 {
108 private static final Logger log = LoggerFactory.getLogger(DynamicServiceInvocationHandler.class);
109 private volatile Object service;
110 private final String filter;
111
112
113 DynamicServiceInvocationHandler(final BundleContext bundleContext, final String filter)
114 {
115 this.filter = filter;
116 try
117 {
118 ServiceReference[] refs = bundleContext.getServiceReferences(null, filter);
119 if (refs != null && refs.length > 0)
120 {
121 service = bundleContext.getService(refs[0]);
122 }
123
124 bundleContext.addServiceListener(new ServiceListener()
125 {
126 public void serviceChanged(ServiceEvent serviceEvent)
127 {
128
129 if (REGISTERED == serviceEvent.getType())
130 {
131 if (log.isDebugEnabled())
132 {
133 log.debug("Updating the host component matching filter: " + filter);
134 }
135 service = bundleContext.getService(serviceEvent.getServiceReference());
136 }
137 }
138 }, filter);
139 }
140 catch (InvalidSyntaxException e)
141 {
142 throw new IllegalArgumentException("Invalid filter string: " + filter, e);
143 }
144 }
145
146 public Object invoke(final Object o, final Method method, final Object[] objects) throws Throwable
147 {
148 if (service == null)
149 {
150 throw new IllegalStateException("Unable to locate host component with filter: " + filter);
151 }
152 try
153 {
154 return method.invoke(service, objects);
155 }
156 catch (final InvocationTargetException e)
157 {
158 throw e.getTargetException();
159 }
160 }
161 }
162 }