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