View Javadoc

1   package org.springframework.osgi.extender.internal.dependencies.startup;
2   
3   import java.security.AccessControlContext;
4   import java.security.AccessController;
5   import java.security.PrivilegedAction;
6   import java.security.PrivilegedExceptionAction;
7   import java.util.ArrayList;
8   import java.util.Collection;
9   import java.util.Collections;
10  import java.util.Iterator;
11  import java.util.LinkedHashMap;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.Set;
15  
16  import org.apache.commons.logging.Log;
17  import org.apache.commons.logging.LogFactory;
18  import org.osgi.framework.BundleContext;
19  import org.osgi.framework.Filter;
20  import org.osgi.framework.ServiceEvent;
21  import org.osgi.framework.ServiceListener;
22  import org.osgi.framework.ServiceReference;
23  import org.springframework.beans.factory.BeanFactoryUtils;
24  import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
25  import org.springframework.beans.factory.config.ConfigurableBeanFactory;
26  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
27  import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;
28  import org.springframework.osgi.context.event.OsgiBundleApplicationContextEvent;
29  import org.springframework.osgi.extender.OsgiServiceDependencyFactory;
30  import org.springframework.osgi.extender.event.BootstrappingDependenciesEvent;
31  import org.springframework.osgi.extender.event.BootstrappingDependencyEvent;
32  import org.springframework.osgi.extender.internal.util.PrivilegedUtils;
33  import org.springframework.osgi.service.importer.OsgiServiceDependency;
34  import org.springframework.osgi.service.importer.event.OsgiServiceDependencyEvent;
35  import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitEndedEvent;
36  import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitStartingEvent;
37  import org.springframework.osgi.util.OsgiFilterUtils;
38  import org.springframework.osgi.util.OsgiListenerUtils;
39  import org.springframework.osgi.util.OsgiStringUtils;
40  
41  /**
42   * ServiceListener used for tracking dependent services. Even if the ServiceListener receives event synchronously,
43   * mutable properties should be synchronized to guarantee safe publishing between threads.
44   * 
45   * @author Costin Leau
46   * @author Hal Hildebrand
47   * @author Andy Piper
48   */
49  public class DependencyServiceManager {
50  
51  	private static final Log log = LogFactory.getLog(DependencyServiceManager.class);
52  
53  	protected final Map<MandatoryServiceDependency, String> dependencies =
54  			Collections.synchronizedMap(new LinkedHashMap<MandatoryServiceDependency, String>());
55  
56  	protected final Map<MandatoryServiceDependency, String> unsatisfiedDependencies =
57  			Collections.synchronizedMap(new LinkedHashMap<MandatoryServiceDependency, String>());
58  
59  	private final ContextExecutorAccessor contextStateAccessor;
60  
61  	private final BundleContext bundleContext;
62  
63  	private final ServiceListener listener;
64  
65  	private final DelegatedExecutionOsgiBundleApplicationContext context;
66  
67  	/**
68  	 * Task to execute if all dependencies are met.
69  	 */
70  	private final Runnable executeIfDone;
71  
72  	/** Maximum waiting time used in events when waiting for dependencies */
73  	private final long waitTime;
74  
75  	/** dependency factories */
76  	private List<OsgiServiceDependencyFactory> dependencyFactories;
77  
78  	/**
79  	 * Actual ServiceListener.
80  	 * 
81  	 * @author Costin Leau
82  	 * @author Hal Hildebrand
83  	 */
84  	private class DependencyServiceListener implements ServiceListener {
85  
86  		/**
87  		 * Process serviceChanged events, completing context initialization if all the required dependencies are
88  		 * satisfied.
89  		 * 
90  		 * @param serviceEvent
91  		 */
92  		public void serviceChanged(ServiceEvent serviceEvent) {
93  			boolean trace = log.isTraceEnabled();
94  
95  			try {
96  				if (unsatisfiedDependencies.isEmpty()) {
97  
98  					// already completed but likely called due to threading
99  					if (trace) {
100 						log.trace("Handling service event, but no unsatisfied dependencies exist for "
101 								+ context.getDisplayName());
102 					}
103 
104 					return;
105 				}
106 
107 				ServiceReference ref = serviceEvent.getServiceReference();
108 				if (trace) {
109 					log.trace("Handling service event [" + OsgiStringUtils.nullSafeToString(serviceEvent) + ":"
110 							+ OsgiStringUtils.nullSafeToString(ref) + "] for " + context.getDisplayName());
111 				}
112 
113 				updateDependencies(serviceEvent);
114 
115 				ContextState state = contextStateAccessor.getContextState();
116 
117 				// already resolved (closed or timed-out)
118 				if (state.isResolved()) {
119 					deregister();
120 					return;
121 				}
122 
123 				// Good to go!
124 				if (unsatisfiedDependencies.isEmpty()) {
125 					deregister();
126 					// context.listener = null;
127 					log.info("No unsatisfied OSGi service dependencies; completing initialization for "
128 							+ context.getDisplayName());
129 
130 					// execute task to complete initialization
131 					// NOTE: the runnable should be able to delegate any long
132 					// process to a
133 					// different thread.
134 					executeIfDone.run();
135 				}
136 			} catch (Throwable th) {
137 				// frameworks will simply not log exception for event handlers
138 				log.error("Exception during dependency processing for " + context.getDisplayName(), th);
139 				contextStateAccessor.fail(th);
140 			}
141 		}
142 
143 		private void updateDependencies(ServiceEvent serviceEvent) {
144 			boolean trace = log.isTraceEnabled();
145 			boolean debug = log.isDebugEnabled();
146 
147 			String referenceToString = null;
148 			String contextToString = null;
149 
150 			if (debug) {
151 				referenceToString = OsgiStringUtils.nullSafeToString(serviceEvent.getServiceReference());
152 				contextToString = context.getDisplayName();
153 			}
154 
155 			for (MandatoryServiceDependency dependency : dependencies.keySet()) {
156 				// check all dependencies (there might be multiple imports for the same service)
157 				if (dependency.matches(serviceEvent)) {
158 					if (trace) {
159 						log.trace(dependency + " matched: " + referenceToString);
160 					}
161 
162 					switch (serviceEvent.getType()) {
163 
164 					case ServiceEvent.REGISTERED:
165 					case ServiceEvent.MODIFIED:
166 						dependency.increment();
167 						if (unsatisfiedDependencies.remove(dependency) != null) {
168 							if (debug) {
169 								log.debug("Registered dependency for " + contextToString + "; eliminating "
170 										+ dependency + ", remaining [" + unsatisfiedDependencies + "]");
171 							}
172 
173 							sendDependencySatisfiedEvent(dependency);
174 							sendBootstrappingDependenciesEvent(unsatisfiedDependencies.keySet());
175 						} else {
176 							if (debug) {
177 								log.debug("Increasing the number of matching services for " + contextToString + "; "
178 										+ dependency + ", remaining [" + unsatisfiedDependencies + "]");
179 							}
180 						}
181 
182 						break;
183 
184 					case ServiceEvent.UNREGISTERING:
185 						int count = dependency.decrement();
186 						if (count == 0) {
187 							unsatisfiedDependencies.put(dependency, dependency.getBeanName());
188 							if (debug) {
189 								log.debug("Unregistered dependency for " + contextToString + " adding " + dependency
190 										+ "; total unsatisfied [" + unsatisfiedDependencies + "]");
191 							}
192 
193 							sendDependencyUnsatisfiedEvent(dependency);
194 							sendBootstrappingDependenciesEvent(unsatisfiedDependencies.keySet());
195 						} else {
196 							if (debug) {
197 								log.debug("Decreasing the number of matching services for " + contextToString + "; "
198 										+ dependency + " still has " + count + " matches left");
199 							}
200 						}
201 						break;
202 					default: // do nothing
203 						if (debug) {
204 							log.debug("Unknown service event type for: " + dependency);
205 						}
206 						break;
207 					}
208 				} else {
209 					if (trace) {
210 						log.trace(dependency + " does not match: " + referenceToString);
211 					}
212 				}
213 			}
214 		}
215 	}
216 
217 	/**
218 	 * Create a dependency manager, indicating the executor bound to, the context that contains the dependencies and the
219 	 * task to execute if all dependencies are met.
220 	 * 
221 	 * @param executor
222 	 * @param context
223 	 * @param executeIfDone
224 	 */
225 	public DependencyServiceManager(ContextExecutorAccessor executor,
226 			DelegatedExecutionOsgiBundleApplicationContext context,
227 			List<OsgiServiceDependencyFactory> dependencyFactories, Runnable executeIfDone, long maxWaitTime) {
228 		this.contextStateAccessor = executor;
229 		this.context = context;
230 		this.dependencyFactories = new ArrayList<OsgiServiceDependencyFactory>(8);
231 
232 		if (dependencyFactories != null)
233 			this.dependencyFactories.addAll(dependencyFactories);
234 
235 		this.waitTime = maxWaitTime;
236 		this.bundleContext = context.getBundleContext();
237 		this.listener = new DependencyServiceListener();
238 
239 		this.executeIfDone = executeIfDone;
240 	}
241 
242 	protected void findServiceDependencies() throws Exception {
243 		try {
244 			if (System.getSecurityManager() != null) {
245 				final AccessControlContext acc = getAcc();
246 
247 				PrivilegedUtils.executeWithCustomTCCL(context.getClassLoader(),
248 						new PrivilegedUtils.UnprivilegedThrowableExecution<Object>() {
249 							public Object run() throws Throwable {
250 								AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
251 									public Object run() throws Exception {
252 										doFindDependencies();
253 										return null;
254 									}
255 								}, acc);
256 								return null;
257 							}
258 						});
259 			} else {
260 				doFindDependencies();
261 			}
262 		} catch (Throwable th) {
263 			if (th instanceof Exception)
264 				throw ((Exception) th);
265 			throw (Error) th;
266 		}
267 
268 		if (log.isDebugEnabled()) {
269 			log.debug(dependencies.size() + " OSGi service dependencies, " + unsatisfiedDependencies.size()
270 					+ " unsatisfied (for beans " + unsatisfiedDependencies.values() + ") in "
271 					+ context.getDisplayName());
272 		}
273 
274 		if (!unsatisfiedDependencies.isEmpty()) {
275 			log.info(context.getDisplayName() + " is waiting for unsatisfied dependencies ["
276 					+ unsatisfiedDependencies.values() + "]");
277 		}
278 		if (log.isTraceEnabled()) {
279 			log.trace("Total OSGi service dependencies beans " + dependencies.values());
280 			log.trace("Unsatified OSGi service dependencies beans " + unsatisfiedDependencies.values());
281 		}
282 	}
283 
284 	private void doFindDependencies() throws Exception {
285 		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
286 		boolean debug = log.isDebugEnabled();
287 		boolean trace = log.isTraceEnabled();
288 
289 		if (trace)
290 			log.trace("Looking for dependency factories inside bean factory [" + beanFactory.toString() + "]");
291 
292 		Map<String, OsgiServiceDependencyFactory> localFactories =
293 				BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, OsgiServiceDependencyFactory.class, true,
294 						false);
295 
296 		if (trace)
297 			log.trace("Discovered local dependency factories: " + localFactories.keySet());
298 
299 		dependencyFactories.addAll(localFactories.values());
300 
301 		for (Iterator<OsgiServiceDependencyFactory> iterator = dependencyFactories.iterator(); iterator.hasNext();) {
302 			OsgiServiceDependencyFactory dependencyFactory = iterator.next();
303 			Collection<OsgiServiceDependency> discoveredDependencies = null;
304 
305 			if (trace) {
306 				log.trace("Interogating dependency factory " + dependencyFactory);
307 			}
308 			try {
309 				discoveredDependencies = dependencyFactory.getServiceDependencies(bundleContext, beanFactory);
310 			} catch (Exception ex) {
311 				log.warn("Dependency factory " + dependencyFactory
312 						+ " threw exception while detecting dependencies for beanFactory " + beanFactory + " in "
313 						+ context.getDisplayName(), ex);
314 				throw ex;
315 			}
316 			// add the dependencies one by one
317 			if (discoveredDependencies != null)
318 				for (OsgiServiceDependency dependency : discoveredDependencies) {
319 					if (dependency.isMandatory()) {
320 						MandatoryServiceDependency msd = new MandatoryServiceDependency(bundleContext, dependency);
321 						dependencies.put(msd, dependency.getBeanName());
322 
323 						if (!msd.isServicePresent()) {
324 							log.info("Adding OSGi service dependency for importer [" + msd.getBeanName()
325 									+ "] matching OSGi filter [" + msd.filterAsString + "]");
326 							unsatisfiedDependencies.put(msd, dependency.getBeanName());
327 						} else {
328 							if (debug)
329 								log.debug("OSGi service dependency for importer [" + msd.getBeanName()
330 										+ "] is already satisfied");
331 						}
332 					}
333 				}
334 		}
335 	}
336 
337 	protected boolean isSatisfied() {
338 		return unsatisfiedDependencies.isEmpty();
339 	}
340 
341 	public Map<MandatoryServiceDependency, String> getUnsatisfiedDependencies() {
342 		return unsatisfiedDependencies;
343 	}
344 
345 	protected void register() {
346 		final String filter = createDependencyFilter();
347 		if (log.isDebugEnabled()) {
348 			log.debug(context.getDisplayName() + " has registered service dependency dependencyDetector with filter: "
349 					+ filter);
350 		}
351 
352 		// send dependency event before registering the filter
353 		sendInitialBootstrappingEvents(unsatisfiedDependencies.keySet());
354 
355 		if (System.getSecurityManager() != null) {
356 			AccessControlContext acc = getAcc();
357 			AccessController.doPrivileged(new PrivilegedAction<Object>() {
358 				public Object run() {
359 					OsgiListenerUtils.addServiceListener(bundleContext, listener, filter);
360 					return null;
361 				}
362 			}, acc);
363 		} else {
364 			OsgiListenerUtils.addServiceListener(bundleContext, listener, filter);
365 		}
366 	}
367 
368 	/**
369 	 * Look at all dependencies and create an appropriate filter. This method concatenates the filters into one. Note
370 	 * that not just unsatisfied dependencies are considered since their number can grow.
371 	 * 
372 	 * @return
373 	 */
374 	private String createDependencyFilter() {
375 		return createDependencyFilter(dependencies.keySet());
376 	}
377 
378 	String createUnsatisfiedDependencyFilter() {
379 		return createDependencyFilter(unsatisfiedDependencies.keySet());
380 	}
381 
382 	private String createDependencyFilter(Collection<MandatoryServiceDependency> dependencies) {
383 		if (dependencies.isEmpty()) {
384 			return null;
385 		}
386 
387 		boolean multiple = dependencies.size() > 1;
388 		StringBuilder sb = new StringBuilder(dependencies.size() << 7);
389 		if (multiple) {
390 			sb.append("(|");
391 		}
392 		for (MandatoryServiceDependency dependency : dependencies) {
393 			sb.append(dependency.filterAsString);
394 		}
395 		if (multiple) {
396 			sb.append(')');
397 		}
398 
399 		String filter = sb.toString();
400 
401 		return filter;
402 	}
403 
404 	protected void deregister() {
405 		if (log.isDebugEnabled()) {
406 			log.debug("Deregistering service dependency dependencyDetector for " + context.getDisplayName());
407 		}
408 
409 		OsgiListenerUtils.removeServiceListener(bundleContext, listener);
410 	}
411 
412 	List<OsgiServiceDependencyEvent> getUnsatisfiedDependenciesAsEvents() {
413 		return getUnsatisfiedDependenciesAsEvents(unsatisfiedDependencies.keySet());
414 	}
415 
416 	private List<OsgiServiceDependencyEvent> getUnsatisfiedDependenciesAsEvents(
417 			Collection<MandatoryServiceDependency> deps) {
418 		List<OsgiServiceDependencyEvent> dependencies = new ArrayList<OsgiServiceDependencyEvent>(deps.size());
419 
420 		for (MandatoryServiceDependency entry : deps) {
421 			OsgiServiceDependencyEvent nestedEvent =
422 					new OsgiServiceDependencyWaitStartingEvent(context, entry.getServiceDependency(), waitTime);
423 			dependencies.add(nestedEvent);
424 		}
425 
426 		return Collections.unmodifiableList(dependencies);
427 
428 	}
429 
430 	// event notification
431 	private void sendDependencyUnsatisfiedEvent(MandatoryServiceDependency dependency) {
432 		OsgiServiceDependencyEvent nestedEvent =
433 				new OsgiServiceDependencyWaitStartingEvent(context, dependency.getServiceDependency(), waitTime);
434 		BootstrappingDependencyEvent dependencyEvent =
435 				new BootstrappingDependencyEvent(context, context.getBundle(), nestedEvent);
436 		publishEvent(dependencyEvent);
437 	}
438 
439 	private void sendDependencySatisfiedEvent(MandatoryServiceDependency dependency) {
440 		OsgiServiceDependencyEvent nestedEvent =
441 				new OsgiServiceDependencyWaitEndedEvent(context, dependency.getServiceDependency(), waitTime);
442 		BootstrappingDependencyEvent dependencyEvent =
443 				new BootstrappingDependencyEvent(context, context.getBundle(), nestedEvent);
444 		publishEvent(dependencyEvent);
445 	}
446 
447 	private void sendInitialBootstrappingEvents(Set<MandatoryServiceDependency> deps) {
448 		// send the fine grained event
449 		List<OsgiServiceDependencyEvent> events = getUnsatisfiedDependenciesAsEvents(deps);
450 		for (OsgiServiceDependencyEvent nestedEvent : events) {
451 			BootstrappingDependencyEvent dependencyEvent =
452 					new BootstrappingDependencyEvent(context, context.getBundle(), nestedEvent);
453 			publishEvent(dependencyEvent);
454 		}
455 
456 		// followed by the composite one
457 		String filterAsString = createDependencyFilter(deps);
458 		Filter filter = (filterAsString != null ? OsgiFilterUtils.createFilter(filterAsString) : null);
459 		BootstrappingDependenciesEvent event =
460 				new BootstrappingDependenciesEvent(context, context.getBundle(), events, filter, waitTime);
461 
462 		publishEvent(event);
463 	}
464 
465 	private void sendBootstrappingDependenciesEvent(Set<MandatoryServiceDependency> deps) {
466 		List<OsgiServiceDependencyEvent> events = getUnsatisfiedDependenciesAsEvents(deps);
467 		String filterAsString = createDependencyFilter(deps);
468 		Filter filter = (filterAsString != null ? OsgiFilterUtils.createFilter(filterAsString) : null);
469 		BootstrappingDependenciesEvent event =
470 				new BootstrappingDependenciesEvent(context, context.getBundle(), events, filter, waitTime);
471 
472 		publishEvent(event);
473 	}
474 
475 	private void publishEvent(OsgiBundleApplicationContextEvent dependencyEvent) {
476 		this.contextStateAccessor.getEventMulticaster().multicastEvent(dependencyEvent);
477 	}
478 
479 	private AccessControlContext getAcc() {
480 		AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
481 		if (beanFactory instanceof ConfigurableBeanFactory) {
482 			return ((ConfigurableBeanFactory) beanFactory).getAccessControlContext();
483 		}
484 		return null;
485 	}
486 }