2 Replies Latest reply: Jan 12, 2011 5:57 AM by David Bosschaert RSS

Supporting ServiceLoader in Standalone Mode

David Bosschaert Expert

While I was implementing URL Handler support as per the OSGi core specification I did this by plugging into the ModularURLStreamHandlerFactory that is provided by JBoss Modules.

 

This works when running inside AS7 (but: MODULES-44 and MODULES-66) but there are a number of issues that still need to be worked out for standalone mode. My commit (https://github.com/jbosgi/jbosgi-framework/commit/01e9741c296f4849bd48b091c5e53cd5cf6f56af) contains a number of hacks for standalone mode simply to get it working, but I'm hoping that we find a better solution for this.

 

Some of the things that I had to work through...

 

Making the System Module Loader aware of the OSGi Framework Module

The ModularURLStreamHandlerFactory looks up modules by calling Module.getSystemModuleLoader().loadModule(identifier). When running in standalone mode the framework module loader is the OSGi Module Loader. However this module loader isn't known to the System Module loader. At the following location I put in a hack to set the OSGi Module Loader in the System Module Loader:

https://github.com/jbosgi/jbosgi-framework/commit/01e9741c296f4849bd48b091c5e53cd5cf6f56af#L10R77

 

This is clearly not the way to go. An alternative might be to set the System Property "system.module.loader" to the OSGiModuleLoader class name before booting the system. This works to a certain degree, but since the instance is stored in a static final SystemModuleLoaderHolder.INSTANCE variable, it can only be initialized once. This is a problem for the unit tests in JBoss OSGi as they initialize the system repeatedly (e.g. to test various initialization options). This can probably be worked around by putting a holder in there instead of the real instance, but that's a workaround...

Another potential problem with the system property approach is that the system module loader instance has to be created by JBoss Modules using a noarg constructor, which limits the initialization options. Both issues could be resolved by providing an API to set and reset the System Module Loader (MODULES-67).

 

Using the ServiceLoader with the classloaders as set up in Standalone Mode

The ModularURLStreamHandlerFactory at some point uses the java.util.ServiceLoader on the OSGi Framework module classloader to load the resource META-INF/services/java.net.URLStreamHandlerFactory. It then loads the classes found in there (i.e. org.jboss.osgi.framework.plugin.internal.URLHandlerFactory) using that classloader and instantiates them. This is isolated in the FrameworkServiceLoaderTestCase. This didn't work in standalone mode. To make this work I had to export META-INF/services and org.jboss.osgi.framework.plugin.internal locations from the framework. This is done in SystemPackagesPluginImpl.getInternalFrameworkPackages() together with changes in FrameworkLocalLoader, SystemLocalLoader and ModuleManagerPlugin which I'm not very happy with. Hopefully there is a cleaner solution to this. None of these changes are needed when running in AS7.

org.jboss.osgi.framework.plugin.internal
  • 1. Re: Supporting ServiceLoader in Standalone Mode
    Thomas Diesler Master

    I refactored the URL handler logic such that it does not use the ModularURLStreamHandlerFactory in STANDALONE. The trick is to initialize the URLHandlerPlugin before modules is accessed for the first time

     

     

          // The properties on the BundleManager are mutable as long the framework is not created
          // Plugins may modify these properties in their respective constructor
          if (initialProperties != null)
             properties.putAll(initialProperties);
    
          // Register the URL handler plugin
          plugins.put(URLHandlerPlugin.class, new URLHandlerPluginImpl(this));
    
          // Initialize the default module loader
          ModuleLoader mlProp = (ModuleLoader)getProperty(ModuleLoader.class.getName());
          systemModuleLoader = mlProp != null ? mlProp : Module.getSystemModuleLoader();
    
          // Get/Create the service container
          ServiceContainer scProp = (ServiceContainer)getProperty(ServiceContainer.class.getName());
          serviceContainer = scProp != null ? scProp : ServiceContainer.Factory.create();
    
          // Register the framework plugins
          plugins.put(BundleDeploymentPlugin.class, new BundleDeploymentPluginImpl(this));
          plugins.put(BundleStoragePlugin.class, new BundleStoragePluginImpl(this));
    

     

    The factory themselves delegate to the plugin where the logic lives. The framework itself exports META-INF/services

     

       private static Set<String> getExportedPaths(BundleManager bundleManager)
       {
          SystemPackagesPlugin plugin = bundleManager.getPlugin(SystemPackagesPlugin.class);
          HashSet<String> paths = new HashSet<String>(plugin.getExportedPaths());
          paths.add("META-INF/services");
          return paths;
       }
    

     

    Only the url package is exported from the framework.

     

       @Override
       public List<String> getFrameworkPackages()
       {
          List<String> packages = new ArrayList<String>();
          packages.add("org.jboss.msc.service");
          packages.add("org.jboss.osgi.deployment.deployer");
          packages.add("org.jboss.osgi.deployment.interceptor");
          packages.add("org.jboss.osgi.framework.url");
          packages.add("org.jboss.osgi.modules");
    
          packages.add("org.osgi.framework;version=1.5");
          packages.add("org.osgi.framework.hooks;version=1.0");
          packages.add("org.osgi.framework.hooks.service;version=1.0");
          packages.add("org.osgi.framework.launch;version=1.0");
          packages.add("org.osgi.service.condpermadmin;version=1.1");
          packages.add("org.osgi.service.packageadmin;version=1.2");
          packages.add("org.osgi.service.permissionadmin;version=1.2");
          packages.add("org.osgi.service.startlevel;version=1.1");
          packages.add("org.osgi.service.url;version=1.0");
          packages.add("org.osgi.util.tracker;version=1.4");
          return packages;
       }
    
    

     

    Could you please add test coverage in jbosgi so that this can be tested in AS and the Runtime?

  • 2. Re: Supporting ServiceLoader in Standalone Mode
    David Bosschaert Expert

    Thomas Diesler wrote:

     

    I refactored the URL handler logic such that it does not use the ModularURLStreamHandlerFactory in STANDALONE.

    Ah, yes that's another option that I didn't consider - Thanks!

     

    Could you please add test coverage in jbosgi so that this can be tested in AS and the Runtime?

    Yes - this was on my to-do list.