Skip to content

Instantly share code, notes, and snippets.

@lukiano
Created August 18, 2015 11:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lukiano/114cb01a59ddd9880aa7 to your computer and use it in GitHub Desktop.
Save lukiano/114cb01a59ddd9880aa7 to your computer and use it in GitHub Desktop.
TransformingClassLoader.java
import org.springframework.core.DecoratingClassLoader;
import org.springframework.instrument.classloading.WeavingTransformer;
import org.springframework.util.FileCopyUtils;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.instrument.ClassFileTransformer;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLDecoder;
public final class TransformingClassLoader extends DecoratingClassLoader {
/** Packages that are excluded by default */
public static final String[] DEFAULT_EXCLUDED_PACKAGES = new String[] {
"java.", "javax.", "sun.", "oracle.", "org.xml.", "com.sun.", "org.w3c."
};
private static final String CLASS_FILE_SUFFIX = ".class";
static {
if (parallelCapableClassLoaderAvailable) {
ClassLoader.registerAsParallelCapable();
}
}
private final WeavingTransformer weavingTransformer;
/**
* Create a new OverridingClassLoader for the given ClassLoader.
* @param parent the ClassLoader to build an overriding ClassLoader for
*/
public TransformingClassLoader(final ClassLoader parent) {
super(parent);
this.weavingTransformer = new WeavingTransformer(parent);
for (String packageName : DEFAULT_EXCLUDED_PACKAGES) {
excludePackage(packageName);
}
}
@Override
protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
Class<?> result = null;
if (isEligibleForOverriding(name)) {
result = loadClassForOverriding(name);
}
if (result != null) {
if (resolve) {
resolveClass(result);
}
return result;
}
else {
return super.loadClass(name, resolve);
}
}
/**
* Determine whether the specified class is eligible for overriding
* by this class loader.
* @param className the class name to check
* @return whether the specified class is eligible
* @see #isExcluded
*/
protected boolean isEligibleForOverriding(final String className) {
return !isExcluded(className);
}
/**
* Load the specified class for overriding purposes in this ClassLoader.
* <p>The default implementation delegates to {@link #findLoadedClass},
* {@link #loadBytesForClass} and {@link #defineClass}.
* @param name the name of the class
* @return the Class object, or {@code null} if no class defined for that name
* @throws ClassNotFoundException if the class for the given name couldn't be loaded
*/
protected Class<?> loadClassForOverriding(final String name) throws ClassNotFoundException {
Class<?> result = findLoadedClass(name);
if (result == null) {
byte[] bytes = loadBytesForClass(name);
if (bytes != null) {
result = defineClass(name, bytes, 0, bytes.length);
// Additional check for defining the package, if not defined yet.
if (result.getPackage() == null) {
int packageSeparator = name.lastIndexOf('.');
if (packageSeparator != -1) {
String packageName = name.substring(0, packageSeparator);
definePackage(packageName, null, null, null, null, null, null, null);
}
}
}
}
return result;
}
/**
* Load the defining bytes for the given class,
* to be turned into a Class object through a {@link #defineClass} call.
* <p>The default implementation delegates to {@link #openStreamForClass}
* and {@link #transformIfNecessary}.
* @param name the name of the class
* @return the byte content (with transformers already applied),
* or {@code null} if no class defined for that name
* @throws ClassNotFoundException if the class for the given name couldn't be loaded
*/
protected byte[] loadBytesForClass(final String name) throws ClassNotFoundException {
InputStream is = openStreamForClass(name);
if (is == null) {
return null;
}
try {
// Load the raw bytes.
byte[] bytes = FileCopyUtils.copyToByteArray(is);
// Transform if necessary and use the potentially transformed bytes.
return transformIfNecessary(name, bytes);
}
catch (final IOException ex) {
throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex);
}
}
/**
* Open an InputStream for the specified class.
* <p>The default implementation loads a standard class file through
* the parent ClassLoader's {@code getResourceAsStream} method.
* @param name the name of the class
* @return the InputStream containing the byte code for the specified class
*/
protected InputStream openStreamForClass(final String name) {
String internalName = name.replace('.', '/') + CLASS_FILE_SUFFIX;
return getParent().getResourceAsStream(internalName);
}
/**
* Add a {@link ClassFileTransformer} to be applied by this ClassLoader.
* @param transformer the {@link ClassFileTransformer} to register
*/
public void addTransformer(final ClassFileTransformer transformer) {
this.weavingTransformer.addTransformer(transformer);
}
protected byte[] transformIfNecessary(final String name, final byte[] bytes) {
return this.weavingTransformer.transformIfNecessary(name, bytes);
}
private String utf8Decode(final String input) {
String result = null;
try {
result = URLDecoder.decode(input, "UTF-8");
} catch (UnsupportedEncodingException uee) {
// Impossible. All JVMs are required to support UTF-8.
}
return result;
}
//for Tomcat
public String getClasspath(final Class... ignored) {
if (getParent() instanceof URLClassLoader) {
URLClassLoader parent = (URLClassLoader) getParent();
StringBuilder classpath = new StringBuilder();
for (URL repo : parent.getURLs()) {
String repository = repo.toString();
if (repository.startsWith("file://"))
repository = utf8Decode(repository.substring(7));
else if (repository.startsWith("file:"))
repository = utf8Decode(repository.substring(5));
else
continue;
if (repository == null)
continue;
if (classpath.length() > 0)
classpath.append(File.pathSeparator);
classpath.append(repository);
}
return classpath.toString();
} else {
return null;
}
}
}
@kriegaex
Copy link

Where does parallelCapableClassLoaderAvailable come from? It seems to be undefined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment