1
2 package org.springframework.osgi.extender.internal.dependencies.startup;
3
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.Iterator;
8 import java.util.LinkedHashMap;
9 import java.util.List;
10 import java.util.Map;
11
12 import org.apache.commons.logging.Log;
13 import org.apache.commons.logging.LogFactory;
14 import org.osgi.framework.BundleContext;
15 import org.osgi.framework.ServiceEvent;
16 import org.osgi.framework.ServiceListener;
17 import org.osgi.framework.ServiceReference;
18 import org.springframework.beans.factory.BeanFactoryUtils;
19 import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
20 import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;
21 import org.springframework.osgi.extender.OsgiServiceDependencyFactory;
22 import org.springframework.osgi.extender.event.BootstrappingDependencyEvent;
23 import org.springframework.osgi.service.importer.OsgiServiceDependency;
24 import org.springframework.osgi.service.importer.event.OsgiServiceDependencyEvent;
25 import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitEndedEvent;
26 import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitStartingEvent;
27 import org.springframework.osgi.util.OsgiListenerUtils;
28 import org.springframework.osgi.util.OsgiStringUtils;
29
30 /**
31 * ServiceListener used for tracking dependent services. Even if the
32 * ServiceListener receives event synchronously, mutable properties should be
33 * synchronized to guarantee safe publishing between threads.
34 *
35 * @author Costin Leau
36 * @author Hal Hildebrand
37 * @author Andy Piper
38 */
39 public class DependencyServiceManager {
40
41 private static final Log log = LogFactory.getLog(DependencyServiceManager.class);
42
43 protected final Map dependencies = Collections.synchronizedMap(new LinkedHashMap());
44
45 protected final Map unsatisfiedDependencies = Collections.synchronizedMap(new LinkedHashMap());
46
47 private final ContextExecutorStateAccessor contextStateAccessor;
48
49 private final BundleContext bundleContext;
50
51 private final ServiceListener listener;
52
53 private final DelegatedExecutionOsgiBundleApplicationContext context;
54
55 /**
56 * Task to execute if all dependencies are met.
57 */
58 private final Runnable executeIfDone;
59
60 /** Maximum waiting time used in events when waiting for dependencies */
61 private final long waitTime;
62
63 /** dependency factories */
64 private List dependencyFactories;
65
66
67 /**
68 * Actual ServiceListener.
69 *
70 * @author Costin Leau
71 * @author Hal Hildebrand
72 */
73 private class DependencyServiceListener implements ServiceListener {
74
75 /**
76 * Process serviceChanged events, completing context initialization if
77 * all the required dependencies are satisfied.
78 *
79 * @param serviceEvent
80 */
81 public void serviceChanged(ServiceEvent serviceEvent) {
82 boolean trace = log.isTraceEnabled();
83
84 try {
85 if (unsatisfiedDependencies.isEmpty()) {
86
87
88 if (trace) {
89 log.trace("Handling service event, but no unsatisfied dependencies exist for "
90 + context.getDisplayName());
91 }
92
93 return;
94 }
95
96 ServiceReference ref = serviceEvent.getServiceReference();
97 if (trace) {
98 log.trace("Handling service event [" + OsgiStringUtils.nullSafeToString(serviceEvent) + ":"
99 + OsgiStringUtils.nullSafeToString(ref) + "] for " + context.getDisplayName());
100 }
101
102 updateDependencies(serviceEvent);
103
104 ContextState state = contextStateAccessor.getContextState();
105
106
107 if (state.isResolved()) {
108 deregister();
109 return;
110 }
111
112
113 if (unsatisfiedDependencies.isEmpty()) {
114 deregister();
115
116 log.info("No unsatisfied OSGi service dependencies; completing initialization for "
117 + context.getDisplayName());
118
119
120
121
122
123 executeIfDone.run();
124 }
125 }
126 catch (Throwable e) {
127
128 log.error("Exception during dependency processing for " + context.getDisplayName(), e);
129 }
130 }
131
132 private void updateDependencies(ServiceEvent serviceEvent) {
133 boolean trace = log.isTraceEnabled();
134 boolean debug = log.isDebugEnabled();
135
136 for (Iterator i = dependencies.keySet().iterator(); i.hasNext();) {
137 MandatoryServiceDependency dependency = (MandatoryServiceDependency) i.next();
138
139
140 if (dependency.matches(serviceEvent)) {
141 switch (serviceEvent.getType()) {
142
143 case ServiceEvent.REGISTERED:
144 case ServiceEvent.MODIFIED:
145 unsatisfiedDependencies.remove(dependency);
146 if (debug) {
147 log.debug("Found service for " + context.getDisplayName() + "; eliminating "
148 + dependency + ", remaining [" + unsatisfiedDependencies + "]");
149 }
150
151 sendDependencySatisfiedEvent(dependency);
152 break;
153
154 case ServiceEvent.UNREGISTERING:
155 unsatisfiedDependencies.put(dependency, dependency.getBeanName());
156 if (debug) {
157 log.debug("Service unregistered; adding " + dependency);
158 }
159 sendDependencyUnsatisfiedEvent(dependency);
160 break;
161 default:
162 if (debug) {
163 log.debug("Unknown service event type for: " + dependency);
164 }
165 break;
166 }
167 }
168 else {
169 if (trace) {
170 log.trace(dependency + " does not match: "
171 + OsgiStringUtils.nullSafeToString(serviceEvent.getServiceReference()));
172 }
173 }
174 }
175 }
176 }
177
178
179 /**
180 * Create a dependency manager, indicating the executor bound to, the
181 * context that contains the dependencies and the task to execute if all
182 * dependencies are met.
183 *
184 * @param executor
185 * @param context
186 * @param executeIfDone
187 */
188 public DependencyServiceManager(ContextExecutorStateAccessor executor,
189 DelegatedExecutionOsgiBundleApplicationContext context, List dependencyFactories, Runnable executeIfDone,
190 long maxWaitTime) {
191 this.contextStateAccessor = executor;
192 this.context = context;
193 this.dependencyFactories = new ArrayList(8);
194
195 if (dependencyFactories != null)
196 this.dependencyFactories.addAll(dependencyFactories);
197
198 this.waitTime = maxWaitTime;
199 this.bundleContext = context.getBundleContext();
200 this.listener = new DependencyServiceListener();
201
202 this.executeIfDone = executeIfDone;
203 }
204
205 protected void findServiceDependencies() throws Exception {
206 Thread currentThread = Thread.currentThread();
207 ClassLoader oldTCCL = currentThread.getContextClassLoader();
208
209 boolean debug = log.isDebugEnabled();
210 boolean trace = log.isTraceEnabled();
211
212 try {
213 currentThread.setContextClassLoader(context.getClassLoader());
214
215 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
216
217 if (trace)
218 log.trace("Looking for dependency factories inside bean factory [" + beanFactory.toString() + "]");
219
220 Map localFactories = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory,
221 OsgiServiceDependencyFactory.class, true, false);
222
223 if (debug)
224 log.debug("Discovered local dependency factories: " + localFactories.keySet());
225
226 dependencyFactories.addAll(localFactories.values());
227
228 for (Iterator iterator = dependencyFactories.iterator(); iterator.hasNext();) {
229 OsgiServiceDependencyFactory dependencyFactory = (OsgiServiceDependencyFactory) iterator.next();
230 Collection discoveredDependencies = null;
231
232 try {
233 discoveredDependencies = dependencyFactory.getServiceDependencies(bundleContext, beanFactory);
234 }
235 catch (Exception ex) {
236 log.warn("Dependency factory " + dependencyFactory
237 + " threw exception while detecting dependencies for beanFactory " + beanFactory + " in "
238 + context.getDisplayName(), ex);
239 throw ex;
240 }
241
242 if (discoveredDependencies != null)
243 for (Iterator dependencyIterator = discoveredDependencies.iterator(); dependencyIterator.hasNext();) {
244 OsgiServiceDependency dependency = (OsgiServiceDependency) dependencyIterator.next();
245 MandatoryServiceDependency msd = new MandatoryServiceDependency(bundleContext, dependency);
246 dependencies.put(msd, dependency.getBeanName());
247
248 if (!msd.isServicePresent()) {
249 log.info("Adding OSGi service dependency for importer [" + msd.getBeanName()
250 + "] matching OSGi filter [" + msd.filterAsString + "]");
251 unsatisfiedDependencies.put(msd, dependency.getBeanName());
252 }
253 }
254 }
255 }
256 finally {
257 currentThread.setContextClassLoader(oldTCCL);
258 }
259
260 if (debug) {
261 log.debug(dependencies.size() + " OSGi service dependencies, " + unsatisfiedDependencies.size()
262 + " unsatisfied (for beans " + unsatisfiedDependencies.values() + ") in "
263 + context.getDisplayName());
264 }
265
266 if (!unsatisfiedDependencies.isEmpty()) {
267 log.info(context.getDisplayName() + " is waiting for unsatisfied dependencies ["
268 + unsatisfiedDependencies.values() + "]");
269 }
270 if (log.isTraceEnabled()) {
271 log.trace("Total OSGi service dependencies beans " + dependencies.values());
272 log.trace("Unsatified OSGi service dependencies beans " + unsatisfiedDependencies.values());
273 }
274 }
275
276 protected boolean isSatisfied() {
277 return unsatisfiedDependencies.isEmpty();
278 }
279
280 public Map getUnsatisfiedDependencies() {
281 return unsatisfiedDependencies;
282 }
283
284 protected void register() {
285 String filter = createDependencyFilter();
286 if (log.isDebugEnabled()) {
287 log.debug(context.getDisplayName() + " has registered service dependency dependencyDetector with filter: "
288 + filter);
289 }
290
291
292 sendInitialDependencyEvents();
293 OsgiListenerUtils.addServiceListener(bundleContext, listener, filter);
294 }
295
296 /**
297 * Look at the existing dependencies and create an appropriate filter. This
298 * method concatenates the filters into one.
299 *
300 * @return
301 */
302 protected String createDependencyFilter() {
303 boolean multiple = unsatisfiedDependencies.size() > 1;
304 StringBuffer sb = new StringBuffer(100 * unsatisfiedDependencies.size());
305 if (multiple) {
306 sb.append("(|");
307 }
308 for (Iterator i = unsatisfiedDependencies.keySet().iterator(); i.hasNext();) {
309 sb.append(((MandatoryServiceDependency) i.next()).filterAsString);
310 }
311 if (multiple) {
312 sb.append(')');
313 }
314 return sb.toString();
315 }
316
317 protected void deregister() {
318 if (log.isDebugEnabled()) {
319 log.debug("Deregistering service dependency dependencyDetector for " + context.getDisplayName());
320 }
321
322 OsgiListenerUtils.removeServiceListener(bundleContext, listener);
323 }
324
325
326 private void sendInitialDependencyEvents() {
327 for (Iterator iterator = unsatisfiedDependencies.keySet().iterator(); iterator.hasNext();) {
328 MandatoryServiceDependency entry = (MandatoryServiceDependency) iterator.next();
329 OsgiServiceDependencyEvent nestedEvent = new OsgiServiceDependencyWaitStartingEvent(context,
330 entry.getServiceDependency(), waitTime);
331 BootstrappingDependencyEvent dependencyEvent = new BootstrappingDependencyEvent(context,
332 context.getBundle(), nestedEvent);
333 publishEvent(dependencyEvent);
334 }
335 }
336
337 private void sendDependencyUnsatisfiedEvent(MandatoryServiceDependency dependency) {
338 OsgiServiceDependencyEvent nestedEvent = new OsgiServiceDependencyWaitStartingEvent(context,
339 dependency.getServiceDependency(), waitTime);
340 BootstrappingDependencyEvent dependencyEvent = new BootstrappingDependencyEvent(context, context.getBundle(),
341 nestedEvent);
342 publishEvent(dependencyEvent);
343 }
344
345 private void sendDependencySatisfiedEvent(MandatoryServiceDependency dependency) {
346 OsgiServiceDependencyEvent nestedEvent = new OsgiServiceDependencyWaitEndedEvent(context,
347 dependency.getServiceDependency(), waitTime);
348 BootstrappingDependencyEvent dependencyEvent = new BootstrappingDependencyEvent(context, context.getBundle(),
349 nestedEvent);
350 publishEvent(dependencyEvent);
351 }
352
353 private void publishEvent(BootstrappingDependencyEvent dependencyEvent) {
354 this.contextStateAccessor.getEventMulticaster().multicastEvent(dependencyEvent);
355 }
356 }