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