/*
* 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);
}
}
}