1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.springframework.osgi.extender.internal.activator;
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Timer;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.osgi.framework.Bundle;
30 import org.osgi.framework.BundleActivator;
31 import org.osgi.framework.BundleContext;
32 import org.osgi.framework.BundleEvent;
33 import org.osgi.framework.ServiceReference;
34 import org.osgi.framework.SynchronousBundleListener;
35 import org.osgi.framework.Version;
36 import org.osgi.service.packageadmin.PackageAdmin;
37 import org.springframework.beans.factory.DisposableBean;
38 import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
39 import org.springframework.core.CollectionFactory;
40 import org.springframework.core.task.SyncTaskExecutor;
41 import org.springframework.core.task.TaskExecutor;
42 import org.springframework.osgi.context.ConfigurableOsgiBundleApplicationContext;
43 import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;
44 import org.springframework.osgi.context.event.OsgiBundleApplicationContextEventMulticaster;
45 import org.springframework.osgi.context.event.OsgiBundleApplicationContextListener;
46 import org.springframework.osgi.extender.OsgiApplicationContextCreator;
47 import org.springframework.osgi.extender.internal.dependencies.shutdown.ComparatorServiceDependencySorter;
48 import org.springframework.osgi.extender.internal.dependencies.shutdown.ServiceDependencySorter;
49 import org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor;
50 import org.springframework.osgi.extender.internal.support.ExtenderConfiguration;
51 import org.springframework.osgi.extender.internal.support.NamespaceManager;
52 import org.springframework.osgi.extender.internal.support.OsgiAnnotationPostProcessor;
53 import org.springframework.osgi.extender.internal.support.OsgiBeanFactoryPostProcessorAdapter;
54 import org.springframework.osgi.extender.internal.util.concurrent.Counter;
55 import org.springframework.osgi.extender.internal.util.concurrent.RunnableTimedExecution;
56 import org.springframework.osgi.extender.support.ApplicationContextConfiguration;
57 import org.springframework.osgi.extender.support.internal.ConfigUtils;
58 import org.springframework.osgi.service.importer.support.Cardinality;
59 import org.springframework.osgi.service.importer.support.CollectionType;
60 import org.springframework.osgi.service.importer.support.OsgiServiceCollectionProxyFactoryBean;
61 import org.springframework.osgi.util.OsgiBundleUtils;
62 import org.springframework.osgi.util.OsgiStringUtils;
63 import org.springframework.util.Assert;
64
65 /**
66 * Osgi Extender that bootstraps 'Spring powered bundles'.
67 *
68 * <p/> The class listens to bundle events and manages the creation and
69 * destruction of application contexts for bundles that have one or both of:
70 * <ul>
71 * <li> A manifest header entry Spring-Context
72 * <li> XML files in META-INF/spring folder
73 * </ul>
74 *
75 * <p/> The extender also discovers any Spring namespace/schema handlers in
76 * resolved bundles and makes them available through a dedicated OSGi service.
77 *
78 * <p/> The extender behaviour can be customized by attaching fragments to the
79 * extender bundle. On startup, the extender will look for
80 * <code>META-INF/spring/*.xml</code> files and merge them into an application
81 * context. From the resulting context, the context will look for beans with
82 * predefined names to determine its configuration. The current version
83 * recognises the following bean names:
84 *
85 * <table border="1">
86 * <tr>
87 * <th>Bean Name</th>
88 * <th>Bean Type</th>
89 * <th>Description</th>
90 * </tr>
91 * <tr>
92 * <td><code>taskExecutor</code></td>
93 * <td><code>org.springframework.core.task.TaskExecutor</code></td>
94 * <td>Task executor used for creating the discovered application contexts.</td>
95 * </tr>
96 * <tr>
97 * <td><code>shutdownTaskExecutor</code></td>
98 * <td><code>org.springframework.core.task.TaskExecutor</code></td>
99 * <td>Task executor used for shutting down various application contexts.</td>
100 * </tr>
101 * <tr>
102 * <td><code>extenderProperties</code></td>
103 * <td><code>java.util.Properties</code></td>
104 * <td>Various properties for configuring the extender behaviour (see below)</td>
105 * </tr>
106 * </table>
107 *
108 * <p/> <code>extenderProperties</code> recognises the following properties:
109 *
110 * <table border="1">
111 * <tr>
112 * <th>Name</th>
113 * <th>Type</th>
114 * <th>Description</th>
115 * </tr>
116 * <tr>
117 * <td><code>shutdown.wait.time</code></td>
118 * <td>Number</td>
119 * <td>The amount of time the extender will wait for each application context
120 * to shutdown gracefully. Expressed in milliseconds.</td>
121 * </tr>
122 * <tr>
123 * <td><code>process.annotations</code></td>
124 * <td>Boolean</td>
125 * <td>Whether or not, the extender will process SpringOSGi annotations.</td>
126 * </tr>
127 * </table>
128 *
129 * <p/> Note: The extender configuration context is created during the bundle
130 * activation (a synchronous OSGi lifecycle callback) and should contain only
131 * simple bean definitions that will not delay context initialisation.
132 * </p>
133 *
134 * @author Bill Gallagher
135 * @author Andy Piper
136 * @author Hal Hildebrand
137 * @author Adrian Colyer
138 * @author Costin Leau
139 */
140 public class ContextLoaderListener implements BundleActivator {
141
142 /**
143 * Common base class for {@link ContextLoaderListener} listeners.
144 *
145 * @author Costin Leau
146 */
147 private abstract class BaseListener implements SynchronousBundleListener {
148
149 /**
150 * A bundle has been started, stopped, resolved, or unresolved. This
151 * method is a synchronous callback, do not do any long-running work in
152 * this thread.
153 *
154 * @see org.osgi.framework.SynchronousBundleListener#bundleChanged
155 */
156 public void bundleChanged(BundleEvent event) {
157
158 boolean trace = log.isTraceEnabled();
159
160
161 synchronized (monitor) {
162 if (isClosed) {
163 if (trace)
164 log.trace("Listener is closed; events are being ignored");
165 return;
166 }
167 }
168 if (trace) {
169 log.debug("Processing bundle event [" + OsgiStringUtils.nullSafeToString(event) + "] for bundle ["
170 + OsgiStringUtils.nullSafeSymbolicName(event.getBundle()) + "]");
171 }
172 try {
173 handleEvent(event);
174 }
175 catch (Exception ex) {
176
177 log.warn("Got exception while handling event " + event, ex);
178 }
179 }
180
181 protected abstract void handleEvent(BundleEvent event);
182 }
183
184 /**
185 * Bundle listener used for detecting namespace handler/resolvers. Exists as
186 * a separate listener so that it can be registered early to avoid race
187 * conditions with bundles in INSTALLING state but still to avoid premature
188 * context creation before the Spring {@link ContextLoaderListener} is not
189 * fully initialized.
190 *
191 * @author Costin Leau
192 */
193 private class NamespaceBundleLister extends BaseListener {
194
195 protected void handleEvent(BundleEvent event) {
196
197 Bundle bundle = event.getBundle();
198
199 switch (event.getType()) {
200 case BundleEvent.RESOLVED: {
201 maybeAddNamespaceHandlerFor(bundle);
202 break;
203 }
204 case BundleEvent.UNRESOLVED: {
205 maybeRemoveNameSpaceHandlerFor(bundle);
206 break;
207 }
208 default:
209 break;
210 }
211 }
212 }
213
214 /**
215 * Bundle listener used for context creation/destruction.
216 */
217 private class ContextBundleListener extends BaseListener {
218
219 protected void handleEvent(BundleEvent event) {
220
221 Bundle bundle = event.getBundle();
222
223
224 if (bundle.getBundleId() == bundleId) {
225 return;
226 }
227
228 switch (event.getType()) {
229 case BundleEvent.STARTED: {
230 maybeCreateApplicationContextFor(bundle);
231 break;
232 }
233 case BundleEvent.STOPPING: {
234 if (OsgiBundleUtils.isSystemBundle(bundle)) {
235 if (log.isDebugEnabled()) {
236 log.debug("System bundle stopping");
237 }
238
239
240 shutdown();
241 }
242 else {
243 maybeCloseApplicationContextFor(bundle);
244 }
245 break;
246 }
247 default:
248 break;
249 }
250 }
251 }
252
253
254 private static final Log log = LogFactory.getLog(ContextLoaderListener.class);
255
256 /** annotation processing system property */
257 private static final String AUTO_ANNOTATION_PROCESSING = "org.springframework.osgi.extender.annotation.auto.processing";
258
259
260 private Timer timer = new Timer(true);
261
262 /** extender bundle id */
263 private long bundleId;
264
265 /** extender configuration */
266 private ExtenderConfiguration extenderConfiguration;
267
268 /**
269 * The contexts we are currently managing. Keys are bundle ids, values are
270 * ServiceDependentOsgiApplicationContexts for the application context
271 */
272 private final Map managedContexts;
273
274 /** Task executor used for bootstraping the Spring contexts in async mode */
275 private TaskExecutor taskExecutor;
276
277 /** ApplicationContext Creator */
278 private OsgiApplicationContextCreator contextCreator;
279
280 /** BFPP list */
281 private List postProcessors;
282
283 /**
284 * Task executor which uses the same thread for running tasks. Used when
285 * doing a synchronous wait-for-dependencies.
286 */
287 private TaskExecutor sameThreadTaskExecutor = new SyncTaskExecutor();
288
289 /** listener counter - used to properly synchronize shutdown */
290 private Counter contextsStarted = new Counter("contextsStarted");
291
292 /** Spring namespace/resolver manager */
293 private NamespaceManager nsManager;
294
295 /** The bundle's context */
296 private BundleContext bundleContext;
297
298 /** Bundle listener interested in context creation */
299 private SynchronousBundleListener contextListener;
300
301 /** Bundle listener interested in namespace resolvers/parsers discovery */
302 private SynchronousBundleListener nsListener;
303
304 /** Service-based dependency sorter for shutdown */
305 private ServiceDependencySorter shutdownDependencySorter = new ComparatorServiceDependencySorter();
306
307 /**
308 * Monitor used for dealing with the bundle activator and synchronous bundle
309 * threads
310 */
311 private transient final Object monitor = new Object();
312
313 /**
314 * flag indicating whether the context is down or not - useful during
315 * shutdown
316 */
317 private boolean isClosed = false;
318
319 /** This extender version */
320 private Version extenderVersion;
321
322 private OsgiBundleApplicationContextEventMulticaster multicaster;
323
324 /** listeners interested in monitoring managed OSGi appCtxs */
325 private List applicationListeners;
326
327 /** dynamicList clean up hook */
328 private DisposableBean applicationListenersCleaner;
329 /** Spring compatibility checker */
330 private SpringTypeCompatibilityChecker compatibilityChecker;
331 /** Spring version used */
332 private Bundle wiredSpringBundle;
333 /** shutdown task executor */
334 private TaskExecutor shutdownTaskExecutor;
335
336
337 /** Required by the BundleActivator contract */
338 public ContextLoaderListener() {
339 this.managedContexts = CollectionFactory.createConcurrentMap(16);
340 }
341
342 /**
343 * <p/> Called by OSGi when this bundle is started. Finds all previously
344 * resolved bundles and adds namespace handlers for them if necessary.
345 * </p>
346 * <p/> Creates application contexts for bundles started before the extender
347 * was started.
348 * </p>
349 * <p/> Registers a namespace/entity resolving service for use by web app
350 * contexts.
351 * </p>
352 *
353 * @see org.osgi.framework.BundleActivator#start
354 */
355 public void start(BundleContext context) throws Exception {
356
357 this.bundleContext = context;
358 this.bundleId = context.getBundle().getBundleId();
359
360 this.extenderVersion = OsgiBundleUtils.getBundleVersion(context.getBundle());
361 log.info("Starting [" + bundleContext.getBundle().getSymbolicName() + "] bundle v.[" + extenderVersion + "]");
362
363 detectSpringVersion(context);
364
365 compatibilityChecker = new SpringTypeCompatibilityChecker(bundleContext);
366
367
368 nsManager = new NamespaceManager(context);
369
370
371
372 nsListener = new NamespaceBundleLister();
373 context.addBundleListener(nsListener);
374
375 Bundle[] previousBundles = context.getBundles();
376
377 for (int i = 0; i < previousBundles.length; i++) {
378 Bundle bundle = previousBundles[i];
379 if (OsgiBundleUtils.isBundleResolved(bundle)) {
380 maybeAddNamespaceHandlerFor(bundle);
381 }
382 }
383
384
385 nsManager.afterPropertiesSet();
386
387
388 extenderConfiguration = new ExtenderConfiguration(context);
389
390
391 this.taskExecutor = extenderConfiguration.getTaskExecutor();
392 this.shutdownTaskExecutor = extenderConfiguration.getShutdownTaskExecutor();
393
394 this.contextCreator = extenderConfiguration.getContextCreator();
395 this.postProcessors = extenderConfiguration.getPostProcessors();
396 addDefaultPostProcessors(postProcessors);
397
398
399 initListenerService();
400
401
402
403
404
405 contextListener = new ContextBundleListener();
406
407 context.addBundleListener(contextListener);
408
409 previousBundles = context.getBundles();
410
411
412
413 for (int i = 0; i < previousBundles.length; i++) {
414 if (OsgiBundleUtils.isBundleActive(previousBundles[i])) {
415 try {
416 maybeCreateApplicationContextFor(previousBundles[i]);
417 }
418 catch (Throwable e) {
419 log.warn("Cannot start bundle " + OsgiStringUtils.nullSafeSymbolicName(previousBundles[i])
420 + " due to", e);
421 }
422 }
423 }
424
425 }
426
427 private void detectSpringVersion(BundleContext context) {
428 boolean debug = log.isDebugEnabled();
429
430
431 ServiceReference ref = bundleContext.getServiceReference(PackageAdmin.class.getName());
432 if (ref != null) {
433 PackageAdmin pa = (PackageAdmin) bundleContext.getService(ref);
434 wiredSpringBundle = pa.getBundle(Assert.class);
435 }
436 else {
437 if (debug) {
438 log.debug("PackageAdmin not available; falling back to raw class loading for detecting the wired Spring bundle");
439 }
440 wiredSpringBundle = SpringTypeCompatibilityChecker.findOriginatingBundle(context, Assert.class);
441 if (wiredSpringBundle == null) {
442 throw new IllegalStateException("Impossible to find the originating Spring bundle for " + Assert.class
443 + "; bailing out");
444 }
445 }
446
447 if (debug)
448 log.debug("Spring-DM v.[" + extenderVersion + "] is wired to Spring core bundle "
449 + OsgiStringUtils.nullSafeSymbolicName(wiredSpringBundle) + " version ["
450 + OsgiBundleUtils.getBundleVersion(wiredSpringBundle) + "]");
451 }
452
453 /**
454 * Called by OSGi when this bundled is stopped. Unregister the
455 * namespace/entity resolving service and clear all state. No further
456 * management of application contexts created by this extender prior to
457 * stopping the bundle occurs after this point (even if the extender bundle
458 * is subsequently restarted).
459 *
460 * @see org.osgi.framework.BundleActivator#stop
461 */
462 public void stop(BundleContext context) throws Exception {
463 shutdown();
464 }
465
466 /**
467 * Shutdown the extender and all bundled managed by it. Shutdown of contexts
468 * is in the topological order of the dependency graph formed by the service
469 * references.
470 */
471 protected void shutdown() {
472 synchronized (monitor) {
473
474 if (isClosed)
475 return;
476 else
477 isClosed = true;
478 }
479 log.info("Stopping [" + bundleContext.getBundle().getSymbolicName() + "] bundle v.[" + extenderVersion + "]");
480
481
482 stopTimer();
483
484
485 if (contextListener != null) {
486 bundleContext.removeBundleListener(contextListener);
487 contextListener = null;
488 }
489
490 if (nsListener != null) {
491 bundleContext.removeBundleListener(nsListener);
492 nsListener = null;
493 }
494
495
496 Bundle[] bundles = new Bundle[managedContexts.size()];
497
498 int i = 0;
499 for (Iterator it = managedContexts.values().iterator(); it.hasNext();) {
500 ConfigurableOsgiBundleApplicationContext context = (ConfigurableOsgiBundleApplicationContext) it.next();
501 bundles[i++] = context.getBundle();
502 }
503
504 bundles = shutdownDependencySorter.computeServiceDependencyGraph(bundles);
505
506 boolean debug = log.isDebugEnabled();
507
508 StringBuffer buffer = new StringBuffer();
509 if (debug) {
510 buffer.append("Shutdown order is: {");
511 for (i = 0; i < bundles.length; i++) {
512 buffer.append("\nBundle [" + bundles[i].getSymbolicName() + "]");
513 }
514 buffer.append("\n}");
515 log.debug(buffer);
516 }
517
518 final List taskList = new ArrayList(managedContexts.size());
519 final List closedContexts = Collections.synchronizedList(new ArrayList());
520 final Object[] contextClosingDown = new Object[1];
521
522 for (i = 0; i < bundles.length; i++) {
523 Long id = new Long(bundles[i].getBundleId());
524 final ConfigurableOsgiBundleApplicationContext context = (ConfigurableOsgiBundleApplicationContext) managedContexts.get(id);
525 if (context != null) {
526 closedContexts.add(context);
527
528 taskList.add(new Runnable() {
529
530 private final String toString = "Closing runnable for context " + context.getDisplayName();
531
532
533 public void run() {
534 contextClosingDown[0] = context;
535
536 closedContexts.remove(context);
537 if (log.isDebugEnabled())
538 log.debug("Closing appCtx " + context.getDisplayName());
539 context.close();
540 }
541
542 public String toString() {
543 return toString;
544 }
545 });
546 }
547 }
548
549
550 final Runnable[] tasks = (Runnable[]) taskList.toArray(new Runnable[taskList.size()]);
551
552
553 for (int j = 0; j < tasks.length; j++) {
554 if (RunnableTimedExecution.execute(tasks[j], extenderConfiguration.getShutdownWaitTime(),
555 shutdownTaskExecutor)) {
556 if (debug) {
557 log.debug(contextClosingDown[0] + " context did not close successfully; forcing shutdown...");
558 }
559 }
560 }
561
562 this.managedContexts.clear();
563
564 nsManager.destroy();
565
566
567 if (applicationListeners != null) {
568 applicationListeners = null;
569 try {
570 applicationListenersCleaner.destroy();
571 }
572 catch (Exception ex) {
573 log.warn("exception thrown while releasing OSGi event listeners", ex);
574 }
575 }
576
577
578 if (multicaster != null) {
579 multicaster.removeAllListeners();
580 multicaster = null;
581 }
582
583
584
585 stopTaskExecutor();
586
587 extenderConfiguration.destroy();
588 }
589
590 /**
591 * Cancel any tasks scheduled for the timer.
592 */
593 private void stopTimer() {
594 if (timer != null) {
595 if (log.isDebugEnabled())
596 log.debug("Canceling timer tasks");
597 timer.cancel();
598 }
599 timer = null;
600 }
601
602 /**
603 * Do some additional waiting so the service dependency listeners detect the
604 * shutdown.
605 */
606 private void stopTaskExecutor() {
607 boolean debug = log.isDebugEnabled();
608
609 if (debug)
610 log.debug("Waiting for " + contextsStarted + " service dependency listener(s) to stop...");
611
612 contextsStarted.waitForZero(extenderConfiguration.getShutdownWaitTime());
613
614 if (!contextsStarted.isZero()) {
615 if (debug)
616 log.debug(contextsStarted.getValue()
617 + " service dependency listener(s) did not responded in time; forcing them to shutdown...");
618 extenderConfiguration.setForceThreadShutdown(true);
619 }
620
621 else
622 log.debug("All listeners closed");
623 }
624
625 /**
626 * Utility method that does extender range versioning and approapriate
627 * logging.
628 *
629 * @param bundle
630 */
631 private boolean handlerBundleMatchesExtenderVersion(Bundle bundle) {
632 if (!ConfigUtils.matchExtenderVersionRange(bundle, extenderVersion)) {
633 if (log.isDebugEnabled())
634 log.debug("Bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle)
635 + "] expects an extender w/ version["
636 + OsgiBundleUtils.getHeaderAsVersion(bundle, ConfigUtils.EXTENDER_VERSION)
637 + "] which does not match current extender w/ version[" + extenderVersion
638 + "]; skipping bundle from handler detection");
639 return false;
640 }
641 return true;
642 }
643
644 private void maybeAddNamespaceHandlerFor(Bundle bundle) {
645 if (handlerBundleMatchesExtenderVersion(bundle))
646 nsManager.maybeAddNamespaceHandlerFor(bundle);
647 }
648
649 private void maybeRemoveNameSpaceHandlerFor(Bundle bundle) {
650 if (handlerBundleMatchesExtenderVersion(bundle))
651 nsManager.maybeRemoveNameSpaceHandlerFor(bundle);
652 }
653
654 /**
655 * Context creation is a potentially long-running activity (certainly more
656 * than we want to do on the synchronous event callback).
657 *
658 * <p/>Based on our configuration, the context can be started on the same
659 * thread or on a different one.
660 *
661 * <p/> Kick off a background activity to create an application context for
662 * the given bundle if needed.
663 *
664 * <b>Note:</b> Make sure to do the fastest filtering first to avoid
665 * slowdowns on platforms with a big number of plugins and wiring (i.e.
666 * Eclipse platform).
667 *
668 * @param bundle
669 */
670 protected void maybeCreateApplicationContextFor(Bundle bundle) {
671
672 boolean debug = log.isDebugEnabled();
673 String bundleString = "[" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "]";
674
675 final Long bundleId = new Long(bundle.getBundleId());
676
677 if (managedContexts.containsKey(bundleId)) {
678 if (debug) {
679 log.debug("Bundle " + bundleString + " is already managed; ignoring...");
680 }
681 return;
682 }
683
684 if (!ConfigUtils.matchExtenderVersionRange(bundle, extenderVersion)) {
685 if (debug)
686 log.debug("Bundle " + bundleString + " expects an extender w/ version["
687 + OsgiBundleUtils.getHeaderAsVersion(bundle, ConfigUtils.EXTENDER_VERSION)
688 + "] which does not match current extender w/ version[" + extenderVersion
689 + "]; skipping bundle from context creation");
690 return;
691 }
692
693 BundleContext localBundleContext = OsgiBundleUtils.getBundleContext(bundle);
694
695 if (debug)
696 log.debug("Scanning bundle " + bundleString + " for configurations...");
697
698
699 final DelegatedExecutionOsgiBundleApplicationContext localApplicationContext;
700
701 if (debug)
702 log.debug("Creating an application context for bundle " + bundleString);
703
704 try {
705 localApplicationContext = contextCreator.createApplicationContext(localBundleContext);
706 }
707 catch (Exception ex) {
708 log.error("Cannot create application context for bundle " + bundleString, ex);
709 return;
710 }
711
712 if (localApplicationContext == null) {
713 log.debug("No application context created for bundle " + bundleString);
714 return;
715 }
716
717
718
719
720
721 if (compatibilityChecker.checkCompatibility(bundle)) {
722 log.debug("Bundle " + bundleString + " is Spring type compatible with Spring-DM");
723
724 }
725 else {
726 log.debug("Ignoring bundle " + bundleString + " as it's Spring incompatible with Spring-DM...");
727 return;
728 }
729
730
731 BeanFactoryPostProcessor processingHook = new OsgiBeanFactoryPostProcessorAdapter(localBundleContext,
732 postProcessors);
733
734
735 localApplicationContext.addBeanFactoryPostProcessor(processingHook);
736
737
738 managedContexts.put(bundleId, localApplicationContext);
739
740 localApplicationContext.setDelegatedEventMulticaster(multicaster);
741
742
743 Runnable contextRefresh = new Runnable() {
744
745 public void run() {
746 localApplicationContext.refresh();
747 }
748 };
749
750
751
752 TaskExecutor executor = null;
753
754 ApplicationContextConfiguration config = new ApplicationContextConfiguration(bundle);
755
756 String creationType;
757
758
759 if (config.isCreateAsynchronously()) {
760
761 executor = taskExecutor;
762 creationType = "Asynchronous";
763 }
764 else {
765
766 executor = sameThreadTaskExecutor;
767 creationType = "Synchronous";
768 }
769
770 if (debug) {
771 log.debug(creationType + " context creation for bundle " + bundleString);
772 }
773
774
775 if (config.isWaitForDependencies()) {
776 DependencyWaiterApplicationContextExecutor appCtxExecutor = new DependencyWaiterApplicationContextExecutor(
777 localApplicationContext, !config.isCreateAsynchronously(),
778 extenderConfiguration.getDependencyFactories());
779
780 appCtxExecutor.setTimeout(config.getTimeout());
781 appCtxExecutor.setWatchdog(timer);
782 appCtxExecutor.setTaskExecutor(executor);
783 appCtxExecutor.setMonitoringCounter(contextsStarted);
784
785 appCtxExecutor.setDelegatedMulticaster(this.multicaster);
786
787 contextsStarted.increment();
788 }
789 else {
790
791 }
792
793 executor.execute(contextRefresh);
794 }
795
796 /**
797 * Closing an application context is a potentially long-running activity,
798 * however, we *have* to do it synchronously during the event process as the
799 * BundleContext object is not valid once we return from this method.
800 *
801 * @param bundle
802 */
803 protected void maybeCloseApplicationContextFor(Bundle bundle) {
804 final ConfigurableOsgiBundleApplicationContext context = (ConfigurableOsgiBundleApplicationContext) managedContexts.remove(new Long(
805 bundle.getBundleId()));
806 if (context == null) {
807 return;
808 }
809
810 RunnableTimedExecution.execute(new Runnable() {
811
812 private final String toString = "Closing runnable for context " + context.getDisplayName();
813
814
815 public void run() {
816 if (context.isActive()) {
817 context.close();
818 }
819 }
820
821 public String toString() {
822 return toString;
823 }
824
825 }, extenderConfiguration.getShutdownWaitTime(), shutdownTaskExecutor);
826 }
827
828 private void initListenerService() {
829 multicaster = extenderConfiguration.getEventMulticaster();
830
831 createListenersList();
832
833 multicaster.addApplicationListener(new ListListenerAdapter(applicationListeners));
834
835 if (log.isDebugEnabled())
836 log.debug("Initialization of OSGi listeners service completed...");
837 }
838
839
840
841
842 private void addDefaultPostProcessors(List postProcessorsList) {
843 if (extenderConfiguration.shouldProcessAnnotation() || Boolean.getBoolean(AUTO_ANNOTATION_PROCESSING)) {
844
845 log.info("Enabled automatic Spring-DM annotation processing");
846
847
848 if (log.isTraceEnabled())
849 log.trace("Adding OSGi annotation post processor");
850 postProcessorsList.add(0, new OsgiAnnotationPostProcessor());
851 }
852
853 else {
854 log.info("Disabled automatic Spring-DM annotation processing");
855 }
856 }
857
858 /**
859 * Creates a dynamic OSGi list of OSGi services interested in receiving
860 * events for OSGi application contexts.
861 */
862 private void createListenersList() {
863 OsgiServiceCollectionProxyFactoryBean fb = new OsgiServiceCollectionProxyFactoryBean();
864 fb.setBundleContext(bundleContext);
865 fb.setCardinality(Cardinality.C_0__N);
866 fb.setCollectionType(CollectionType.LIST);
867 fb.setInterfaces(new Class[] { OsgiBundleApplicationContextListener.class });
868 fb.setBeanClassLoader(extenderConfiguration.getClassLoader());
869 fb.afterPropertiesSet();
870
871 applicationListenersCleaner = fb;
872 applicationListeners = (List) fb.getObject();
873 }
874 }