2 Replies Latest reply on Jan 26, 2010 12:22 PM by thomas.diesler

    Initial support for Fragments

    thomas.diesler

      OSGiBundleManager makes the distinction between bundles and fragments based on Fragment-Host in the manifest. The appropriate

      state object is attached.

       

       

         public AbstractBundleState addDeployment(DeploymentUnit unit)
         {
               ...
      
               OSGiMetaData osgiMetaData = unit.getAttachment(OSGiMetaData.class);
               ParameterizedAttribute fragmentHost = osgiMetaData.getFragmentHost();
               if (fragmentHost != null)
               {
                  // Create a new OSGiFragmentState
                  OSGiFragmentState fragmentState = new OSGiFragmentState(unit);
                  absBundle = fragmentState;
                  addBundle(fragmentState);
               }
               else
               {
                  // Create a new OSGiBundleState
                  AbstractDeployedBundleState bundleState = new OSGiBundleState(unit);
                  absBundle = bundleState;
                  addBundle(bundleState);
               }
               
               // Attach the abstract bundle state
               unit.addAttachment(AbstractBundleState.class, absBundle);
      
      

       

      There are two ClassLoadingMetaData deployers.

       

      * OSGiBundleClassLoadingDeployer

      * OSGiFragmentClassLoadingDeployer

       

      The former does (currently) nothing special additional to consuming OSGiMetaData and constructing/configuring the OSGiClassLoadingMetaData. The latter addtionally configures FragmentHostMetaData

       

      Fragment attachment is supposed to happen during bundle resolve phase.

       

      OSGiFragmentAttachmentDeployer iterate over all installed fragments and attaches the OSGiFragmentState to the host's OSGiBundleState when appropriate. It then creates a DelegateLoader for the fragment and adds it to the host's OSGiClassLoaderPolicy.

       

      All of this should really be handled at the underlying CL layer. There should be support for fragments in ClassLoadingMetaData, which should create the appropriate delegates in the underlying policy.

       

      Resource and class loading from bundles is (due to the above limitations) handled in the OSGiClassLoaderPolicy and OSGiClassLoader respectively.

       

         @Override
         public URL getResource(String path)
         {
            URL resourceURL = super.getResource(path);
            
            // Try to find the resource in the attached fragments
            if (resourceURL == null && fragmentLoaders != null)
            {
               for (DelegateLoader fragLoader : fragmentLoaders)
               {
                  resourceURL = fragLoader.getResource(path);
                  if (resourceURL != null)
                     break;
               }
            }
            
            return resourceURL;
         }
      
      

       

       

         @Override
         public Class<?> loadClass(String className) throws ClassNotFoundException
         {
            try
            {
               Class<?> clazz = super.loadClass(className);
               return clazz;
            }
            catch (ClassNotFoundException ex)
            {
               // Try to load the class in the attached fragments
               List<DelegateLoader> fragmentLoaders = osgiPolicy.getFragmentLoaders();
               if (fragmentLoaders != null)
               {
                  for (DelegateLoader fragLoader : fragmentLoaders)
                  {
                     Class<?> clazz = fragLoader.loadClass(className);
                     if (clazz != null)
                        return clazz;
                  }
               }
               
               // Throw the ClassNotFoundException 
               throw ex;
            }
         }

       

      which is both incorrect and only works for the most basic scenarios. See JBCL-137 for progress on this.

       

      Test coverage is here


       

      $ mvn -Dtest=fragments/** test

       

      Running org.jboss.test.osgi.fragments.FragmentTestCase
      Tests run: 6, Failures: 0, Errors: 0, Skipped: 3, Time elapsed: 3.932 sec

        • 1. Re: Initial support for Fragments
          thomas.diesler

          I pulled up the notion of dynamically attachable fragment root files to VFSClassLoaderPolicy

           

             /**
              * Attach a new fragment root to the policy.
              * @param fragRoot The fragment root file
              */
             public void attachFragment(VirtualFile fragRoot)
             {
                if (fragRoot == null)
                   throw new IllegalArgumentException("Null fragment file");
                
                if (fragments == null)
                   fragments = new CopyOnWriteArrayList<VirtualFile>();
                
                fragments.add(fragRoot);
             }
             
             /**
              * Detach a fragment root from the policy.
              * @param fragRoot The fragment root file
              * @return true if the fragment could be detached
              */
             public boolean detachFragment(VirtualFile fragRoot)
             {
                if (fragRoot == null)
                   throw new IllegalArgumentException("Null fragment file");
                
                if (fragments == null)
                   return false;
                
                return fragments.remove(fragRoot);
             }
             
             /**
              * Get the array of attached fragment root files.
              * @return The array of attached fragment root files or null.
              */
             public VirtualFile[] getFragmentRoots()
             {
                if (fragments == null)
                   return null;
                
                VirtualFile[] retarr = new VirtualFile[fragments.size()];
                fragments.toArray(retarr);
                return retarr;
             }

           

          By default VFSClassLoaderPolicy has no fragments attached.

           

          If the policy cannot find a resource in the set of its root files, it falls back to list of attached fragment root files

           

             /**
              * Find the virtual file information for a path
              * 
              * @param path the path
              * @return the virtual file information
              */
             protected VirtualFileInfo findVirtualFileInfo(String path)
             {
                VirtualFileInfo result = vfsCache.get(path);
                if (result != null)
                   return result;
                
                for (VirtualFile root : roots)
                {
                   try
                   {
                      VirtualFile file = root.getChild(path);
                      if (file != null)
                      {
                         result = new VirtualFileInfo(file, root);
                         vfsCache.put(path, result);
                         return result;
                      }
                   }
                   catch (Exception ignored)
                   {
                   }
                }
                
                if (fragments != null)
                {
                   for (VirtualFile root : fragments)
                   {
                      try
                      {
                         VirtualFile file = root.getChild(path);
                         if (file != null)
                         {
                            result = new VirtualFileInfo(file, root);
                            vfsCache.put(path, result);
                            return result;
                         }
                      }
                      catch (Exception ignored)
                      {
                      }
                   }
                }
                
                return null;
             }

           

          As a result, the hacks in OSGiBundleClassLoader.loadClass(String classname) and OSGiClassLoaderPolicy.getResource(String path) have been removed, which enables fragment support in jbossas where we have no control over the CL domain to create a custom OSGiBundleClassLoader.

           

          Adrian, posted in https://community.jboss.org/thread/97003

           

          4) Allow fragments - i.e. additions to the "classpath" of a classloader

          ...


          I think (4) would be better implemented in the deployers by allowing one deployment
          to dynamically become a subdeployment of another one. But this still requires
          a mechanism to augment the classpath roots in the VFSClassLoaderPolicy.

          • 2. Re: Initial support for Fragments
            thomas.diesler

            The reason to pull fragment support up to the base layer was that

             

               protected VirtualFileInfo findVirtualFileInfo(String path)
               {
                   ...
               }
            

             

            cannot really be subclassed (despite its signature) because the return type VirtualFileInfo was private.

             

            I promoted this type to top-level, which now allows me to do the fragment handling for class and resource lookups in OSGiClassLoaderPolicy.