1 package com.atlassian.plugin.osgi.container.felix;
2
3 import com.atlassian.plugin.ReferenceMode;
4 import com.atlassian.plugin.event.PluginEventListener;
5 import com.atlassian.plugin.event.PluginEventManager;
6 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
7 import com.atlassian.plugin.event.events.PluginFrameworkStartingEvent;
8 import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartingEvent;
9 import com.atlassian.plugin.event.events.PluginUninstalledEvent;
10 import com.atlassian.plugin.event.events.PluginUpgradedEvent;
11 import com.atlassian.plugin.instrumentation.PluginSystemInstrumentation;
12 import com.atlassian.plugin.osgi.container.OsgiContainerException;
13 import com.atlassian.plugin.osgi.container.OsgiContainerManager;
14 import com.atlassian.plugin.osgi.container.OsgiContainerStartedEvent;
15 import com.atlassian.plugin.osgi.container.OsgiContainerStoppedEvent;
16 import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
17 import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
18 import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
19 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
20 import com.atlassian.plugin.osgi.hostcomponents.impl.DefaultComponentRegistrar;
21 import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
22 import com.atlassian.plugin.util.ContextClassLoaderSwitchingUtil;
23 import com.atlassian.plugin.util.PluginUtils;
24 import com.google.common.annotations.VisibleForTesting;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.felix.framework.Felix;
27 import org.apache.felix.framework.Logger;
28 import org.apache.felix.framework.cache.BundleArchive;
29 import org.apache.felix.framework.cache.BundleCache;
30 import org.apache.felix.framework.util.FelixConstants;
31 import org.apache.felix.framework.util.StringMap;
32 import org.osgi.framework.Bundle;
33 import org.osgi.framework.BundleActivator;
34 import org.osgi.framework.BundleContext;
35 import org.osgi.framework.BundleEvent;
36 import org.osgi.framework.BundleException;
37 import org.osgi.framework.BundleListener;
38 import org.osgi.framework.Constants;
39 import org.osgi.framework.FrameworkEvent;
40 import org.osgi.framework.FrameworkListener;
41 import org.osgi.framework.ServiceReference;
42 import org.osgi.framework.ServiceRegistration;
43 import org.osgi.service.packageadmin.PackageAdmin;
44 import org.osgi.util.tracker.ServiceTracker;
45 import org.osgi.util.tracker.ServiceTrackerCustomizer;
46 import org.slf4j.LoggerFactory;
47
48 import java.io.File;
49 import java.net.URL;
50 import java.util.ArrayList;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.Optional;
57 import java.util.concurrent.CountDownLatch;
58 import java.util.concurrent.ThreadFactory;
59 import java.util.concurrent.TimeUnit;
60 import java.util.function.Function;
61
62 import static com.atlassian.plugin.util.FileUtils.conditionallyExtractZipFile;
63 import static com.google.common.base.Preconditions.checkNotNull;
64
65
66
67
68 public class FelixOsgiContainerManager implements OsgiContainerManager {
69 public static final int REFRESH_TIMEOUT = 10;
70
71 private static final org.slf4j.Logger log = LoggerFactory.getLogger(FelixOsgiContainerManager.class);
72 public static final String ATLASSIAN_BOOTDELEGATION = "atlassian.org.osgi.framework.bootdelegation";
73 public static final String ATLASSIAN_BOOTDELEGATION_EXTRA = "atlassian.org.osgi.framework.bootdelegation.extra";
74 public static final String ATLASSIAN_DISABLE_REFERENCE_PROTOCOL = "atlassian.felix.disable.reference.protocol";
75
76 private final OsgiPersistentCache persistentCache;
77 private Function<DefaultComponentRegistrar, BundleRegistration> bundleRegistrationFactory;
78 private final PackageScannerConfiguration packageScannerConfig;
79 private final HostComponentProvider hostComponentProvider;
80 private final List<ServiceTracker> trackers;
81 private final ExportsBuilder exportsBuilder;
82
83 private final ThreadFactory threadFactory = runnable -> {
84 final Thread thread = new Thread(runnable, "Felix:Startup");
85 thread.setDaemon(true);
86 return thread;
87 };
88 private BundleRegistration registration = null;
89 private Felix felix = null;
90 private boolean felixRunning = false;
91 private boolean disableMultipleBundleVersions = true;
92 private Logger felixLogger;
93 private final PluginEventManager pluginEventManager;
94
95
96
97
98
99
100
101
102
103
104
105
106 public FelixOsgiContainerManager(
107 final URL frameworkBundlesZip,
108 final OsgiPersistentCache persistentCache,
109 final PackageScannerConfiguration packageScannerConfig,
110 final HostComponentProvider provider,
111 final PluginEventManager eventManager)
112 throws OsgiContainerException {
113 this(registrar -> new BundleRegistration(frameworkBundlesZip, persistentCache.getFrameworkBundleCache(), registrar),
114 persistentCache, packageScannerConfig, provider, eventManager);
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 public FelixOsgiContainerManager(
132 final File frameworkBundlesDirectory,
133 final OsgiPersistentCache persistentCache,
134 final PackageScannerConfiguration packageScannerConfig,
135 final HostComponentProvider provider,
136 final PluginEventManager eventManager)
137 throws OsgiContainerException {
138 this(registrar -> new BundleRegistration(frameworkBundlesDirectory, registrar), persistentCache,
139 packageScannerConfig, provider, eventManager);
140 }
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157 private FelixOsgiContainerManager(
158 final Function<DefaultComponentRegistrar, BundleRegistration> bundleRegistrationFactory,
159 final OsgiPersistentCache persistentCache,
160 final PackageScannerConfiguration packageScannerConfig,
161 final HostComponentProvider provider,
162 final PluginEventManager eventManager)
163 throws OsgiContainerException {
164 checkNotNull(bundleRegistrationFactory, "The bundle registration factory must not be null");
165 checkNotNull(persistentCache, "The framework bundles directory must not be null");
166 checkNotNull(packageScannerConfig, "The package scanner configuration must not be null");
167 checkNotNull(eventManager, "The plugin event manager must not be null");
168
169 this.bundleRegistrationFactory = bundleRegistrationFactory;
170 this.packageScannerConfig = packageScannerConfig;
171 this.persistentCache = persistentCache;
172 hostComponentProvider = provider;
173 trackers = Collections.synchronizedList(new ArrayList<>());
174 this.pluginEventManager = eventManager;
175 eventManager.register(this);
176 felixLogger = new FelixLoggerBridge(log);
177 exportsBuilder = new ExportsBuilder();
178 }
179
180 public void setFelixLogger(final Logger logger) {
181 felixLogger = logger;
182 }
183
184 public void setDisableMultipleBundleVersions(final boolean val) {
185 disableMultipleBundleVersions = val;
186 }
187
188
189
190
191
192
193 public void clearExportCache() {
194 exportsBuilder.clearExportCache();
195 }
196
197 @SuppressWarnings({"UnusedDeclaration"})
198 @PluginEventListener
199 public void onStart(final PluginFrameworkStartingEvent event) {
200 start();
201 }
202
203 @SuppressWarnings({"UnusedDeclaration"})
204 @PluginEventListener
205 public void onShutdown(final PluginFrameworkShutdownEvent event) {
206 stop();
207 }
208
209 @SuppressWarnings({"UnusedDeclaration"})
210 @PluginEventListener
211 public void onPluginUpgrade(final PluginUpgradedEvent event) {
212 registration.refreshPackages();
213 }
214
215 @SuppressWarnings({"UnusedDeclaration"})
216 @PluginEventListener
217 public void onPluginUninstallation(final PluginUninstalledEvent event) {
218 registration.refreshPackages();
219 }
220
221 @SuppressWarnings({"UnusedDeclaration"})
222 @PluginEventListener
223 public void onPluginFrameworkWarmRestarting(final PluginFrameworkWarmRestartingEvent event) {
224 registration.loadHostComponents(collectHostComponents(hostComponentProvider));
225 }
226
227 public void start() throws OsgiContainerException {
228 if (isRunning()) {
229 return;
230 }
231
232 final DefaultComponentRegistrar registrar = collectHostComponents(hostComponentProvider);
233
234 final StringMap configMap = new StringMap();
235
236
237
238 configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, exportsBuilder.getExports(registrar.getRegistry(), packageScannerConfig));
239
240
241 configMap.put(BundleCache.CACHE_ROOTDIR_PROP, persistentCache.getOsgiBundleCache().getAbsolutePath());
242
243 configMap.put(FelixConstants.LOG_LEVEL_PROP, String.valueOf(felixLogger.getLogLevel()));
244 configMap.put(FelixConstants.LOG_LOGGER_PROP, felixLogger);
245 String bootDelegation = System.getProperty(ATLASSIAN_BOOTDELEGATION);
246 if ((bootDelegation == null) || (bootDelegation.trim().length() == 0)) {
247
248
249
250 bootDelegation = "weblogic,weblogic.*," +
251 "META-INF.services," +
252 "jdk.*," +
253 "com.yourkit,com.yourkit.*," +
254 "com.chronon,com.chronon.*," +
255 "org.jboss.byteman,org.jboss.byteman.*," +
256 "com.jprofiler,com.jprofiler.*," +
257 "org.apache.xerces,org.apache.xerces.*," +
258 "org.apache.xalan,org.apache.xalan.*," +
259 "org.apache.xml.serializer," +
260 "sun.*," +
261 "com.sun.xml.bind.v2," +
262 "com.sun.xml.internal.bind.v2," +
263 "com.icl.saxon," +
264 "com_cenqua_clover," +
265 "com.cenqua.clover,com.cenqua.clover.*," +
266 "com.atlassian.clover,com.atlassian.clover.*";
267 }
268 final String extraBootDelegation = System.getProperty(ATLASSIAN_BOOTDELEGATION_EXTRA, "").trim();
269 if (0 != extraBootDelegation.length()) {
270 bootDelegation = bootDelegation + "," + extraBootDelegation;
271 }
272
273 configMap.put(FelixConstants.FRAMEWORK_BOOTDELEGATION, bootDelegation);
274 configMap.put(FelixConstants.IMPLICIT_BOOT_DELEGATION_PROP, "false");
275
276 configMap.put(FelixConstants.FRAMEWORK_BUNDLE_PARENT, FelixConstants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK);
277 if (log.isDebugEnabled()) {
278 log.debug("Felix configuration: " + configMap);
279 }
280
281 validateConfiguration(configMap);
282
283 try {
284
285 registration = bundleRegistrationFactory.apply(registrar);
286 final List<BundleActivator> list = new ArrayList<>();
287 list.add(registration);
288 configMap.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list);
289
290
291
292 felix = new Felix(configMap);
293
294
295 final Runnable start = () -> {
296 try {
297 Thread.currentThread().setContextClassLoader(null);
298 felix.start();
299 felixRunning = true;
300 } catch (final BundleException e) {
301 throw new OsgiContainerException("Unable to start felix", e);
302 }
303 };
304 final Thread t = threadFactory.newThread(start);
305 t.start();
306
307
308 t.join(10 * 60 * 1000);
309
310 } catch (final Exception ex) {
311 throw new OsgiContainerException("Unable to start OSGi container", ex);
312 }
313 pluginEventManager.broadcast(new OsgiContainerStartedEvent(this));
314 }
315
316
317
318
319
320 private void validateConfiguration(final StringMap configMap) throws OsgiContainerException {
321 final String systemExports = (String) configMap.get(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA);
322
323 final String cacheKeySource = StringUtils.join(new Object[]{this.getRuntimeEnvironment(), systemExports}, ',');
324
325 validateCaches(cacheKeySource);
326
327 detectIncorrectOsgiVersion();
328 detectXercesOverride(systemExports);
329 }
330
331
332
333
334
335
336
337
338 void detectXercesOverride(final String systemExports) throws OsgiContainerException {
339 int pos = systemExports.indexOf("org.apache.xerces.util");
340 if (pos > -1) {
341 if (pos == 0 || systemExports.charAt(pos - 1) == ',') {
342 pos += "org.apache.xerces.util".length();
343
344
345 if (pos >= systemExports.length() || ';' != systemExports.charAt(pos)) {
346 throw new OsgiContainerException(
347 "Detected an incompatible version of Apache Xerces on the classpath. If using Tomcat, you may have " +
348 "an old version of Xerces in $TOMCAT_HOME/common/lib/endorsed that will need to be removed."
349 );
350 }
351 }
352 }
353 }
354
355
356
357
358
359
360
361 private void validateCaches(final String cacheKeySource) {
362 log.info("Using Felix bundle cacheKey source: {}", cacheKeySource);
363 persistentCache.validate(cacheKeySource);
364 log.debug("Using Felix bundle cache directory: {}", persistentCache.getOsgiBundleCache().getAbsolutePath());
365 }
366
367
368
369
370 private void detectIncorrectOsgiVersion() {
371 try {
372 Bundle.class.getMethod("getBundleContext");
373 } catch (final NoSuchMethodException e) {
374 throw new OsgiContainerException("Detected older version (4.0 or earlier) of OSGi. If using WebSphere 6.1, "
375 + "please enable application-first (parent-last) classloading and the 'Single classloader for "
376 + "application' WAR classloader policy.");
377 }
378 }
379
380 public void stop() throws OsgiContainerException {
381 if (felixRunning) {
382 for (final ServiceTracker tracker : new HashSet<>(trackers)) {
383 tracker.close();
384 }
385 final FrameworkListener listener = event -> {
386 if (event.getType() == FrameworkEvent.WAIT_TIMEDOUT) {
387 log.error("Timeout waiting for OSGi to shutdown");
388 threadDump();
389 } else if (event.getType() == FrameworkEvent.STOPPED) {
390 log.info("OSGi shutdown successful");
391 }
392 };
393 try {
394 felix.getBundleContext().addFrameworkListener(listener);
395 felix.stop();
396 felix.waitForStop(TimeUnit.SECONDS.toMillis(60));
397 } catch (final InterruptedException e) {
398 log.warn("Interrupting Felix shutdown", e);
399 } catch (final BundleException ex) {
400 log.error("An error occurred while stopping the Felix OSGi Container. ", ex);
401 }
402 }
403
404 felixRunning = false;
405 felix = null;
406 pluginEventManager.broadcast(new OsgiContainerStoppedEvent(this));
407 }
408
409 private void threadDump() {
410 final StringBuilder sb = new StringBuilder();
411 final String nl = System.getProperty("line.separator");
412 for (final Map.Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) {
413 final Thread key = entry.getKey();
414 final StackTraceElement[] trace = entry.getValue();
415 sb.append(key).append(nl);
416 for (final StackTraceElement aTrace : trace) {
417 sb.append(" ").append(aTrace).append(nl);
418 }
419 }
420 log.debug("Thread dump: " + nl + sb.toString());
421 }
422
423 public Bundle[] getBundles() {
424 if (isRunning()) {
425 return registration.getBundles();
426 } else {
427 throw new IllegalStateException("Cannot retrieve the bundles if the Felix container isn't running. Check "
428 + "earlier in the logs for the possible cause as to why Felix didn't start correctly.");
429 }
430 }
431
432 public ServiceReference[] getRegisteredServices() {
433 return felix.getRegisteredServices();
434 }
435
436 public ServiceTracker getServiceTracker(final String interfaceClassName) {
437 return getServiceTracker(interfaceClassName, null);
438 }
439
440 @Override
441 public ServiceTracker getServiceTracker(
442 final String interfaceClassName,
443 final ServiceTrackerCustomizer serviceTrackerCustomizer) {
444 if (!isRunning()) {
445 throw new IllegalStateException("Unable to create a tracker when osgi is not running");
446 }
447
448 final ServiceTracker tracker = registration.getServiceTracker(interfaceClassName, trackers, serviceTrackerCustomizer);
449 tracker.open();
450 trackers.add(tracker);
451 return tracker;
452 }
453
454 @Override
455 public void addBundleListener(BundleListener listener) {
456 felix.getBundleContext().addBundleListener(listener);
457 }
458
459 @Override
460 public void removeBundleListener(BundleListener listener) {
461 Felix felix = this.felix;
462 if (felix != null) {
463 BundleContext context = felix.getBundleContext();
464 if (context != null) {
465 context.removeBundleListener(listener);
466 }
467 }
468 }
469
470 @Override
471 public Bundle installBundle(final File file, final ReferenceMode referenceMode) throws OsgiContainerException {
472 try {
473 return registration.install(file, disableMultipleBundleVersions, referenceMode.allowsReference());
474 } catch (final BundleException e) {
475 throw new OsgiContainerException("Unable to install bundle", e);
476 }
477 }
478
479 DefaultComponentRegistrar collectHostComponents(final HostComponentProvider provider) {
480 final DefaultComponentRegistrar registrar = new DefaultComponentRegistrar();
481 if (provider != null) {
482 provider.provide(registrar);
483 }
484 return registrar;
485 }
486
487 public boolean isRunning() {
488 return felixRunning;
489 }
490
491 public List<HostComponentRegistration> getHostComponentRegistrations() {
492 return registration.getHostComponentRegistrations();
493 }
494
495
496
497
498
499
500 @VisibleForTesting
501 String getRuntimeEnvironment() {
502
503
504
505 return String.format("java.version=%s,plugin.enable.timeout=%d", System.getProperty("java.version"),
506 PluginUtils.getDefaultEnablingWaitPeriod());
507 }
508
509
510
511
512
513 static class BundleRegistration implements BundleActivator, BundleListener, FrameworkListener {
514
515 private final URL frameworkBundlesUrl;
516 private final File frameworkBundlesDir;
517 private DefaultComponentRegistrar registrar;
518 private ClassLoader initializedClassLoader;
519
520 private BundleContext bundleContext;
521 private PackageAdmin packageAdmin;
522 private List<ServiceRegistration> hostServicesReferences;
523 private List<HostComponentRegistration> hostComponentRegistrations;
524 private Optional<ServiceRegistration> instrumentationServiceReference = Optional.empty();
525
526 public BundleRegistration(final File frameworkBundlesDir, final DefaultComponentRegistrar registrar) {
527 this(null, frameworkBundlesDir, registrar);
528 }
529
530 public BundleRegistration(final URL frameworkBundlesUrl, final File frameworkBundlesDir, final DefaultComponentRegistrar registrar) {
531 this.frameworkBundlesUrl = frameworkBundlesUrl;
532 this.frameworkBundlesDir = frameworkBundlesDir;
533 this.registrar = registrar;
534 this.initializedClassLoader = Thread.currentThread().getContextClassLoader();
535 }
536
537 public void start(final BundleContext context) throws Exception {
538 bundleContext = context;
539 final ServiceReference ref = context.getServiceReference(PackageAdmin.class.getName());
540 packageAdmin = (PackageAdmin) context.getService(ref);
541
542 context.addBundleListener(this);
543 context.addFrameworkListener(this);
544
545 loadHostComponents(registrar);
546 if (null != frameworkBundlesUrl) {
547 conditionallyExtractZipFile(frameworkBundlesUrl, frameworkBundlesDir);
548 }
549 installFrameworkBundles();
550
551
552 instrumentationServiceReference.ifPresent(ServiceRegistration::unregister);
553 instrumentationServiceReference = Optional.empty();
554
555
556 final Optional instrumentRegistry = PluginSystemInstrumentation.instance().getInstrumentRegistry();
557 if (instrumentRegistry.isPresent()) {
558 instrumentationServiceReference = Optional.of(context.registerService(PluginSystemInstrumentation.INSTRUMENT_REGISTRY_CLASS, instrumentRegistry.get(), null));
559 } else {
560 instrumentationServiceReference = Optional.empty();
561 }
562 }
563
564 public void stop(final BundleContext ctx) {
565 ctx.removeBundleListener(this);
566 ctx.removeFrameworkListener(this);
567 if (hostServicesReferences != null) {
568 for (final ServiceRegistration ref : hostServicesReferences) {
569 ref.unregister();
570 }
571 }
572
573
574 instrumentationServiceReference.ifPresent(ServiceRegistration::unregister);
575 instrumentationServiceReference = Optional.empty();
576
577 bundleContext = null;
578 packageAdmin = null;
579 hostServicesReferences = null;
580 hostComponentRegistrations = null;
581 registrar = null;
582 initializedClassLoader = null;
583 }
584
585 public void bundleChanged(final BundleEvent evt) {
586 switch (evt.getType()) {
587 case BundleEvent.INSTALLED:
588 log.info("Installed bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
589 break;
590 case BundleEvent.RESOLVED:
591 log.info("Resolved bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
592 break;
593 case BundleEvent.UNRESOLVED:
594 log.info("Unresolved bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
595 break;
596 case BundleEvent.STARTED:
597 log.info("Started bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
598 break;
599 case BundleEvent.STOPPED:
600 log.info("Stopped bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
601 break;
602 case BundleEvent.UNINSTALLED:
603 log.info("Uninstalled bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
604 break;
605 }
606 }
607
608 public Bundle install(final File path, final boolean uninstallOtherVersions)
609 throws BundleException {
610 return install(path, uninstallOtherVersions, false);
611 }
612
613 public Bundle install(final File path, final boolean uninstallOtherVersions, final boolean allowReference)
614 throws BundleException {
615 boolean bundleUninstalled = false;
616 if (uninstallOtherVersions) {
617 final String pluginKey = OsgiHeaderUtil.getPluginKey(path);
618 if (null == pluginKey) {
619
620
621 throw new BundleException("No plugin key in (possibly malformed) bundle jar '" + path + "'");
622 }
623
624 for (final Bundle oldBundle : bundleContext.getBundles()) {
625 if (pluginKey.equals(OsgiHeaderUtil.getPluginKey(oldBundle))) {
626 log.info("Uninstalling existing version " + oldBundle.getHeaders().get(Constants.BUNDLE_VERSION));
627 oldBundle.uninstall();
628 bundleUninstalled = true;
629 }
630 }
631 }
632 String location = path.toURI().toString();
633 if (allowReference &&
634 (!Boolean.getBoolean(ATLASSIAN_DISABLE_REFERENCE_PROTOCOL)) &&
635 location.startsWith(BundleArchive.FILE_PROTOCOL)) {
636
637
638 location = BundleArchive.REFERENCE_PROTOCOL + location;
639 }
640 final Bundle bundle = bundleContext.installBundle(location);
641 if (bundleUninstalled) {
642 refreshPackages();
643 }
644 return bundle;
645 }
646
647 public Bundle[] getBundles() {
648 return bundleContext.getBundles();
649 }
650
651 public ServiceTracker getServiceTracker(final String clazz, final Collection<ServiceTracker> trackedTrackers) {
652 return getServiceTracker(clazz, trackedTrackers, null);
653 }
654
655 public ServiceTracker getServiceTracker(final String clazz,
656 final Collection<ServiceTracker> trackedTrackers,
657 final ServiceTrackerCustomizer customizer) {
658 return new ServiceTracker(bundleContext, clazz, customizer) {
659 @Override
660 public void close() {
661 super.close();
662 trackedTrackers.remove(this);
663 }
664 };
665 }
666
667 public List<HostComponentRegistration> getHostComponentRegistrations() {
668 return hostComponentRegistrations;
669 }
670
671 void loadHostComponents(final DefaultComponentRegistrar registrar) {
672
673 if (hostServicesReferences != null) {
674 for (final ServiceRegistration reg : hostServicesReferences) {
675 reg.unregister();
676 }
677 }
678
679 ContextClassLoaderSwitchingUtil.runInContext(initializedClassLoader, () -> {
680 hostServicesReferences = registrar.writeRegistry(bundleContext);
681 hostComponentRegistrations = registrar.getRegistry();
682 });
683 }
684
685 private void installFrameworkBundles() throws BundleException {
686 final File[] bundleFiles = frameworkBundlesDir == null
687 ? null : frameworkBundlesDir.listFiles((file, s) -> s.endsWith(".jar"));
688 if (bundleFiles == null) {
689 throw new BundleException("Directory with framework bundle jars could not be read: " + frameworkBundlesDir);
690 }
691
692 final List<Bundle> bundles = new ArrayList<>();
693 for (final File bundleFile : bundleFiles) {
694 bundles.add(install(bundleFile, false, false));
695 }
696
697 packageAdmin.resolveBundles(null);
698
699 for (final Bundle bundle : bundles) {
700 if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) == null) {
701 bundle.start();
702 }
703 }
704 }
705
706 public void refreshPackages() {
707 final CountDownLatch latch = new CountDownLatch(1);
708 final FrameworkListener refreshListener = event -> {
709 if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) {
710 log.info("Packages refreshed");
711 latch.countDown();
712 }
713 };
714
715 bundleContext.addFrameworkListener(refreshListener);
716 try {
717 packageAdmin.refreshPackages(null);
718 boolean refreshed = false;
719 try {
720 refreshed = latch.await(REFRESH_TIMEOUT, TimeUnit.SECONDS);
721 } catch (final InterruptedException e) {
722
723 }
724 if (!refreshed) {
725 log.warn("Timeout exceeded waiting for package refresh");
726 }
727 } finally {
728 bundleContext.removeFrameworkListener(refreshListener);
729 }
730 }
731
732 @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
733 public void frameworkEvent(final FrameworkEvent event) {
734 String bundleBits = "";
735 if (event.getBundle() != null) {
736 bundleBits = " in bundle " + event.getBundle().getSymbolicName();
737 }
738 switch (event.getType()) {
739 case FrameworkEvent.ERROR:
740 log.error("Framework error" + bundleBits, event.getThrowable());
741 break;
742 case FrameworkEvent.WARNING:
743 log.warn("Framework warning" + bundleBits, event.getThrowable());
744 break;
745 case FrameworkEvent.INFO:
746 log.info("Framework info" + bundleBits, event.getThrowable());
747 break;
748 }
749 }
750 }
751
752 }