1 package com.atlassian.plugin.osgi.bridge.external;
2
3 import org.springframework.beans.factory.FactoryBean;
4 import org.springframework.beans.factory.InitializingBean;
5 import org.springframework.osgi.context.BundleContextAware;
6 import org.osgi.framework.BundleContext;
7 import org.osgi.framework.ServiceReference;
8 import org.osgi.framework.InvalidSyntaxException;
9 import org.osgi.framework.ServiceListener;
10 import org.osgi.framework.Filter;
11 import org.osgi.framework.ServiceEvent;
12 import static org.osgi.framework.ServiceEvent.*;
13 import org.apache.commons.lang.Validate;
14 import org.apache.commons.logging.LogFactory;
15 import org.apache.commons.logging.Log;
16 import com.atlassian.plugin.PluginException;
17
18 import java.util.Arrays;
19 import java.util.List;
20 import java.lang.reflect.Proxy;
21 import java.lang.reflect.InvocationHandler;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.InvocationTargetException;
24
25
26
27
28
29
30
31 public class HostComponentFactoryBean implements FactoryBean, BundleContextAware, InitializingBean
32 {
33 private BundleContext bundleContext;
34 private String filter;
35 private Object service;
36 private Class<?>[] interfaces;
37
38 public Object getObject() throws Exception
39 {
40 return findService();
41 }
42
43 public Class getObjectType()
44 {
45 return (findService() != null ? findService().getClass() : null);
46 }
47
48 public boolean isSingleton()
49 {
50 return true;
51 }
52
53 public void setBundleContext(BundleContext bundleContext)
54 {
55 this.bundleContext = bundleContext;
56 }
57
58
59
60
61
62
63 public void setFilter(String filter)
64 {
65 this.filter = filter;
66 }
67
68 public void setInterfaces(Class<?>[] interfaces)
69 {
70 this.interfaces = interfaces;
71 }
72
73
74
75
76
77
78
79
80 private Object findService() throws PluginException
81 {
82 return service;
83 }
84
85
86
87
88
89
90 private Object createHostComponentProxy()
91 {
92
93 return Proxy.newProxyInstance(bundleContext.getClass().getClassLoader(), interfaces, new DynamicServiceInvocationHandler(
94 bundleContext, filter));
95 }
96
97 public void afterPropertiesSet() throws Exception
98 {
99 Validate.notNull(bundleContext);
100 Validate.notNull(interfaces);
101 service = createHostComponentProxy();
102 }
103
104
105
106
107
108 static class DynamicServiceInvocationHandler implements InvocationHandler
109 {
110 private static final Log log = LogFactory.getLog(DynamicServiceInvocationHandler.class);
111 private volatile Object service;
112
113
114 DynamicServiceInvocationHandler(final BundleContext bundleContext, final String filter)
115 {
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 try
149 {
150 return method.invoke(service, objects);
151 }
152 catch (final InvocationTargetException e)
153 {
154 throw e.getTargetException();
155 }
156 }
157 }
158 }