/* * Copyright (C) 2015, 2016 higherfrequencytrading.com * Copyright (C) 2008, 2013 Google Inc. * Copyright (C) 2016 Roman Leventov * * This program 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 3 of the License. * * This program 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 program. If not, see <http://www.gnu.org/licenses/>. */ package net.openhft.chronicle.values; import net.openhft.chronicle.bytes.Bytes; import java.lang.ref.WeakReference; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.WeakHashMap; import java.util.logging.Logger; /** * Stripped down version of classes * https://github.com/google/guice/blob/9867f9c2142355ae958f9eeb8fb96811082c8812/core/src/com/google/inject/internal/InternalFlags.java * and * https://github.com/google/guice/blob/09fec22916264e76d95333b81c69030b3b713e64/core/src/com/google/inject/internal/BytecodeGen.java * * <p>When loading classes, we need to be careful of: * <ul> * <li><strong>Memory leaks.</strong> 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><strong>Visibility.</strong> Containers like <code>OSGi</code> use class loader boundaries * to enforce modularity at runtime. * </ul> * * <p>For each generated class, there's multiple class loaders involved: * <ul> * <li><strong>The related class's class loader.</strong> Every generated class services exactly * one user-supplied class. This class loader must be used to access members with protected * and package visibility. * <li><strong>Values's class loader.</strong> * <li><strong>Our bridge class loader.</strong> 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 Values * class loader (for internal classes that are used by the generated classes). This class * loader that owns the classes generated by Values. * </ul> * * @author mcculls@gmail.com (Stuart McCulloch) * @author jessewilson@google.com (Jesse Wilson) */ final class BytecodeGen { static final Logger logger = Logger.getLogger(BytecodeGen.class.getName()); private static final CustomClassLoadingOption CUSTOM_CLASS_LOADING = parseCustomClassLoadingOption(); /** * The options for Values custom class loading. */ public enum CustomClassLoadingOption { /** No custom class loading */ OFF, /** Automatically bridge between class loaders (Default) */ BRIDGE } public static CustomClassLoadingOption getCustomClassLoadingOption() { return CUSTOM_CLASS_LOADING; } private static CustomClassLoadingOption parseCustomClassLoadingOption() { return getSystemOption("chronicle_values_custom_class_loading", CustomClassLoadingOption.BRIDGE, CustomClassLoadingOption.OFF); } /** * Gets the system option indicated by the specified key; runs as a privileged action. * * @param name of the system option * @param defaultValue if the option is not set * @param secureValue if the security manager disallows access to the option * * @return value of the option, defaultValue if not set, secureValue if no access */ private static <T extends Enum<T>> T getSystemOption(final String name, T defaultValue, T secureValue) { Class<T> enumType = defaultValue.getDeclaringClass(); String value = null; try { value = AccessController.doPrivileged( (PrivilegedAction<String>) () -> System.getProperty(name)); return (value != null && value.length() > 0) ? Enum.valueOf(enumType, value) : defaultValue; } catch (SecurityException e) { return secureValue; } catch (IllegalArgumentException e) { logger.warning(value + " is not a valid flag value for " + name + ". " + " Values must be one of " + Arrays.asList(enumType.getEnumConstants())); return defaultValue; } } static final ClassLoader VALUES_CLASS_LOADER = canonicalize(BytecodeGen.class.getClassLoader()); // initialization-on-demand... private static class SystemBridgeHolder { static final BridgeClassLoader SYSTEM_BRIDGE = new BridgeClassLoader(); } /** ie. "net.openhft.chronicle.values" */ static final String VALUES_PACKAGE = BytecodeGen.class.getName().replaceFirst("\\.values\\..*$", ".values"); /** ie. "net.openhft.chronicle.bytes" */ static final String BYTES_PACKAGE = Bytes.class.getName().replaceFirst("\\.bytes\\..*$", ".bytes"); /** * Weak cache of bridge class loaders that make the Chronicle Values implementation * classes visible to various code-generated proxies of client classes. */ private static final WeakHashMap<ClassLoader, WeakReference<ClassLoader>> CLASS_LOADER_CACHE = new WeakHashMap<>(); private static ClassLoader getFromClassLoaderCache(ClassLoader typeClassLoader) { synchronized (CLASS_LOADER_CACHE) { return CLASS_LOADER_CACHE.compute(typeClassLoader, (k, ref) -> { if (ref == null || ref.get() == null) { logger.fine("Creating a bridge ClassLoader for " + typeClassLoader); return AccessController.doPrivileged( (PrivilegedAction<WeakReference<ClassLoader>>) () -> new WeakReference<>(new BridgeClassLoader(typeClassLoader))); } else { return ref; } }).get(); } } /** * 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 VALUES_CLASS_LOADER; } delegate = canonicalize(delegate); // no need for a bridge if using same class loader, or it's already a bridge if (delegate == VALUES_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 getFromClassLoaderCache(delegate); } // delegate may or may not be null here return SystemBridgeHolder.SYSTEM_BRIDGE; } return delegate; // last-resort: do nothing! } /** * The required visibility of a user's class from a Values-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 Values-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 Values-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 forType(Class<?> type) { return (type.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC)) != 0 ? PUBLIC : SAME_PACKAGE; } public abstract Visibility and(Visibility that); } /** * Loader for Values-generated classes. For referenced classes, this delegates to either either * the user's classloader (which is the parent of this classloader) or Values's class loader. */ 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(VALUES_PACKAGE) || name.startsWith(BYTES_PACKAGE)) { if (null == VALUES_CLASS_LOADER) { // use special system bridge to load classes from bootstrap class loader return SystemBridgeHolder.SYSTEM_BRIDGE.classicLoadClass(name, resolve); } try { Class<?> clazz = VALUES_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); } } }