View Javadoc

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  					// already completed but likely called due to threading
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 				// already resolved (closed or timed-out)
107 				if (state.isResolved()) {
108 					deregister();
109 					return;
110 				}
111 
112 				// Good to go!
113 				if (unsatisfiedDependencies.isEmpty()) {
114 					deregister();
115 					// context.listener = null;
116 					log.info("No unsatisfied OSGi service dependencies; completing initialization for "
117 							+ context.getDisplayName());
118 
119 					// execute task to complete initialization
120 					// NOTE: the runnable should be able to delegate any long
121 					// process to a
122 					// different thread.
123 					executeIfDone.run();
124 				}
125 			}
126 			catch (Throwable e) {
127 				// frameworks will simply not log exception for event handlers
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 				// check if there is a match on the service
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: // do nothing
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 				// add the dependencies one by one
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 		// send dependency event before registering the filter
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 	// event notification
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 }