1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.osgi.config;
18
19 import java.lang.reflect.Method;
20 import java.util.Map;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.osgi.framework.ServiceReference;
25 import org.springframework.beans.BeansException;
26 import org.springframework.beans.factory.BeanFactory;
27 import org.springframework.beans.factory.BeanFactoryAware;
28 import org.springframework.beans.factory.InitializingBean;
29 import org.springframework.osgi.service.importer.ImportedOsgiServiceProxy;
30 import org.springframework.osgi.service.importer.OsgiServiceLifecycleListener;
31 import org.springframework.osgi.util.internal.ReflectionUtils;
32 import org.springframework.util.Assert;
33 import org.springframework.util.StringUtils;
34
35 /**
36 * OsgiServiceLifecycleListener wrapper for custom beans, useful when custom
37 * methods are being used.
38 *
39 * <p/> <strong>Note:</strong> To support cyclic injection, this adapter does
40 * dependency lookup for the actual listener.
41 *
42 * @author Costin Leau
43 */
44 class OsgiServiceLifecycleListenerAdapter implements OsgiServiceLifecycleListener, InitializingBean, BeanFactoryAware {
45
46 private static final Log log = LogFactory.getLog(OsgiServiceLifecycleListenerAdapter.class);
47
48 /**
49 * Map of methods keyed by the first parameter which indicates the service
50 * type expected.
51 */
52 private Map bindMethods, unbindMethods;
53
54 /**
55 * anyName(ServiceReference reference) method signature.
56 */
57 private Method bindReference, unbindReference;
58
59 private String bindMethod, unbindMethod;
60
61 /** does the target implement the listener interface */
62 private boolean isLifecycleListener;
63
64 /** target bean factory */
65 private BeanFactory beanFactory;
66
67 /** used when dealing with a cycle */
68 private String targetBeanName;
69
70 /** target object (can be null at first when dealing with a cycle */
71 private Object target;
72
73 /** init flag */
74 private boolean initialized;
75
76
77 public void afterPropertiesSet() {
78 Assert.notNull(beanFactory);
79 Assert.isTrue(target != null || StringUtils.hasText(targetBeanName),
80 "one of 'target' or 'targetBeanName' properties has to be set");
81
82 if (target != null)
83 initialized = true;
84
85
86 initialize();
87
88 }
89
90 private void retrieveTarget() {
91 target = beanFactory.getBean(targetBeanName);
92 initialized = true;
93 }
94
95 /**
96 * Initialise adapter. Determine custom methods and do validation.
97 */
98 private void initialize() {
99
100 Class clazz = (target == null ? beanFactory.getType(targetBeanName) : target.getClass());
101
102 isLifecycleListener = OsgiServiceLifecycleListener.class.isAssignableFrom(clazz);
103 if (isLifecycleListener)
104 if (log.isDebugEnabled())
105 log.debug(clazz.getName() + " is a lifecycle listener");
106
107 bindMethods = CustomListenerAdapterUtils.determineCustomMethods(clazz, bindMethod);
108 unbindMethods = CustomListenerAdapterUtils.determineCustomMethods(clazz, unbindMethod);
109
110 if (StringUtils.hasText(bindMethod)) {
111
112 bindReference = org.springframework.util.ReflectionUtils.findMethod(clazz, bindMethod,
113 new Class[] { ServiceReference.class });
114
115 if (bindReference != null)
116 org.springframework.util.ReflectionUtils.makeAccessible(bindReference);
117 }
118 if (StringUtils.hasText(unbindMethod)) {
119 unbindReference = org.springframework.util.ReflectionUtils.findMethod(clazz, unbindMethod,
120 new Class[] { ServiceReference.class });
121
122 if (unbindReference != null)
123 org.springframework.util.ReflectionUtils.makeAccessible(unbindReference);
124 }
125
126 if (!isLifecycleListener
127 && (bindMethods.isEmpty() && unbindMethods.isEmpty() && bindReference == null && unbindReference == null))
128 throw new IllegalArgumentException("target object needs to implement "
129 + OsgiServiceLifecycleListener.class.getName()
130 + " or custom bind/unbind methods have to be specified");
131 }
132
133 /**
134 * Invoke method with signature <code>bla(ServiceReference ref)</code>.
135 *
136 * @param target
137 * @param method
138 * @param service
139 */
140 private void invokeCustomServiceReferenceMethod(Object target, Method method, Object service) {
141 if (method != null) {
142 boolean trace = log.isTraceEnabled();
143
144
145
146 if (trace)
147 log.trace("invoking listener custom method " + method);
148
149 ServiceReference ref = (service != null ? ((ImportedOsgiServiceProxy) service).getServiceReference() : null);
150
151 try {
152 ReflectionUtils.invokeMethod(method, target, new Object[] { ref });
153 }
154
155
156
157 catch (Exception ex) {
158 Exception cause = ReflectionUtils.getInvocationException(ex);
159 log.warn("custom method [" + method + "] threw exception when passing service reference ["
160 + (service != null ? service.getClass().getName() : null) + "]", cause);
161 }
162 }
163 }
164
165 public void bind(Object service, Map properties) throws Exception {
166 boolean trace = log.isTraceEnabled();
167 if (trace)
168 log.trace("invoking bind method for service " + service + " with props=" + properties);
169
170 if (!initialized)
171 retrieveTarget();
172
173
174 if (isLifecycleListener) {
175 if (trace)
176 log.trace("invoking listener interface methods");
177
178 try {
179 ((OsgiServiceLifecycleListener) target).bind(service, properties);
180 }
181 catch (Exception ex) {
182 log.warn("standard bind method on [" + target.getClass().getName() + "] threw exception", ex);
183 }
184 }
185
186 CustomListenerAdapterUtils.invokeCustomMethods(target, bindMethods, service, properties);
187 invokeCustomServiceReferenceMethod(target, bindReference, service);
188 }
189
190 public void unbind(Object service, Map properties) throws Exception {
191 boolean trace = log.isTraceEnabled();
192 if (!initialized)
193 retrieveTarget();
194
195 if (trace)
196 log.trace("invoking unbind method for service " + service + " with props=" + properties);
197
198
199 if (isLifecycleListener) {
200 if (trace)
201 log.trace("invoking listener interface methods");
202 try {
203 ((OsgiServiceLifecycleListener) target).unbind(service, properties);
204 }
205 catch (Exception ex) {
206 log.warn("standard unbind method on [" + target.getClass().getName() + "] threw exception", ex);
207 }
208 }
209
210 CustomListenerAdapterUtils.invokeCustomMethods(target, unbindMethods, service, properties);
211 invokeCustomServiceReferenceMethod(target, unbindReference, service);
212 }
213
214 /**
215 * @param bindMethod The bindMethod to set.
216 */
217 public void setBindMethod(String bindMethod) {
218 this.bindMethod = bindMethod;
219 }
220
221 /**
222 * @param unbindMethod The unbindMethod to set.
223 */
224 public void setUnbindMethod(String unbindMethod) {
225 this.unbindMethod = unbindMethod;
226 }
227
228 public void setTarget(Object target) {
229 this.target = target;
230 }
231
232 public void setTargetBeanName(String targetName) {
233 this.targetBeanName = targetName;
234 }
235
236 public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
237 this.beanFactory = beanFactory;
238 }
239 }