Package org.jboss.mx.loading

Source Code of org.jboss.mx.loading.ClassLoaderUtils$RepositoryClassLoaderComparator

/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.mx.loading;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.lang.reflect.Method;

import org.jboss.logging.Logger;
import org.jboss.mx.loading.LoadMgr3.PkgClassLoader;

/** Utility methods for class loader to package names, etc.
*
* @author Scott.Stark@jboss.org
* @version $Revision: 81022 $
*/
public class ClassLoaderUtils
{
   private static Logger log = Logger.getLogger(ClassLoaderUtils.class);

   /** The singleton instance of repository classloader comparator */
   private static final Comparator repositoryClassLoaderComparator = new RepositoryClassLoaderComparator();

   /** Format a string buffer containing the Class, Interfaces, CodeSource,
    and ClassLoader information for the given object clazz.

    @param clazz the Class
    @param results - the buffer to write the info to
    */
   public static void displayClassInfo(Class clazz, StringBuffer results)
   {
      // Print out some codebase info for the ProbeHome
      ClassLoader cl = clazz.getClassLoader();
      results.append("\n"+clazz.getName()+"("+Integer.toHexString(clazz.hashCode())+").ClassLoader="+cl);
      ClassLoader parent = cl;
      while( parent != null )
      {
         results.append("\n.."+parent);
         URL[] urls = getClassLoaderURLs(parent);
         int length = urls != null ? urls.length : 0;
         for(int u = 0; u < length; u ++)
         {
            results.append("\n...."+urls[u]);
         }
         if( parent != null )
            parent = parent.getParent();
      }
      CodeSource clazzCS = clazz.getProtectionDomain().getCodeSource();
      if( clazzCS != null )
         results.append("\n++++CodeSource: "+clazzCS);
      else
         results.append("\n++++Null CodeSource");

      results.append("\nImplemented Interfaces:");
      Class[] ifaces = clazz.getInterfaces();
      for(int i = 0; i < ifaces.length; i ++)
      {
         Class iface = ifaces[i];
         results.append("\n++"+iface+"("+Integer.toHexString(iface.hashCode())+")");
         ClassLoader loader = ifaces[i].getClassLoader();
         results.append("\n++++ClassLoader: "+loader);
         ProtectionDomain pd = ifaces[i].getProtectionDomain();
         CodeSource cs = pd.getCodeSource();
         if( cs != null )
            results.append("\n++++CodeSource: "+cs);
         else
            results.append("\n++++Null CodeSource");
      }
   }

   /** Use reflection to access a URL[] getURLs or URL[] getClasspath method so
    that non-URLClassLoader class loaders, or class loaders that override
    getURLs to return null or empty, can provide the true classpath info.
    */
   public static URL[] getClassLoaderURLs(ClassLoader cl)
   {
      URL[] urls = {};
      try
      {
         Class returnType = urls.getClass();
         Class[] parameterTypes = {};
         Class clClass = cl.getClass();
         Method getURLs = clClass.getMethod("getURLs", parameterTypes);
         if( returnType.isAssignableFrom(getURLs.getReturnType()) )
         {
            Object[] args = {};
            urls = (URL[]) getURLs.invoke(cl, args);
         }
         if( urls == null || urls.length == 0 )
         {
            Method getCp = clClass.getMethod("getClasspath", parameterTypes);
            if( returnType.isAssignableFrom(getCp.getReturnType()) )
            {
               Object[] args = {};
               urls = (URL[]) getCp.invoke(cl, args);              
            }
         }
      }
      catch(Exception ignore)
      {
      }
      return urls;
   }


   /** Get all of the URLClassLoaders from cl on up the hierarchy
    *
    * @param cl the class loader to start from
    * @return The possibly empty array of URLClassLoaders from cl through
    *    its parent class loaders
    */
   public static URLClassLoader[] getClassLoaderStack(ClassLoader cl)
   {
      ArrayList stack = new ArrayList();
      while( cl != null )
      {
         if( cl instanceof URLClassLoader )
         {
            stack.add(cl);
         }
         cl = cl.getParent();
      }
      URLClassLoader[] ucls = new URLClassLoader[stack.size()];
      stack.toArray(ucls);
      return ucls;
   }

   /** Translates a dot class name (java.lang.String) into a path form
    * suitable for a jar entry (java/lang/String.class)
    *
    * @param className java.lang.String
    * @return java/lang/String.class
    */
   public static String getJarClassName(String className)
   {
      String jarClassName = className.replace('.', '/');
      return jarClassName + ".class";
   }

   /** Parse a class name into its package prefix. This has to handle
      array classes whose name is prefixed with [L.
    */
   public static String getPackageName(String className)
   {
      int startIndex = 0;
      // Strip any leading "[+L" found in array class names
      if( className.length() > 0 && className.charAt(0) == '[' )
      {
         // Move beyond the [...[L prefix
         startIndex = className.indexOf('L') + 1;
      }
     // Now extract the package name
      String pkgName = "";
      int endIndex = className.lastIndexOf('.');
      if( endIndex > 0 )
         pkgName = className.substring(startIndex, endIndex);
      return pkgName;
   }

   /** Parse a class name into its resource form. This has to handle
      array classes whose name is prefixed with [L.
    */
   public static String getResourceName(String className)
   {
      int startIndex = 0;
      // Strip any leading "[+L" found in array class names
      if( className.length() > 0 && className.charAt(0) == '[' )
      {
         // Move beyond the [...[L prefix
         startIndex = className.indexOf('L') + 1;
      }
     // Now extract the package name
      String resName = "";
      int endIndex = className.lastIndexOf('.');
      if( endIndex > 0 )
         resName = className.substring(startIndex, endIndex);
      return resName.replace('.', '/');
   }

   /**
    * Create a new package set
    *
    * @return the new package set
    */
   public static Set newPackageSet()
   {
      return new TreeSet(repositoryClassLoaderComparator);
   }

   /**
    * Clone a package set
    *
    * @param toClone set to clone
    * @return the cloned package set
    */
   public static Set clonePackageSet(Object toClone)
   {
      TreeSet original = (TreeSet) toClone;
      return (Set) original.clone();
   }
     
   /** Augment the package name associated with a UCL.
    * @param cl the UCL that loads from url
    * @param packagesMap the Map<cl, String[]> to update
    * @param url the URL to parse for package names
    * @param prevPkgNames the set of pckage names already associated with cl
    * @return the updated unique set of package names
    * @throws Exception
    */
   public static void updatePackageMap(URL url, PkgNameListener listener)
      throws Exception
   {
      ClassPathIterator cpi = new ClassPathIterator(url);
      updatePackageMap(cpi, listener);
   }

   /** Augment the class names associated with a UCL.
    * @param cl the UCL that loads from url
    * @param classNamesMap the Map<cl, String[]> to update
    * @param url the URL to parse for class names
    * @param prevClassNames the set of pckage names already associated with cl
    * @return the updated list of class names
    * @throws Exception
    */
   public static String[] updateClassNamesMap(Object cl,
      Map classNamesMap, URL url, String[] prevClassNames)
      throws Exception
   {
      ClassPathIterator cpi = new ClassPathIterator(url);
      HashSet classNameSet = null;
      if (prevClassNames == null)
         classNameSet = new HashSet();
      else
         classNameSet = new HashSet(Arrays.asList(prevClassNames));
      return updateClassNamesMap(cl, classNamesMap, cpi, classNameSet);
   }

   static void updatePackageMap(ClassPathIterator cpi, PkgNameListener listener)
      throws Exception
   {
      ClassPathEntry entry;
      while( (entry = cpi.getNextEntry()) != null )
      {
         String name = entry.getName();
         // First look for a META-INF/INDEX.LIST entry
         if( name.equals("META-INF/INDEX.LIST") )
         {
            readJarIndex(cpi, listener);
            // We are done
            break;
         }

         // Skip empty directory entries
         if( entry.isDirectory() == true )
            continue;

         String pkgName = entry.toPackageName();
         listener.addPackage(pkgName);
      }
      cpi.close();
   }

   static String[] updateClassNamesMap(Object cl, Map classNamesMap,
      ClassPathIterator cpi, HashSet classNameSet)
      throws Exception
   {
      boolean trace = log.isTraceEnabled();
      ClassPathEntry entry;
      while( (entry = cpi.getNextEntry()) != null )
      {
         String name = entry.getName();
         // Skip empty directory entries
         if( entry.isDirectory() == true )
            continue;
         // Skip non .class files
         if( name.endsWith(".class") == false )
            continue;

         addClass(name, classNamesMap, cl, trace);
         classNameSet.add(name);
      }
      cpi.close();

      // Return an array of the package names
      String[] classNames = new String[classNameSet.size()];
      classNameSet.toArray(classNames);
      return classNames;
   }

   /** Read the JDK 1.3+ META-INF/INDEX.LIST entry to obtain the package
    names without having to iterate through all entries in the jar.
    */
   private static void readJarIndex(ClassPathIterator cpi, PkgNameListener listener)
      throws Exception
   {
      boolean trace = log.isTraceEnabled();
      InputStream zis = cpi.getInputStream();
      BufferedReader br = new BufferedReader(new InputStreamReader(zis));
      String line;
      // Skip the jar index header
      while( (line = br.readLine()) != null )
      {
         if( line.length() == 0 )
            break;
      }

      // Read the main jar section
      String jarName = br.readLine();
      if( trace )
         log.trace("Reading INDEX.LIST for jar: "+jarName);
      while( (line = br.readLine()) != null )
      {
         if( line.length() == 0 )
            break;
         String pkgName = line.replace('/', '.');
         listener.addPackage(pkgName);
      }
      br.close();
   }

   /** Add a class name to the UCL map.
    * @param jarClassName the class name in the jar (java/lang/String.class)
    * @param classNamesMap the UCL class name mappings
    * @param cl the UCL
    * @param trace the logging trace level flag
    */
   private static void addClass(String jarClassName, Map classNamesMap,
      Object cl, boolean trace)
   {
      LinkedList ucls = (LinkedList) classNamesMap.get(jarClassName);
      if( ucls == null )
      {
         ucls = new LinkedList();
         ucls.add(cl);
         classNamesMap.put(jarClassName, ucls);
      }
      else
      {
         boolean uclIsMapped = ucls.contains(cl);
         if( uclIsMapped == false )
         {
            log.debug("Multiple class loaders found for class: "+jarClassName
               + ", duplicate UCL: "+cl);
            ucls.add(cl);
         }
      }
      if( trace )
         log.trace("Indexed class: "+jarClassName+", UCL: "+ucls.get(0));
   }

   public static interface PkgNameListener
   {
      public void addPackage(String name);
   }

   /**
   */
   static class FileIterator
   {
      LinkedList subDirectories = new LinkedList();
      FileFilter filter;
      File[] currentListing;
      int index = 0;

      FileIterator(File start)
      {
         String name = start.getName();
         // Don't recurse into wars
         boolean isWar = name.endsWith(".war");
         if( isWar )
            currentListing = new File[0];
         else
            currentListing = start.listFiles();
      }
      FileIterator(File start, FileFilter filter)
      {
         String name = start.getName();
         // Don't recurse into wars
         boolean isWar = name.endsWith(".war");
         if( isWar )
            currentListing = new File[0];
         else
            currentListing = start.listFiles(filter);
         this.filter = filter;
      }

      File getNextEntry()
      {
         File next = null;
         if( index >= currentListing.length && subDirectories.size() > 0 )
         {
            do
            {
               File nextDir = (File) subDirectories.removeFirst();
               currentListing = nextDir.listFiles(filter);
            } while( currentListing.length == 0 && subDirectories.size() > 0 );
            index = 0;
         }
         if( index < currentListing.length )
         {
            next = currentListing[index ++];
            if( next.isDirectory() )
               subDirectories.addLast(next);
         }
         return next;
      }
   }

   /**
    */
   static class ClassPathEntry
   {
      String name;
      ZipEntry zipEntry;
      File fileEntry;

      ClassPathEntry(ZipEntry zipEntry)
      {
         this.zipEntry = zipEntry;
         this.name = zipEntry.getName();
      }
      ClassPathEntry(File fileEntry, int rootLength)
      {
         this.fileEntry = fileEntry;
         this.name = fileEntry.getPath().substring(rootLength);
      }

      String getName()
      {
         return name;
      }
      /** Convert the entry path to a package name
       */
      String toPackageName()
      {
         String pkgName = name;
         char separatorChar = zipEntry != null ? '/' : File.separatorChar;
         int index = name.lastIndexOf(separatorChar);
         if( index > 0 )
         {
            pkgName = name.substring(0, index);
            pkgName = pkgName.replace(separatorChar, '.');
         }
         else
         {
            // This must be an entry in the default package (e.g., X.class)
            pkgName = "";
         }
         return pkgName;
      }

      boolean isDirectory()
      {
         boolean isDirectory = false;
         if( zipEntry != null )
            isDirectory = zipEntry.isDirectory();
         else
            isDirectory = fileEntry.isDirectory();
         return isDirectory;
      }
   }

   /** An iterator for jar entries or directory structures.
   */
   static class ClassPathIterator
   {
      ZipInputStream zis;
      FileIterator fileIter;
      File file;
      int rootLength;

      ClassPathIterator(URL url) throws IOException
      {
         String protocol = url != null ? url.getProtocol() : null;
         if( protocol == null )
         {
         }
         else if( protocol.equals("file") )
         {
            File tmp = new File(url.getFile());
            if( tmp.isDirectory() )
            {
               rootLength = tmp.getPath().length() + 1;
               fileIter = new FileIterator(tmp);
            }
            else
            {
               // Assume this is a jar archive
               InputStream is = new FileInputStream(tmp);
               zis = new ZipInputStream(is);
            }
         }
         else
         {
            // Assume this points to a jar
            InputStream is = url.openStream();
            zis = new ZipInputStream(is);
         }
      }

      ClassPathEntry getNextEntry() throws IOException
      {
         ClassPathEntry entry = null;
         if( zis != null )
         {
            ZipEntry zentry = zis.getNextEntry();
            if( zentry != null )
               entry = new ClassPathEntry(zentry);
         }
         else if( fileIter != null )
         {
            File fentry = fileIter.getNextEntry();
            if( fentry != null )
               entry = new ClassPathEntry(fentry, rootLength);
            file = fentry;
         }

         return entry;
      }

      InputStream getInputStream() throws IOException
      {
         InputStream is = zis;
         if( zis == null )
         {
            is = new FileInputStream(file);
         }
         return is;
      }

      void close() throws IOException
      {
         if( zis != null )
            zis.close();
      }

   }
  
   /**
    * A comparator for comparing repository classloaders
    */
   private static class RepositoryClassLoaderComparator implements Comparator
   {
      /**
       * Compares two repository classloaders, they are ordered by:
       * 1) parent->child delegation rules in the loader repository
       * 2) added order inside the loader repository
       */
      public int compare(Object o1, Object o2)
      {
         if (o1 instanceof PkgClassLoader)
         {
            PkgClassLoader pkg1 = (PkgClassLoader) o1;
            PkgClassLoader pkg2 = (PkgClassLoader) o2;
            RepositoryClassLoader rcl1 = pkg1.ucl;
            RepositoryClassLoader rcl2 = pkg2.ucl;
            // We use the package classloader ordering before the repository order
            int test = (pkg1.order - pkg2.order);
            if (test != 0)
               return test;
            else
               return rcl1.getAddedOrder() - rcl2.getAddedOrder();
         }
         else
         {
            RepositoryClassLoader rcl1 = (RepositoryClassLoader) o1;
            RepositoryClassLoader rcl2 = (RepositoryClassLoader) o2;
            return rcl1.getAddedOrder() - rcl2.getAddedOrder();
           
            // REVIEW: Alternative to using the pkgClassLoader is
            //         ordering based on the loader repository
           
            //LoaderRepository lr1 = rcl1.getLoaderRepository();
            //LoaderRepository lr2 = rcl2.getLoaderRepository();

            // Are the loader repositories ordered?
            //int test = lr1.compare(lr2);
            //if (test != 0)
            //   return test;
            //else
            //   return rcl1.getAddedOrder() - rcl2.getAddedOrder();
         }
      }
   }
}
TOP

Related Classes of org.jboss.mx.loading.ClassLoaderUtils$RepositoryClassLoaderComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.