Package net.sourceforge.javautil.classloader.impl

Source Code of net.sourceforge.javautil.classloader.impl.ClassContext

package net.sourceforge.javautil.classloader.impl;

import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.jar.Attributes.Name;

import net.sourceforge.javautil.classloader.resolver.IClassDependencyPool;
import net.sourceforge.javautil.classloader.resolver.IClassPackage;
import net.sourceforge.javautil.classloader.resolver.impl.ClassDependencyPoolImpl;
import net.sourceforge.javautil.classloader.source.ClassSource;
import net.sourceforge.javautil.classloader.source.CompositeClassSource;
import net.sourceforge.javautil.common.exception.ThrowableManagerRegistry;
import net.sourceforge.javautil.common.io.VirtualArtifactNotFoundException;
import net.sourceforge.javautil.common.logging.ILogger;
import net.sourceforge.javautil.common.logging.LoggingContext;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;

/**
* A class context is a {@link ClassLoader} whose class resolving logic is defined by
* an arbitrary {@link IClassLoaderHeiarchy}.
*
* @author elponderador
* @author $Author: ponderator $
* @version $Id: ClassContext.java 2709 2010-12-31 00:46:45Z ponderator $
*/
public class ClassContext extends URLClassLoader {

  protected IClassLoaderHeiarchy heiarchy;
 
  protected long modified = System.currentTimeMillis();
  protected CompositeClassSource all;
  protected CompositeClassSource nonPackageResources;
 
  protected IClassDependencyPool pool;
  protected ClassFileTransformer transformer;
 
  protected ILogger log = LoggingContext.getContextualLogger(ClassContext.class);

  /**
   * This will store the pool used and extract the class sources for it.
   *
   * @see #ClassContext(IClassLoaderHeiarchy, Collection)
   */
  public ClassContext(IClassLoaderHeiarchy heiarchy, ClassDependencyPoolImpl pool) {
    this(heiarchy, pool, new ClassSource[0]);
  }
 
  /**
   * This will take the sources collection which will be translated and passed onto as an array.
   *
   * @see #ClassContext(IClassLoaderHeiarchy, ClassSource...)
   */
  public ClassContext(IClassLoaderHeiarchy heiarchy, Collection<ClassSource> sources) {
    this(heiarchy, sources.toArray(new ClassSource[sources.size()]));
  }
 
  /**
   * @param heiarchy The heiarchy to use
   * @param sources One or more sources that compose what this class context refers to
   */
  public ClassContext(IClassLoaderHeiarchy heiarchy, ClassSource... sources) {
    this(heiarchy, null, sources);
  }
 
  public ClassContext (IClassLoaderHeiarchy heiarchy, IClassDependencyPool pool, ClassSource... sources) {
    super(new URL[0]);
    this.heiarchy = heiarchy;
    this.nonPackageResources = sources.length == 1 && sources[0] instanceof CompositeClassSource ?
        (CompositeClassSource) sources[0] : new CompositeClassSource(sources);
    this.setPool(pool);
   
    log.info("Class Loader Configuration: ");
    for (ClassSource src : this.nonPackageResources.getAllNonCompositeSources()) {
      log.info("   ----> " + src);
    }
   
    if (this.pool != null) {
      log.info(" ");
      for (IClassPackage pkg : this.pool.getPackages(true)) {
        ClassSource src = pkg.getMainJarSource();
        log.info("   ----> " + pkg + "/" + src);
        src.cleanup();
      }
    }
  }

  @Override public URL[] getURLs() {
    return this.getAllSources().getUrls().toArray(new URL[0]);
  }

  /**
   * @return The dependency pool that was provided (when using {@link #ClassContext(IClassLoaderHeiarchy, ClassDependencyPoolImpl)} or
   * {@link #setPool(ClassDependencyPoolImpl)}), or null if none was provided
   */
  public IClassDependencyPool getPool() { return pool; }
 
  /**
   * If this is called after the pool has been initialized, it will throw an {@link IllegalArgumentException}.
   *
   * @param pool The pool related to this context
   * @return A reference to this object for chaining
   */
  public ClassContext setPool(IClassDependencyPool pool) {
    if (this.pool != null) throw new IllegalArgumentException("A pool has already been assigned to this class context");
   
    this.all = pool == null ? this.nonPackageResources : new CompositeClassSource(pool.getCompositeClassSource(), this.nonPackageResources);
    this.pool = pool;

    if (this.pool != null) {
      log.info(" ");
      for (IClassPackage pkg : this.pool.getPackages(true)) {
        ClassSource src = pkg.getMainJarSource();
        log.info("   ----> " + pkg + "/" + src);
        src.cleanup();
      }
    }
   
    return this;
  }

  /**
   * @return The composite class source composing non {@link IClassPackage} sources
   */
  public CompositeClassSource getNonPackageSources() { return this.nonPackageResources; }

  /**
   * @return The heiarchy logic to use in connection with this class context
   */
  public IClassLoaderHeiarchy getHeiarchy() { return heiarchy; }

  /**
   * @return The transformer used by this class loader, or null if no transformer is being used
   */
  public ClassFileTransformer getTransformer() { return transformer; }
  public ClassContext setTransformer(ClassFileTransformer transformer) { this.transformer = transformer; return this; }
 
  /**
   * @return All class sources including non {@link IClassPackage} sources and {@link IClassPackage} sources
   */
  public CompositeClassSource getAllSources () {
    if (this.pool != null && this.pool.getLastModified() > this.modified) {
      this.modified = this.pool.getLastModified();
      this.all = new CompositeClassSource(this.pool.getCompositeClassSource(), this.nonPackageResources);
     
      log.info(" ");
      for (IClassPackage pkg : this.pool.getPackages(true)) {
        ClassSource src = pkg.getMainJarSource();
        log.info("   ----> " + pkg + "/" + src);
        src.cleanup();
      }
    }
    return this.all;
  }

  @Override protected String findLibrary(String libname) {
    URL url = this.getAllSources().getResource(System.mapLibraryName(libname));
    if (url != null && url.toExternalForm().startsWith("file:/")) {
      return url.toExternalForm().substring(6);
    }
   
    return null;
  }

  @Override protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    try {
      return this.loadClass( heiarchy.createRootContext(this, name, resolve) );
    } catch (LinkageError error) {
      if (this.pool != null) {
        this.pool.printHeiarchy(System.out);
      }
      throw error;
    }
  }

  public synchronized Class<?> loadClass(String name) throws ClassNotFoundException {
    return this.loadClass( heiarchy.createRootContext(this, name, false) );
  }
 
  /**
   * @param ctx The current class loading context
   * @return The class that is specified by the context
   * @throws ClassNotFoundException
   */
  public Class<?> loadClass(IClassLoadingContext ctx) throws ClassNotFoundException {
    while ( heiarchy.assignNextClassLoader(ctx) ) {
      try {
        ClassLoader loader = ctx.getCurrentLoader();
        if (loader instanceof ClassContext) {
         
          if (loader == this) {
           
            return this.loadClass(ctx.getSearchInfo(), ctx.isResolveNeeded());
           
          } else if (loader == heiarchy.getParent()) {
           
            return ((ClassContext)loader).resolveFromChild(this, ctx);
           
          } else return ((ClassContext)loader).loadClass( heiarchy.createChildContext(this, (ClassContext)loader, ctx ) );
       
        } else return loader.loadClass(ctx.getSearchInfo().getFullClassName());
      } catch (ClassNotFoundException e) {}
    }
   
//    if (getParent() instanceof ClassContext) return ((ClassContext)getParent()).resolveFromChild(this, ctx);
//    else return getParent().loadClass(ctx.getSearchInfo().getFullClassName());
   
    throw new ClassNotFoundException(ctx.getSearchInfo().getFullClassName());
  }
 
  /**
   * @param csi The search info for the class in question
   * @param resolve True if it should be resolved, otherwise false
   * @return The class that was found
   * @throws ClassNotFoundException
   */
  protected synchronized Class<?> loadClass (ClassSearchInfo csi, boolean resolve) throws ClassNotFoundException {
    Class<?> clazz = this.findLoadedClass(csi.getFullClassName());
   
    if (clazz == null) clazz = this.findClass(csi);
   
    if (resolve) {
      try {
        this.resolveClass(clazz);
      } catch (LinkageError error) {
        if (this.pool != null) this.pool.printHeiarchy(System.out);
        throw error;
      }
    }
   
    return clazz;
  }

  @Override protected Class<?> findClass(String name) throws ClassNotFoundException {
    return this.findClass(new ClassSearchInfo(name));
  }
 
  /**
   * @param csi The search info for the class in question
   * @return The class that was found
   * @throws ClassNotFoundException
   */
  protected Class<?> findClass (ClassSearchInfo csi) throws ClassNotFoundException {
    ClassInfo info = this.getAllSources().getIfHasClass(csi);
   
    if (info == null) throw new ClassNotFoundException(csi.getFullClassName());
   
    return this.defineClass(info);
  }
 
  /**
   * @param info The info about the found class
   * @return A byte[] compiled class, this will take care of package sealing
   * @throws ClassNotFoundException
   */
  protected Class<?> defineClass (ClassInfo info) throws ClassNotFoundException {
    byte[] data = info.getClassData();
   
    try {
      if (transformer != null)
        data = transformer.transform(this, info.getFullyQualifiedName(), null, null, data);
    } catch (IllegalClassFormatException e) {
      throw ThrowableManagerRegistry.caught(e);
    }

    String pkgname = info.getPackageName();
    if (pkgname != null && !pkgname.equals("")) {
      Package pkg = getPackage(pkgname);
      Manifest man = info.getSource().getManifest();
      if (pkg != null) info.getSource().verifySeal(pkg, man);
      else {
        if (man != null) definePackage(pkgname, man, info.getSource().getURL());
        else definePackage(pkgname, null, null, null, null, null, null, null);
      }
    }
   
    try {
      return defineClass(info.getFullyQualifiedName(), data, 0, data.length);
    } catch (LinkageError e) {
      throw new ClassLoadingException("Could not define class: " + info.getFullyQualifiedName(), e);
    }
  }

  /**
   * @param name The name of the package
   * @param man The corresponding manifest file
   * @param url The url of the sealed jar, maybe null
   * @return The newly created package
   * @throws IllegalArgumentException
   */
  protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException {
    String path = name.replace('.', '/').concat("/");
    String specTitle = null, specVersion = null, specVendor = null;
    String implTitle = null, implVersion = null, implVendor = null;
    String sealed = null;
    URL sealBase = null;

    Attributes attr = man.getAttributes(path);
    if (attr != null) {
        specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
        specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
        specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
        implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
        implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
        implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
        sealed      = attr.getValue(Name.SEALED);
    }

    attr = man.getMainAttributes();
   
    if (attr != null) {
      if (specTitle == null) specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
      if (specVersion == null) specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
      if (specVendor == null) specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
      if (implTitle == null) implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
      if (implVersion == null) implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
      if (implVendor == null) implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
      if (sealed == null) sealed = attr.getValue(Name.SEALED);
    }
   
    if ("true".equalsIgnoreCase(sealed)) sealBase = url;
    return definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
  }

  @Override public URL findResource(String name) {
    try {
      return this.getAllSources().getResource(name);
    } catch (VirtualArtifactNotFoundException e) {
      return null;
    }
  }

  @Override public Enumeration<URL> findResources(String name) throws IOException {
    if (this.getAllSources() instanceof CompositeClassSource) {
      return Collections.enumeration( this.getAllSources().getResources(name) );
    } else {
      List<URL> url = new ArrayList<URL>();
      url.add( this.findResource(name) );
      return Collections.enumeration( url );
    }
  }
 
  /**
   * @param child The child context using this context for child linking/resolution
   * @param ctx The current class loading context
   * @return The class found
   * @throws ClassNotFoundException
   */
  public Class<?> resolveFromChild (ClassContext child, IClassLoadingContext ctx) throws ClassNotFoundException {
    return this.loadClass( heiarchy.createParentContext(this, child, ctx) );
  }

  @Override public URL getResource(String name) {
    return this.getResource( heiarchy.createRootResourceContext(this, name) );
  }
 
  /**
   * @param ctx The current resource loading context
   * @return A URL pointing to the resource, or null if it could not be found
   */
  public URL getResource (IClassResourceLoadingContext ctx) {
    URL url = this.findResource(ctx.getResourcePath());
    if (url != null) return url;
   
    while ( url == null && heiarchy.assignNextResourceLoader( ctx ) ) {
     
      ClassLoader loader = ctx.getCurrentLoader();
     
      if (loader == this) continue;
      if (loader instanceof ClassContext) {
       
        if (loader == heiarchy.getParent()) {
         
          url = ((ClassContext)loader).resolveFromChild(this, ctx);
         
        } else url = ((ClassContext)loader).getResource( heiarchy.createChildResourceContext(this, (ClassContext) loader, ctx) );
       
      } else url = loader.getResource(ctx.getResourcePath());
     
    }
   
    return url;
  }
 
  /**
   * @param child The child context call this context as a parent
   * @param ctx The resource loading context currently in use
   * @return A URL from this parent for the resource, or null if it could not be located
   */
  public URL resolveFromChild (ClassContext child, IClassResourceLoadingContext ctx) {
    return this.getResource( heiarchy.createParentResourceContext(this, child, ctx) );
  }

  @Override public InputStream getResourceAsStream(String name) {
    try {
      URL url = this.getResource(name);
      return url == null ? null : url.openStream();
    } catch (IOException e) {
      throw ThrowableManagerRegistry.caught(e);
    }
  }
 
  @Override public Enumeration<URL> getResources(String name) throws IOException {
    return Collections.enumeration( this.getResources( heiarchy.createRootResourceContext(this, name) ) );
  }
 
  /**
   * @param ctx The current resource loading context
   * @return A list of URL's (possibly empty) pointing to the resources that matched
   * @throws IOException
   */
  public List<URL> getResources(IClassResourceLoadingContext ctx) throws IOException {
    Set<URL> urls = new LinkedHashSet<URL>(this.getAllSources().getResources(ctx.getResourcePath()));
   
    while ( heiarchy.assignNextResourceLoader( ctx ) ) {
     
      ClassLoader loader = ctx.getCurrentLoader();
     
      if (loader == this) continue;
      if (loader instanceof ClassContext) {
       
        urls.addAll( ((ClassContext)loader).getResources( heiarchy.createChildResourceContext(this, (ClassContext) loader, ctx) ) );
       
      } else {
       
        urls.addAll( Collections.list( loader.getResources(ctx.getResourcePath()) ) );
     
      }
     
    }
   
    return new ArrayList<URL>(urls);
  }
 
  /**
   * This MUST be called before discarding use of object, in order to allow internal system
   * links to be cleared, if any.
   */
  public void cleanup () { ClassCache.getCache().remove(this); this.getAllSources().cleanup(); }
 
}
TOP

Related Classes of net.sourceforge.javautil.classloader.impl.ClassContext

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.