All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.util.inject.internal.BytecodeGen Maven / Gradle / Ivy

There is a newer version: 8.15.1
Show newest version
/**
 * 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 org.elasticsearch.util.inject.internal;

import org.elasticsearch.util.base.Function;
import org.elasticsearch.util.collect.MapMaker;

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.Map;
import java.util.logging.Logger;

import static org.elasticsearch.util.inject.internal.Preconditions.*;

/**
 * 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.
 *
 * 

When loading classes, we need to be careful of: *

    *
  • 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. *
  • Visibility. Containers like OSGi use class loader boundaries * to enforce modularity at runtime. *
* *

For each generated class, there's multiple class loaders involved: *

    *
  • 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 private and * package visibility. *
  • Guice's class loader. *
  • 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. *
* * @author [email protected] (Stuart McCulloch) * @author [email protected] (Jesse Wilson) */ public final class BytecodeGen { private static final Logger logger = Logger.getLogger(BytecodeGen.class.getName()); static final ClassLoader GUICE_CLASS_LOADER = BytecodeGen.class.getClassLoader(); /** ie. "com.google.inject.internal" */ private static final String GUICE_INTERNAL_PACKAGE = BytecodeGen.class.getName().replaceFirst("\\.internal\\..*$", ".internal"); private static final String CGLIB_PACKAGE = " "; // any string that's illegal in a package name /** Use "-Dguice.custom.loader=false" to disable custom classloading. */ static final boolean HOOK_ENABLED = "true".equals(System.getProperty("guice.custom.loader", "true")); /** * Weak cache of bridge class loaders that make the Guice implementation * classes visible to various code-generated proxies of client classes. */ private static final Map CLASS_LOADER_CACHE = new MapMaker().weakKeys().weakValues().makeComputingMap( new Function() { public ClassLoader apply(final @Nullable ClassLoader typeClassLoader) { logger.fine("Creating a bridge ClassLoader for " + typeClassLoader); return AccessController.doPrivileged(new PrivilegedAction() { public ClassLoader run() { return new BridgeClassLoader(typeClassLoader); } }); } }); /** * For class loaders, {@code null}, is always an alias to the * {@link ClassLoader#getSystemClassLoader() system class loader}. This method * will not return null. */ private static ClassLoader canonicalize(ClassLoader classLoader) { return classLoader != null ? classLoader : checkNotNull(getSystemClassLoaderOrNull(), "Couldn't get a ClassLoader"); } /** * Returns the system classloader, or {@code null} if we don't have * permission. */ private static ClassLoader getSystemClassLoaderOrNull() { try { return ClassLoader.getSystemClassLoader(); } catch (SecurityException e) { return null; } } /** * 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) { delegate = canonicalize(delegate); // if the application is running in the System classloader, assume we can run there too if (delegate == getSystemClassLoaderOrNull()) { return delegate; } // Don't bother bridging existing bridge classloaders if (delegate instanceof BridgeClassLoader) { return delegate; } if (HOOK_ENABLED && Visibility.forType(type) == Visibility.PUBLIC) { return CLASS_LOADER_CACHE.get(delegate); } return delegate; } /** * 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 { 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 { 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 = member instanceof Constructor ? ((Constructor) member).getParameterTypes() : ((Method) member).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 { public BridgeClassLoader(ClassLoader usersClassLoader) { super(usersClassLoader); } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { // delegate internal requests to Guice class space if (name.startsWith(GUICE_INTERNAL_PACKAGE) || name.startsWith(CGLIB_PACKAGE)) { try { Class clazz = GUICE_CLASS_LOADER.loadClass(name); if (resolve) { resolveClass(clazz); } return clazz; } catch (Exception e) { // fall back to classic delegation } } return super.loadClass(name, resolve); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy