1   package com.atlassian.plugin.osgi.container.felix;
2   
3   import com.atlassian.plugin.event.PluginEventListener;
4   import com.atlassian.plugin.event.PluginEventManager;
5   import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
6   import com.atlassian.plugin.event.events.PluginFrameworkStartingEvent;
7   import com.atlassian.plugin.event.events.PluginUpgradedEvent;
8   import com.atlassian.plugin.event.events.PluginFrameworkWarmRestartingEvent;
9   import com.atlassian.plugin.osgi.container.OsgiContainerException;
10  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
11  import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
12  import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
13  import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
14  import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
15  import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
16  import com.atlassian.plugin.osgi.hostcomponents.impl.DefaultComponentRegistrar;
17  import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
18  import com.atlassian.plugin.util.ClassLoaderUtils;
19  
20  import org.apache.commons.io.FileUtils;
21  import org.apache.commons.lang.Validate;
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.felix.framework.Felix;
25  import org.apache.felix.framework.Logger;
26  import org.apache.felix.framework.cache.BundleCache;
27  import org.apache.felix.framework.util.FelixConstants;
28  import org.apache.felix.framework.util.StringMap;
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.BundleException;
34  import org.osgi.framework.BundleListener;
35  import org.osgi.framework.Constants;
36  import org.osgi.framework.FrameworkEvent;
37  import org.osgi.framework.FrameworkListener;
38  import org.osgi.framework.ServiceReference;
39  import org.osgi.framework.ServiceRegistration;
40  import org.osgi.service.packageadmin.PackageAdmin;
41  import org.osgi.util.tracker.ServiceTracker;
42  
43  import java.io.File;
44  import java.io.FilenameFilter;
45  import java.io.IOException;
46  import java.net.URL;
47  import java.util.ArrayList;
48  import java.util.Collections;
49  import java.util.HashSet;
50  import java.util.List;
51  import java.util.Set;
52  import java.util.concurrent.ThreadFactory;
53  import java.util.concurrent.CountDownLatch;
54  import java.util.concurrent.TimeUnit;
55  import java.util.jar.JarFile;
56  
57  /**
58   * Felix implementation of the OSGi container manager
59   */
60  public class FelixOsgiContainerManager implements OsgiContainerManager
61  {
62      public static final String OSGI_FRAMEWORK_BUNDLES_ZIP = "osgi-framework-bundles.zip";
63  
64      private static final Log log = LogFactory.getLog(FelixOsgiContainerManager.class);
65      private static final String OSGI_BOOTDELEGATION = "org.osgi.framework.bootdelegation";
66      private static final String ATLASSIAN_PREFIX = "atlassian.";
67  
68      private final OsgiPersistentCache persistentCache;
69      private final URL frameworkBundlesUrl;
70      private final PackageScannerConfiguration packageScannerConfig;
71      private final HostComponentProvider hostComponentProvider;
72      private final Set<ServiceTracker> trackers;
73      private final ExportsBuilder exportsBuilder;
74      private final ThreadFactory threadFactory = new ThreadFactory()
75      {
76          public Thread newThread(final Runnable r)
77          {
78              final Thread thread = new Thread(r, "Felix:Startup");
79              thread.setDaemon(true);
80              return thread;
81          }
82      };
83  
84      private BundleRegistration registration = null;
85      private Felix felix = null;
86      private boolean felixRunning = false;
87      private boolean disableMultipleBundleVersions = true;
88      private Logger felixLogger;
89  
90      /**
91       * Constructs the container manager using the framework bundles zip file located in this library
92       * @param frameworkBundlesDir The directory to unzip the framework bundles into.
93       * @param packageScannerConfig The configuration for package scanning
94       * @param provider The host component provider.  May be null.
95       * @param eventManager The plugin event manager to register for init and shutdown events
96       * @deprecated Since 2.2.0, use
97       *   {@link #FelixOsgiContainerManager(OsgiPersistentCache,PackageScannerConfiguration,HostComponentProvider,PluginEventManager)} instead
98       */
99      @Deprecated
100     public FelixOsgiContainerManager(final File frameworkBundlesDir, final PackageScannerConfiguration packageScannerConfig, final HostComponentProvider provider, final PluginEventManager eventManager)
101     {
102         this(ClassLoaderUtils.getResource(OSGI_FRAMEWORK_BUNDLES_ZIP, FelixOsgiContainerManager.class), frameworkBundlesDir, packageScannerConfig,
103             provider, eventManager);
104     }
105 
106     /**
107      * Constructs the container manager
108      * @param frameworkBundlesZip The location of the zip file containing framework bundles
109      * @param frameworkBundlesDir The directory to unzip the framework bundles into.
110      * @param packageScannerConfig The configuration for package scanning
111      * @param provider The host component provider.  May be null.
112      * @param eventManager The plugin event manager to register for init and shutdown events
113      * @deprecated Since 2.2.0, use
114      *   {@link #FelixOsgiContainerManager(URL, OsgiPersistentCache,PackageScannerConfiguration,HostComponentProvider,PluginEventManager)} instead
115      */
116     @Deprecated
117     public FelixOsgiContainerManager(final URL frameworkBundlesZip, final File frameworkBundlesDir, final PackageScannerConfiguration packageScannerConfig, final HostComponentProvider provider, final PluginEventManager eventManager)
118     {
119         this(frameworkBundlesZip, new DefaultOsgiPersistentCache(new File(frameworkBundlesDir.getParentFile(),
120             "osgi-cache")), packageScannerConfig, provider, eventManager);
121     }
122 
123     /**
124      * Constructs the container manager using the framework bundles zip file located in this library
125      * @param persistentCache The persistent cache configuration.
126      * @param packageScannerConfig The configuration for package scanning
127      * @param provider The host component provider.  May be null.
128      * @param eventManager The plugin event manager to register for init and shutdown events
129      *
130      * @since 2.2.0
131      */
132     public FelixOsgiContainerManager(final OsgiPersistentCache persistentCache, final PackageScannerConfiguration packageScannerConfig, final HostComponentProvider provider, final PluginEventManager eventManager)
133     {
134         this(ClassLoaderUtils.getResource(OSGI_FRAMEWORK_BUNDLES_ZIP, FelixOsgiContainerManager.class), persistentCache, packageScannerConfig,
135             provider, eventManager);
136     }
137 
138     /**
139      * Constructs the container manager
140      * @param frameworkBundlesZip The location of the zip file containing framework bundles
141      * @param persistentCache The persistent cache to use for the framework and framework bundles
142      * @param packageScannerConfig The configuration for package scanning
143      * @param provider The host component provider.  May be null.
144      * @param eventManager The plugin event manager to register for init and shutdown events
145      *
146      * @since 2.2.0
147      * @throws com.atlassian.plugin.osgi.container.OsgiContainerException If the host version isn't supplied and the
148      *  cache directory cannot be cleaned.
149      */
150     public FelixOsgiContainerManager(final URL frameworkBundlesZip, OsgiPersistentCache persistentCache,
151                                      final PackageScannerConfiguration packageScannerConfig,
152                                      final HostComponentProvider provider, final PluginEventManager eventManager)
153                                      throws OsgiContainerException
154     {
155         Validate.notNull(frameworkBundlesZip, "The framework bundles zip is required");
156         Validate.notNull(persistentCache, "The framework bundles directory must not be null");
157         Validate.notNull(packageScannerConfig, "The package scanner configuration must not be null");
158         Validate.notNull(eventManager, "The plugin event manager is required");
159 
160         frameworkBundlesUrl = frameworkBundlesZip;
161         this.packageScannerConfig = packageScannerConfig;
162         this.persistentCache = persistentCache;
163         hostComponentProvider = provider;
164         trackers = Collections.synchronizedSet(new HashSet<ServiceTracker>());
165         eventManager.register(this);
166         felixLogger = new FelixLoggerBridge(log);
167         exportsBuilder = new ExportsBuilder();
168     }
169 
170     public void setFelixLogger(final Logger logger)
171     {
172         felixLogger = logger;
173     }
174 
175     public void setDisableMultipleBundleVersions(final boolean val)
176     {
177         disableMultipleBundleVersions = val;
178     }
179 
180     @PluginEventListener
181     public void onStart(final PluginFrameworkStartingEvent event)
182     {
183         start();
184     }
185 
186     @PluginEventListener
187     public void onShutdown(final PluginFrameworkShutdownEvent event)
188     {
189         stop();
190     }
191 
192     @PluginEventListener
193     public void onPluginUpgrade(PluginUpgradedEvent event)
194     {
195         registration.refreshPackages();
196     }
197 
198     @PluginEventListener
199     public void onPluginFrameworkWarmRestarting(PluginFrameworkWarmRestartingEvent event)
200     {
201         registration.loadHostComponents(collectHostComponents(hostComponentProvider));
202     }
203 
204     public void start() throws OsgiContainerException
205     {
206         if (isRunning())
207         {
208             return;
209         }
210 
211         final DefaultComponentRegistrar registrar = collectHostComponents(hostComponentProvider);
212         // Create a case-insensitive configuration property map.
213         final StringMap configMap = new StringMap(false);
214         // Configure the Felix instance to be embedded.
215         configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true");
216         // Add the bundle provided service interface package and the core OSGi
217         // packages to be exported from the class path via the system bundle.
218         configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, exportsBuilder.getExports(registrar.getRegistry(), packageScannerConfig));
219 
220         // Explicitly specify the directory to use for caching bundles.
221         configMap.put(BundleCache.CACHE_PROFILE_DIR_PROP, persistentCache.getOsgiBundleCache().getAbsolutePath());
222 
223         configMap.put(FelixConstants.LOG_LEVEL_PROP, String.valueOf(felixLogger.getLogLevel()));
224         String bootDelegation = getAtlassianSpecificOsgiSystemProperty(OSGI_BOOTDELEGATION);
225         if ((bootDelegation == null) || (bootDelegation.trim().length() == 0))
226         {
227             bootDelegation = "weblogic.*,META-INF.services,com.yourkit.*,com.jprofiler.*,org.apache.xerces.*,com.icl.saxon";
228         }
229 
230         configMap.put(Constants.FRAMEWORK_BOOTDELEGATION, bootDelegation);
231         if (log.isDebugEnabled())
232         {
233             log.debug("Felix configuration: " + configMap);
234         }
235 
236         validateConfiguration(configMap);
237 
238         try
239         {
240             // Create host activator;
241             registration = new BundleRegistration(frameworkBundlesUrl, persistentCache.getFrameworkBundleCache(), registrar);
242             final List<BundleActivator> list = new ArrayList<BundleActivator>();
243             list.add(registration);
244 
245             // Now create an instance of the framework with
246             // our configuration properties and activator.
247             felix = new Felix(felixLogger, configMap, list);
248 
249             // Now start Felix instance.  Starting in a different thread to explicity set daemon status
250             final Runnable start = new Runnable()
251             {
252                 public void run()
253                 {
254                     try
255                     {
256                         Thread.currentThread().setContextClassLoader(null);
257                         felix.start();
258                         felixRunning = true;
259                     }
260                     catch (final BundleException e)
261                     {
262                         throw new OsgiContainerException("Unable to start felix", e);
263                     }
264                 }
265             };
266             final Thread t = threadFactory.newThread(start);
267             t.start();
268 
269             // Give it 10 seconds
270             t.join(10 * 60 * 1000);
271         }
272         catch (final Exception ex)
273         {
274             throw new OsgiContainerException("Unable to start OSGi container", ex);
275         }
276     }
277 
278     /**
279      *
280      * @param configMap The Felix configuration
281      * @throws OsgiContainerException If any validation fails
282      */
283     private void validateConfiguration(StringMap configMap) throws OsgiContainerException
284     {
285         String systemExports = (String) configMap.get(Constants.FRAMEWORK_SYSTEMPACKAGES);
286         validateCaches(systemExports);
287         detectIncorrectOsgiVersion();
288         detectXercesOverride(systemExports);
289     }
290 
291     /**
292      * Detect when xerces has no version, most likely due to an installation of Tomcat where an old version of xerces
293      * is installed into common/lib/endorsed in order to support Java 1.4.
294      *
295      * @param systemExports The system exports
296      * @throws OsgiContainerException If xerces has no version 
297      */
298     void detectXercesOverride(String systemExports) throws OsgiContainerException
299     {
300         int pos = systemExports.indexOf("org.apache.xerces.util");
301         if (pos > -1)
302         {
303             pos += "org.apache.xerces.util".length();
304 
305             // only fail if no xerces found and xerces has no version
306             if (pos >= systemExports.length() || ';' != systemExports.charAt(pos))
307             {
308                 throw new OsgiContainerException(
309                     "Detected an incompatible version of Apache Xerces on the classpath.  If using Tomcat, you may have " +
310                     "an old version of Xerces in $TOMCAT_HOME/common/lib/endorsed that will need to be removed.");
311             }
312         }
313     }
314 
315     /**
316      * Validate caches based on the list of packages exported from the application.  If the list has changed, the cache
317      * directories should be cleared.
318      *
319      * @param systemExports The value of system exports in the header
320      */
321     private void validateCaches(String systemExports)
322     {
323         String cacheKey = String.valueOf(systemExports.hashCode());
324         persistentCache.validate(cacheKey);
325 
326         log.debug("Using Felix bundle cache directory :" + persistentCache.getOsgiBundleCache().getAbsolutePath());
327     }
328 
329     /**
330      * Detects incorrect configuration of WebSphere 6.1 that leaks OSGi 4.0 jars into the application
331      */
332     private void detectIncorrectOsgiVersion()
333     {
334         try
335         {
336             Bundle.class.getMethod("getBundleContext");
337         }
338         catch (final NoSuchMethodException e)
339         {
340             throw new OsgiContainerException(
341                 "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.");
342         }
343     }
344 
345     public void stop() throws OsgiContainerException
346     {
347         if (felixRunning)
348         {
349             for (final ServiceTracker tracker : new HashSet<ServiceTracker>(trackers))
350             {
351                 tracker.close();
352             }
353             felix.stopAndWait();
354         }
355 
356         felixRunning = false;
357         felix = null;
358     }
359 
360     public Bundle[] getBundles()
361     {
362         if (isRunning())
363         {
364             return registration.getBundles();
365         }
366         else
367         {
368             throw new IllegalStateException(
369                 "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.");
370         }
371     }
372 
373     public ServiceReference[] getRegisteredServices()
374     {
375         return felix.getRegisteredServices();
376     }
377 
378     public ServiceTracker getServiceTracker(final String cls)
379     {
380         if (!isRunning())
381         {
382             throw new IllegalStateException("Unable to create a tracker when osgi is not running");
383         }
384 
385         final ServiceTracker tracker = registration.getServiceTracker(cls, trackers);
386         tracker.open();
387         trackers.add(tracker);
388         return tracker;
389     }
390 
391     public Bundle installBundle(final File file) throws OsgiContainerException
392     {
393         try
394         {
395             return registration.install(file, disableMultipleBundleVersions);
396         }
397         catch (final BundleException e)
398         {
399             throw new OsgiContainerException("Unable to install bundle", e);
400         }
401     }
402 
403     DefaultComponentRegistrar collectHostComponents(final HostComponentProvider provider)
404     {
405         final DefaultComponentRegistrar registrar = new DefaultComponentRegistrar();
406         if (provider != null)
407         {
408             provider.provide(registrar);
409         }
410         return registrar;
411     }
412 
413     public boolean isRunning()
414     {
415         return felixRunning;
416     }
417 
418     public List<HostComponentRegistration> getHostComponentRegistrations()
419     {
420         return registration.getHostComponentRegistrations();
421     }
422 
423     private String getAtlassianSpecificOsgiSystemProperty(final String originalSystemProperty)
424     {
425         return System.getProperty(ATLASSIAN_PREFIX + originalSystemProperty);
426     }
427 
428     /**
429      * Manages framework-level framework bundles and host components registration, and individual plugin bundle
430      * installation and removal.
431      */
432     static class BundleRegistration implements BundleActivator, BundleListener, FrameworkListener
433     {
434         private BundleContext bundleContext;
435         private DefaultComponentRegistrar registrar;
436         private List<ServiceRegistration> hostServicesReferences;
437         private List<HostComponentRegistration> hostComponentRegistrations;
438         private final URL frameworkBundlesUrl;
439         private final File frameworkBundlesDir;
440         private final ClassLoader initializedClassLoader;
441         private PackageAdmin packageAdmin;
442 
443         public BundleRegistration(final URL frameworkBundlesUrl, final File frameworkBundlesDir, final DefaultComponentRegistrar registrar)
444         {
445             this.registrar = registrar;
446             this.frameworkBundlesUrl = frameworkBundlesUrl;
447             this.frameworkBundlesDir = frameworkBundlesDir;
448             this.initializedClassLoader = Thread.currentThread().getContextClassLoader();
449         }
450 
451         public void start(final BundleContext context) throws Exception
452         {
453             bundleContext = context;
454             final ServiceReference ref = context.getServiceReference(org.osgi.service.packageadmin.PackageAdmin.class.getName());
455             packageAdmin = (PackageAdmin) context.getService(ref);
456 
457             context.addBundleListener(this);
458             context.addFrameworkListener(this);
459 
460             loadHostComponents(registrar);
461             extractAndInstallFrameworkBundles();
462         }
463 
464         public void stop(final BundleContext ctx) throws Exception
465         {
466             ctx.removeBundleListener(this);
467             ctx.removeFrameworkListener(this);
468             if (hostServicesReferences != null)
469             {
470                 for (ServiceRegistration ref : hostServicesReferences)
471                 {
472                     ref.unregister();
473                 }
474             }
475             bundleContext = null;
476             packageAdmin = null;
477             hostServicesReferences = null;
478             hostComponentRegistrations = null;
479             registrar = null;
480         }
481 
482         public void bundleChanged(final BundleEvent evt)
483         {
484             switch (evt.getType())
485             {
486                 case BundleEvent.INSTALLED:
487                     log.info("Installed bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
488                     break;
489                 case BundleEvent.RESOLVED:
490                     log.info("Resolved bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
491                     break;
492                 case BundleEvent.UNRESOLVED:
493                     log.info("Unresolved bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
494                     break;
495                 case BundleEvent.STARTED:
496                     log.info("Started bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
497                     break;
498                 case BundleEvent.STOPPED:
499                     log.info("Stopped bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
500                     break;
501                 case BundleEvent.UNINSTALLED:
502                     log.info("Uninstalled bundle " + evt.getBundle().getSymbolicName() + " (" + evt.getBundle().getBundleId() + ")");
503                     break;
504             }
505         }
506 
507         public Bundle install(final File path, final boolean uninstallOtherVersions) throws BundleException
508         {
509             boolean bundleUninstalled = false;
510             if (uninstallOtherVersions)
511             {
512                 try
513                 {
514                     JarFile jar = null;
515                     String pluginKey = null;
516                     try
517                     {
518                         jar = new JarFile(path);
519                         pluginKey = OsgiHeaderUtil.getPluginKey(jar.getManifest());
520                     }
521                     finally
522                     {
523                         jar.close();
524                     }
525                     for (final Bundle oldBundle : bundleContext.getBundles())
526                     {
527                         if (pluginKey.equals(OsgiHeaderUtil.getPluginKey(oldBundle)))
528                         {
529                             log.info("Uninstalling existing version " + oldBundle.getHeaders().get(Constants.BUNDLE_VERSION));
530                             oldBundle.uninstall();
531                             bundleUninstalled = true;
532                         }
533                     }
534                 }
535                 catch (final IOException e)
536                 {
537                     throw new BundleException("Invalid bundle format", e);
538                 }
539             }
540             final Bundle bundle = bundleContext.installBundle(path.toURI().toString());
541             if (bundleUninstalled)
542             {
543                 refreshPackages();
544             }
545             return bundle;
546         }
547 
548         public Bundle[] getBundles()
549         {
550             return bundleContext.getBundles();
551         }
552 
553         public ServiceTracker getServiceTracker(final String clazz, final Set<ServiceTracker> trackedTrackers)
554         {
555             return new ServiceTracker(bundleContext, clazz, null)
556             {
557                 @Override
558                 public void close()
559                 {
560                     trackedTrackers.remove(this);
561                 }
562             };
563         }
564 
565         public List<HostComponentRegistration> getHostComponentRegistrations()
566         {
567             return hostComponentRegistrations;
568         }
569 
570         void loadHostComponents(final DefaultComponentRegistrar registrar)
571         {
572             // Unregister any existing host components
573             if (hostServicesReferences != null)
574             {
575                 for (final ServiceRegistration reg : hostServicesReferences)
576                 {
577                     reg.unregister();
578                 }
579             }
580 
581             // Swap the old classloader back in when creating proxies for host components
582             ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
583             try
584             {
585                 Thread.currentThread().setContextClassLoader(initializedClassLoader);
586                 // Register host components as OSGi services
587                 hostServicesReferences = registrar.writeRegistry(bundleContext);
588                 hostComponentRegistrations = registrar.getRegistry();
589             }
590             finally
591             {
592                 Thread.currentThread().setContextClassLoader(oldCl);
593             }
594 
595         }
596 
597         private void extractAndInstallFrameworkBundles() throws BundleException
598         {
599             final List<Bundle> bundles = new ArrayList<Bundle>();
600             com.atlassian.plugin.util.FileUtils.conditionallyExtractZipFile(frameworkBundlesUrl, frameworkBundlesDir);
601             for (final File bundleFile : frameworkBundlesDir.listFiles(new FilenameFilter()
602                 {
603                     public boolean accept(final File file, final String s)
604                     {
605                         return s.endsWith(".jar");
606                     }
607                 }))
608             {
609                 bundles.add(install(bundleFile, false));
610             }
611 
612             for (final Bundle bundle : bundles)
613             {
614                 bundle.start();
615             }
616         }
617 
618         public void refreshPackages()
619         {
620             final CountDownLatch latch = new CountDownLatch(1);
621             FrameworkListener refreshListener = new FrameworkListener()
622             {
623 
624                 public void frameworkEvent(FrameworkEvent event)
625                 {
626                     if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED)
627                     {
628                         log.info("Packages refreshed");
629                         latch.countDown();
630                     }
631                 }
632             };
633 
634             bundleContext.addFrameworkListener(refreshListener);
635             try
636             {
637                 packageAdmin.refreshPackages(null);
638                 boolean refreshed = false;
639                 try
640                 {
641                     refreshed = latch.await(10, TimeUnit.SECONDS);
642                 }
643                 catch (InterruptedException e)
644                 {
645                     // ignore
646                 }
647                 if (!refreshed)
648                 {
649                     log.warn("Timeout exceeded waiting for package refresh");
650                 }
651             }
652             finally
653             {
654                 bundleContext.removeFrameworkListener(refreshListener);
655             }
656         }
657 
658         public void frameworkEvent(FrameworkEvent event)
659         {
660             String bundleBits = "";
661             if (event.getBundle() != null)
662             {
663                 bundleBits = " in bundle " + event.getBundle().getSymbolicName();
664             }
665             switch (event.getType())
666             {
667                 case FrameworkEvent.ERROR:
668                     log.error("Framework error" + bundleBits, event.getThrowable());
669                     break;
670                 case FrameworkEvent.WARNING:
671                     log.warn("Framework warning" + bundleBits, event.getThrowable());
672                     break;
673                 case FrameworkEvent.INFO:
674                     log.info("Framework info" + bundleBits, event.getThrowable());
675                     break;
676             }
677         }
678     }
679 
680 }