org.jadira.reflection.access.classloader.AccessClassLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cloning Show documentation
Show all versions of cloning Show documentation
Cloning for Jadira Framework
/*
* Copyright 2013 Christopher Pheby
*
* 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.jadira.reflection.access.classloader;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* A ClassLoader which can be used to load classes from arbitrary byte arrays.
* Jadira uses this to load classes generated using ASM.
*/
public class AccessClassLoader extends ClassLoader {
private static final Method DEFINE_METHOD;
static {
Method defineMethod = null;
try {
defineMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
defineMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
} catch (SecurityException e) {
}
DEFINE_METHOD = defineMethod;
}
private static final ConcurrentHashMap ASM_CLASS_LOADERS = new ConcurrentHashMap();
private static final Map registeredClasses = new HashMap();
/**
* Creates a new instance using a suitable ClassLoader for the specified class
* @param typeToBeExtended The class to use to obtain a ClassLoader
* @return A new instance, or an existing instance if one already exists.
*/
public static final AccessClassLoader get(Class> typeToBeExtended) {
ClassLoader loader = typeToBeExtended.getClassLoader();
return get(loader == null ? ClassLoader.getSystemClassLoader() : loader);
}
/**
* Creates an AccessClassLoader for the given parent
* @param parent The parent ClassLoader for this instance
* @return A new instance, or an existing instance if one already exists.
*/
public synchronized static final AccessClassLoader get(ClassLoader parent) {
AccessClassLoader loader = (AccessClassLoader) ASM_CLASS_LOADERS.get(parent);
if (loader == null) {
loader = new AccessClassLoader(parent);
ASM_CLASS_LOADERS.put(parent, loader);
}
return loader;
}
private AccessClassLoader(ClassLoader parentClassLoader) {
super(parentClassLoader);
}
@Override
public Class> loadClass(String name) throws ClassNotFoundException {
Class> loadedClass = findLoadedClass(name);
if (loadedClass == null) {
try {
loadedClass = findClass(name);
} catch (ClassNotFoundException e) {
// Ignore
}
if (loadedClass == null) {
loadedClass = super.loadClass(name);
}
}
return loadedClass;
}
/**
* Registers a class by its name
* @param name The name of the class to be registered
* @param bytes An array of bytes containing the class
*/
public void registerClass(String name, byte[] bytes) {
if (registeredClasses.containsKey(name)) {
throw new IllegalStateException("Attempted to register a class that has been registered already: " + name);
}
registeredClasses.put(name, bytes);
}
@Override
public Class> findClass(String name) throws ClassNotFoundException {
byte[] bytes = registeredClasses.get(name);
if (bytes != null) {
registeredClasses.remove(name);
try {
return (Class>) DEFINE_METHOD.invoke(getParent(), new Object[] { name, bytes, Integer.valueOf(0), Integer.valueOf(bytes.length) });
} catch (Exception ignored) {
}
return defineClass(name, bytes, 0, bytes.length);
}
throw new ClassNotFoundException("Cannot find class: " + name);
}
}