alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (BytecodeGen.java)

This example Java source code file (BytecodeGen.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

bridgeclassloader, class, classloader, classnotfoundexception, constructor, guice_class_loader, loading, log, logging, method, object, override, public, reflection, same_package, security, string, visibility

The BytecodeGen.java Java example source code

/**
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.inject.internal;

import static com.google.inject.internal.InternalFlags.getCustomClassLoadingOption;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.inject.internal.InternalFlags.CustomClassLoadingOption;

import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Utility methods for runtime code generation and class loading. We use this stuff for {@link
 * net.sf.cglib.reflect.FastClass faster reflection}, {@link net.sf.cglib.proxy.Enhancer method
 * interceptors} and to proxy circular dependencies.
 *
 * <p>When loading classes, we need to be careful of:
 * <ul>
 *   <li>Memory leaks. Generated classes need to be garbage collected in long-lived
 *       applications. Once an injector and any instances it created can be garbage collected, the
 *       corresponding generated classes should be collectable.
 *   <li>Visibility. Containers like OSGi use class loader boundaries
 *       to enforce modularity at runtime.
 * </ul>
 *
 * <p>For each generated class, there's multiple class loaders involved:
 * <ul>
 *    <li>The related class's class loader. Every generated class services exactly
 *        one user-supplied class. This class loader must be used to access members with protected
 *        and package visibility.
 *    <li>Guice's class loader.
 *    <li>Our bridge class loader. This is a child of the user's class loader. It
 *        selectively delegates to either the user's class loader (for user classes) or the Guice
 *        class loader (for internal classes that are used by the generated classes). This class
 *        loader that owns the classes generated by Guice.
 * </ul>
 *
 * @author mcculls@gmail.com (Stuart McCulloch)
 * @author jessewilson@google.com (Jesse Wilson)
 */
public final class BytecodeGen {

  static final Logger logger = Logger.getLogger(BytecodeGen.class.getName());

  static final ClassLoader GUICE_CLASS_LOADER = canonicalize(BytecodeGen.class.getClassLoader());

  // initialization-on-demand...
  private static class SystemBridgeHolder {
    static final BridgeClassLoader SYSTEM_BRIDGE = new BridgeClassLoader();
  }

  /** ie. "com.google.inject.internal" */
  static final String GUICE_INTERNAL_PACKAGE
      = BytecodeGen.class.getName().replaceFirst("\\.internal\\..*$", ".internal");

  /*if[AOP]*/
  /** either "net.sf.cglib", or "com.google.inject.internal.cglib" */
  static final String CGLIB_PACKAGE
      = net.sf.cglib.proxy.Enhancer.class.getName().replaceFirst("\\.cglib\\..*$", ".cglib");

  static final net.sf.cglib.core.NamingPolicy FASTCLASS_NAMING_POLICY
      = new net.sf.cglib.core.DefaultNamingPolicy() {
    @Override protected String getTag() {
      return "ByGuice";
    }

    @Override
    public String getClassName(String prefix, String source, Object key,
        net.sf.cglib.core.Predicate names) {
      // we explicitly set the source here to "FastClass" so that our jarjar renaming
      // to $FastClass doesn't leak into the class names.  if we did not do this,
      // classes would end up looking like $$$FastClassByGuice$$, with the extra $
      // at the front.
      return super.getClassName(prefix, "FastClass", key, names);
    }
  };

  static final net.sf.cglib.core.NamingPolicy ENHANCER_NAMING_POLICY
      = new net.sf.cglib.core.DefaultNamingPolicy() {
    @Override
    protected String getTag() {
      return "ByGuice";
    }

    @Override
    public String getClassName(String prefix, String source, Object key,
        net.sf.cglib.core.Predicate names) {
      // we explicitly set the source here to "Enhancer" so that our jarjar renaming
      // to $Enhancer doesn't leak into the class names.  if we did not do this,
      // classes would end up looking like $$$EnhancerByGuice$$, with the extra $
      // at the front.
      return super.getClassName(prefix, "Enhancer", key, names);
    }
  };
  /*end[AOP]*/
  /*if[NO_AOP]
  private static final String CGLIB_PACKAGE = " "; // any string that's illegal in a package name
  end[NO_AOP]*/

  /**
   * Weak cache of bridge class loaders that make the Guice implementation
   * classes visible to various code-generated proxies of client classes.
   */
  private static final LoadingCache<ClassLoader, ClassLoader> CLASS_LOADER_CACHE;

  static {
    CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder().weakKeys().weakValues();
    if (getCustomClassLoadingOption() == CustomClassLoadingOption.OFF) {
      builder.maximumSize(0);
    }
    CLASS_LOADER_CACHE = builder.build(
        new CacheLoader<ClassLoader, ClassLoader>() {
          @Override public ClassLoader load(final ClassLoader typeClassLoader) {
            logger.fine("Creating a bridge ClassLoader for " + typeClassLoader);
            return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
              public ClassLoader run() {
                return new BridgeClassLoader(typeClassLoader);
              }
            });
          }
        });
  }

  /**
   * Attempts to canonicalize null references to the system class loader.
   * May return null if for some reason the system loader is unavailable.
   */
  private static ClassLoader canonicalize(ClassLoader classLoader) {
    return classLoader != null ? classLoader : SystemBridgeHolder.SYSTEM_BRIDGE.getParent();
  }

  /**
   * Returns the class loader to host generated classes for {@code type}.
   */
  public static ClassLoader getClassLoader(Class<?> type) {
    return getClassLoader(type, type.getClassLoader());
  }

  private static ClassLoader getClassLoader(Class<?> type, ClassLoader delegate) {

    // simple case: do nothing!
    if (getCustomClassLoadingOption() == CustomClassLoadingOption.OFF) {
      return delegate;
    }

    // java.* types can be seen everywhere
    if (type.getName().startsWith("java.")) {
      return GUICE_CLASS_LOADER;
    }

    delegate = canonicalize(delegate);

    // no need for a bridge if using same class loader, or it's already a bridge
    if (delegate == GUICE_CLASS_LOADER || delegate instanceof BridgeClassLoader) {
      return delegate;
    }

    // don't try bridging private types as it won't work
    if (Visibility.forType(type) == Visibility.PUBLIC) {
      if (delegate != SystemBridgeHolder.SYSTEM_BRIDGE.getParent()) {
        // delegate guaranteed to be non-null here
        return CLASS_LOADER_CACHE.getUnchecked(delegate);
      }
      // delegate may or may not be null here
      return SystemBridgeHolder.SYSTEM_BRIDGE;
    }

    return delegate; // last-resort: do nothing!
  }

  /*if[AOP]*/
  // use fully-qualified names so imports don't need preprocessor statements
  /**
   * Returns a FastClass proxy for invoking the given member or {@code null} if access rules 
   * disallow it.
   * 
   * @see #newFastClassForMember(Class, Member) for a full description
   */
  public static net.sf.cglib.reflect.FastClass newFastClassForMember(Member member) {
    return newFastClassForMember(member.getDeclaringClass(), member);
  }

  /**
   * Returns a FastClass proxy for invoking the given member or {@code null} if access rules 
   * disallow it.
   * 
   * <p>FastClass works by generating a type in the same package as the target {@code type}. This
   * may or may not work depending on the access level of the class/member. It breaks down into the
   * following cases depending on accessibility:
   * <ul>
   *   <li>Public: This always works since we can generate the type into the 
   *   {@link BridgeClassLoader} which ensures there are no versioning issues.
   *   <li>Package private and Protected: This works as long as:
   *   <ul> 
   *     <li>We can generate into the same classloader as the type.  This is not possible for JDK
   *     types which use the 'bootstrap' loader.
   *     <li>The classloader of the type has the same version of {@code FastClass} as we do.  This
   *     may be violated when running in OSGI bundles.
   *   </ul>
   *   <li>Private: This never works.
   * </ul>
   * 
   * If we are unable to generate the type, then we return null and callers should work around by
   * using normal java reflection.
   */
  public static net.sf.cglib.reflect.FastClass newFastClassForMember(Class<?> type, Member member) {
    if (!new net.sf.cglib.core.VisibilityPredicate(type, false).evaluate(member)) {
      // the member cannot be indexed by fast class.  Bail out.
      return null;
    }

    boolean publiclyCallable = isPubliclyCallable(member);
    if (!publiclyCallable && !hasSameVersionOfCglib(type.getClassLoader())) {
      // The type is in a classloader with a different version of cglib and is not publicly visible
      // (so we can't use the bridge classloader to work around).  Bail out.
      return null;
    }
    net.sf.cglib.reflect.FastClass.Generator generator
        = new net.sf.cglib.reflect.FastClass.Generator();
    if (publiclyCallable) {
      // Use the bridge classloader if we can
      generator.setClassLoader(getClassLoader(type));
    }
    generator.setType(type);
    generator.setNamingPolicy(FASTCLASS_NAMING_POLICY);
    if (logger.isLoggable(Level.FINE)) {
      logger.fine("Loading " + type + " FastClass with " + generator.getClassLoader());
    }
    return generator.create();
  }

  /**
   * Returns true if the types classloader has the same version of cglib that BytecodeGen has.  This
   * only returns false in strange OSGI situations, but it prevents us from using FastClass for non
   * public members.
   */
  private static boolean hasSameVersionOfCglib(ClassLoader classLoader) {
    Class<?> fc = net.sf.cglib.reflect.FastClass.class;
    try {
      return classLoader.loadClass(fc.getName()) == fc;
    } catch (ClassNotFoundException e) {
      return false;
    }
  }

  /**
   * Returns true if the member can be called by a fast class generated in a different classloader.
   */
  private static boolean isPubliclyCallable(Member member) {
    if (!Modifier.isPublic(member.getModifiers())) {
      return false;
    }
    Class<?>[] parameterTypes;
    if (member instanceof Constructor) {
      parameterTypes = ((Constructor) member).getParameterTypes();
    } else {
      Method method = (Method) member;
      if (!Modifier.isPublic(method.getReturnType().getModifiers())) {
        return false;
      }
      parameterTypes = method.getParameterTypes();
    }

    for (Class<?> type : parameterTypes) {
      if (!Modifier.isPublic(type.getModifiers())) {
        return false;
      }
    }
    return true;
  }

  public static net.sf.cglib.proxy.Enhancer newEnhancer(Class<?> type, Visibility visibility) {
    net.sf.cglib.proxy.Enhancer enhancer = new net.sf.cglib.proxy.Enhancer();
    enhancer.setSuperclass(type);
    enhancer.setUseFactory(false);
    if (visibility == Visibility.PUBLIC) {
      enhancer.setClassLoader(getClassLoader(type));
    }
    enhancer.setNamingPolicy(ENHANCER_NAMING_POLICY);
    logger.fine("Loading " + type + " Enhancer with " + enhancer.getClassLoader());
    return enhancer;
  }
  /*end[AOP]*/

  /**
   * The required visibility of a user's class from a Guice-generated class. Visibility of
   * package-private members depends on the loading classloader: only if two classes were loaded by
   * the same classloader can they see each other's package-private members. We need to be careful
   * when choosing which classloader to use for generated classes. We prefer our bridge classloader,
   * since it's OSGi-safe and doesn't leak permgen space. But often we cannot due to visibility.
   */
  public enum Visibility {

    /**
     * Indicates that Guice-generated classes only need to call and override public members of the
     * target class. These generated classes may be loaded by our bridge classloader.
     */
    PUBLIC {
      @Override
      public Visibility and(Visibility that) {
        return that;
      }
    },

    /**
     * Indicates that Guice-generated classes need to call or override package-private members.
     * These generated classes must be loaded in the same classloader as the target class. They
     * won't work with OSGi, and won't get garbage collected until the target class' classloader is
     * garbage collected.
     */
    SAME_PACKAGE {
      @Override
      public Visibility and(Visibility that) {
        return this;
      }
    };

    public static Visibility forMember(Member member) {
      if ((member.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) == 0) {
        return SAME_PACKAGE;
      }

      Class[] parameterTypes;
      if (member instanceof Constructor) {
        parameterTypes = ((Constructor) member).getParameterTypes();
      } else {
        Method method = (Method) member;
        if (forType(method.getReturnType()) == SAME_PACKAGE) {
          return SAME_PACKAGE;
        }
        parameterTypes = method.getParameterTypes();
      }

      for (Class<?> type : parameterTypes) {
        if (forType(type) == SAME_PACKAGE) {
          return SAME_PACKAGE;
        }
      }

      return PUBLIC;
    }

    public static Visibility forType(Class<?> type) {
      return (type.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) != 0
          ? PUBLIC
          : SAME_PACKAGE;
    }

    public abstract Visibility and(Visibility that);
  }

  /**
   * Loader for Guice-generated classes. For referenced classes, this delegates to either either the
   * user's classloader (which is the parent of this classloader) or Guice's class loader.
   */
  private static class BridgeClassLoader extends ClassLoader {

    BridgeClassLoader() {
      // use system loader as parent
    }

    BridgeClassLoader(ClassLoader usersClassLoader) {
      super(usersClassLoader);
    }

    @Override protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException {

      if (name.startsWith("sun.reflect")) {
        // these reflection classes must be loaded from bootstrap class loader
        return SystemBridgeHolder.SYSTEM_BRIDGE.classicLoadClass(name, resolve);
      }

      if (name.startsWith(GUICE_INTERNAL_PACKAGE) || name.startsWith(CGLIB_PACKAGE)) {
        if (null == GUICE_CLASS_LOADER) {
          // use special system bridge to load classes from bootstrap class loader
          return SystemBridgeHolder.SYSTEM_BRIDGE.classicLoadClass(name, resolve);
        }
        try {
          Class<?> clazz = GUICE_CLASS_LOADER.loadClass(name);
          if (resolve) {
            resolveClass(clazz);
          }
          return clazz;
        } catch (Throwable e) {
          // fall-back to classic delegation
        }
      }

      return classicLoadClass(name, resolve);
    }

    // make the classic delegating loadClass method visible
    Class<?> classicLoadClass(String name, boolean resolve)
      throws ClassNotFoundException {
      return super.loadClass(name, resolve);
    }
  }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java BytecodeGen.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.