I want to load specific jars in ScriptEngineManager using specific ClassLoader
This constructor loads the implementations of ScriptEngineFactory visible to the given ClassLoader using the service provider mechanism.
The problem when I tried to create Classloader
File file = new File("c:\\myclasses\\");
try {
// Convert File to a URL
URL url = file.toURI().toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls)
And I'm getting also class that weren't in class for example Spring
Class cls1 = cl.loadClass("org.springframework.http.HttpStatus");
How can I create a clean classloader with only specific folder's classes?
EDIT
If it's not possible, can I use in groovy script something equivalent to Rhino's ClassShutter
private static void setJavaClassesVisibleInvisibleSandbox(Context cx)
{
cx.setClassShutter(new ClassShutter()
{
public boolean visibleToScripts(String className)
{
// No Java classes allowed inside scripts
EDIT
When tried #Vinz243 CustomClassLoader solution failed to load classes for classes files or jar files from specific folder, even tried rt.jar
java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:662)
at java.lang.ClassLoader.defineClass(ClassLoader.java:761)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at com.CustomClassLoader.loadClass(CustomClassLoader.java:15)
As specified in the javadoc :
public URLClassLoader(URL[] urls)
Constructs a new URLClassLoader for the specified URLs using the default delegation parent ClassLoader. The URLs will be searched in the order specified for classes and resources after first searching in the parent class loader. Any URL that ends with a '/' is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a JAR file which will be downloaded and opened as needed.
So you can try to extend ClassLoader since the mechanism responsible for loading the parent class is inside java.lang.ClassLoader#loadClass(java.lang.String, boolean)
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
EDIT
Not tested, but something like that should do the job:
class CustomClassLoader extends URLClassLoader {
public CustomClassLoader (URL[] urls) throws NoSuchMethodException {
super(urls);
}
#Override
protected Class<?> loadClass (String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> aClass = findClass(name);
if (resolve) {
resolveClass(aClass);
}
return aClass;
}
}
}
I currently try to run java code in Maxmsp using a mxj object, and I want to load some classes inside of the code.
But I always get the errors, although the code runs properly in eclipse.
What is the problem?
This is my code.
If I bang in Maxmsp, call() will be called.
package Load;
import com.cycling74.max.*;
public class Loaded extends MaxObject{
public static void main(String[] args) {
//This works properly in eclipse
call();
}
public void bang() {
//This should work in Maxmsp, but get errors
call();
}
public static void call() {
try {
//this is just a example
//I want to load some classes which locate the same directory as this class
Thread.currentThread().getContextClassLoader().loadClass("Load.Loaded");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
And this is the error message:
java.lang.ClassNotFoundException: Load.Loaded
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at Load.Loaded.call(Loaded.java:21)
at Load.Loaded.bang(Loaded.java:16)
MXJ System class path is:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/commons-codec-1.11.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/core.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/gluegen-rt-natives-macosx-universal.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/gluegen-rt.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/jitter.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/jode-1.1.2-pre-embedded.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/jogl-all-natives-macosx-universal.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/jogl-all.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/max.jar:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/lib/sadamLib.jar
MXJ Classloader CLASSPATH is:
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/classes/
/Applications/Max.app/Contents/Resources/C74/packages/max-mxj/java-classes/
/Users/MyName/Documents/ecllipse-workspace/009_Processing/bin
Loaded.class is in /Users/MyName/Documents/ecllipse-workspace/009_Processing/bin
You need to include any of your dependencies on the classpath:
java -cp "path/to/maxmsp.jar;path/to/dependency2.jar;path/to/your.jar" classpath.of.your.Main
If you are just running directly from a classfile and haven't JARred your project then you can omit the path/to/your.jar and just run from the same directory with the classpath of your Main.
The above is for running java from command line.
Since Max is what is running and what's taking control of the classloading im guessing that sun.misc.Launcher$AppClassLoader is not working. Try debugging and see what it's doing. Also maybe try to find a way to use the Max classloader instead of the Java AppClassLoader.
The problem was what Max cannot load class properly.
So I created class loader method.
public static ClassLoader createClassLoader(String dirname) throws java.io.IOException {
java.net.URL[] url = new java.net.URL[1];
java.io.File file;
if (dirname.endsWith("/")) {
file = new java.io.File(dirname);
}
else {
file = new java.io.File(dirname + "/");
}
url[0]= file.toURI().toURL();
ClassLoader parent = ClassLoader.getSystemClassLoader();
java.net.URLClassLoader loader = new java.net.URLClassLoader(url, parent);
return loader
}
And call
ClassLoader loader = createClassLoader("ClassPath");
Class<?> c = Class.forName("Classname", true, loader);
i am planning for the hot deployment of class using the custom class loader.For this task i have written custom class loader. Which looks like
public class CustomClassLoader extends ClassLoader{
public CustomClassLoader(ClassLoader parent) {
super(parent);
}
public Class loadClass(String name) throws ClassNotFoundException {
if(!"classLoader.TestCase".equals(name))
return super.loadClass(name);
try {
String url = null;
String clzName = null;
url = "file:/home/naveen/workspace/JavaConcept/bin/classLoader/TestCase.class";
clzName = "classLoader.TestCase";
}
URL myUrl = new URL(url);
URLConnection connection = myUrl.openConnection();
InputStream input = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int data = input.read();
while(data != -1){
buffer.write(data);
data = input.read();
}
input.close();
byte[] classData = buffer.toByteArray();
return defineClass(clzName,classData, 0, classData.length);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
Now i have a another class called TestCase which has an method called print().
public void print(){
System.out.println("Hello");
System.out.println("Bye");
}
and i am calling it from main method something like this
public static void main(String arg[]) throws InstantiationException, IllegalAccessException{
TestCase t = new TestCase();
t.print();
try {
ClassLoader classLoader = CustomClassLoader.class.getClassLoader();
classLoader = new CustomClassLoader(classLoader);
Class clz = classLoader.loadClass("classLoader.TestCase");
TestCase t2 = new TestCase();
t2.print();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
Now here i want to do one thing that inside a main t.print() method is called which print hello and bye on console. Now i have a another version of this Testcase class's print method which only print Hello. So what i did i stated the program in debug mode and let the program going on untill the classLoader.loadClass() line. then i replaced the Testcase.class from directory structure with new version which print only hello.But still it showing the output Hello and Bye.
Can someone help me what's wrong with this program or my understand regarding the class loader is not correct. Please correct me and help to complete my task.
Custom class loaders are difficult to implement because of the many rules you need to keep in mind. In your case, I guess the problem is that your class file is part of your program, which means that if you try to load a class, the default class loader will load it before calling your loadClass() method that will never be called. Try removing your class file from your classpath so that its not part of your program, then your loadClass() should be called.
Second problem is that you are creating your TestCase object using new, that will invoke the default class loader, a better approach would be to call classLoader.loadClass() and then create an instance from the class returned, but if you cast to TestCase that it means that the class is defined in your default classloader which leads to the first problem. An alternative is to create an interface and cast to that interface in order to call your method.
I'm working on a program that watches a directory and runs all tests in the directory when it sees changes in the directory.
This requires the program to dynamically load the classes, instead of getting the cached copies.
I can dynamically load the test classes. Changes to the tests get detected and used at runtime. However, this isn't the case for the classes tested by the tests.
My code for dynamically loading the classes and returning a list of test classes:
List<Class<?>> classes = new ArrayList<Class<?>>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\\', '.');
name = name.substring(0, name.length() - 6);
tempClass = new DynamicClassLoader(Thread.currentThread().getContextClassLoader()).findClass(name) } catch (ClassNotFoundException e1) {
// TODO Decide how to handle exception
e1.printStackTrace();
}
boolean cHasTestMethods = false;
for(Method method: tempClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
cHasTestMethods = true;
break;
}
}
if (!Modifier.isAbstract(cachedClass.getModifiers()) && cHasTestMethods) {
classes.add(tempClass);
}
}
return classes;
with DynamicClassLoader being as the Reloader described here How to force Java to reload class upon instantiation?
Any idea how to fix it? I thought all classes would be dynamically loaded. Note however that I don't overwrite loadclass in my DynamicClassLoader because if I do my test classes give init
EDIT:
This doesn't work, the class gets loaded but the tests in it aren't detected...
List<Request> requests = new ArrayList<Request>();
for (File file : classFiles) {
String fullName = file.getPath();
String name = fullName.substring(fullName.indexOf("bin")+4)
.replace('/', '.')
.replace('\\', '.');
name = name.substring(0, name.length() - 6);
Class<?> cachedClass = null;
Class<?> dynamicClass = null;
try {
cachedClass = Class.forName(name);
URL[] urls={ cachedClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = cachedClass .getClassLoader().getParent();
URLClassLoader cl = new URLClassLoader(urls, delegateParent) ;
dynamicClass = cl.loadClass(name);
System.out.println(dynamicClass);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Edit edit: i detect the test methods like this:
for(Method method: dynamicClass.getMethods()){
if(method.isAnnotationPresent(Test.class)){
requests.add(Request.method(dynamicClass, method.getName()));
}
}
If you used the custom ClassLoader exactly like in the linked answer it is not overriding the method protected Class<?> loadClass(String name, boolean resolve). This implies that when the JVM is resolving dependencies it will still delegate to the parent class loader. And, of course, when it was not delegating to the parent ClassLoader it had the risk of missing some required classes.
The easiest solution is to set up the right parent class loader. You are currently passing Thread.currentThread().getContextClassLoader() which is a bit strange as your main intention is that the delegation should not delegate to that loader but load the changed classes. You have to think about which class loaders exist and which to use and which not. E.g. if the class Foo is within the scope of your current code but you want to (re)load it with the new ClassLoader, Foo.class.getClassLoader().getParent() would be the right delegate parent for the new ClassLoader. Note that it might be null but this doesn’t matter as in this case it would use the bootstrap loader which is the correct parent then.
Note that when you set up the right parent ClassLoader matching your intentions you don’t need that custom ClassLoader anymore. The default implementation (see URLClassLoader) already does the right thing. And with current Java versions it is Closeable making it even more suitable for dynamic loading scenarios.
Here is a simple example of a class reloading:
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
public class ReloadMyClass
{
public static void main(String[] args)
throws ClassNotFoundException, IOException {
Class<?> myClass=ReloadMyClass.class;
System.out.printf("my class is Class#%x%n", myClass.hashCode());
System.out.println("reloading");
URL[] urls={ myClass.getProtectionDomain().getCodeSource().getLocation() };
ClassLoader delegateParent = myClass.getClassLoader().getParent();
try(URLClassLoader cl=new URLClassLoader(urls, delegateParent)) {
Class<?> reloaded=cl.loadClass(myClass.getName());
System.out.printf("reloaded my class: Class#%x%n", reloaded.hashCode());
System.out.println("Different classes: "+(myClass!=reloaded));
}
}
}
I would like to create a parent-last / child-first class loader, e.g. a class loader that will look for classes in the child class loder first, and only then delegate to it's parent ClassLoader to search for classes.
Clarification:
I know now that to get complete ClassLoading seperation I need to use something like a URLClassLoader passing null as it's parent, thanks to this answer to my previous question
However the current question comes to help me resolve this issue:
My code + dependent jars are being loaded into an existing system, using a ClassLoader that sets that System's ClassLoader as it's parent (URLClassLoader)
That System uses some libraries of a version not compatible with the one I need (e.g. older version of Xerces, that doesn't allow me to run my code)
My code runs perfectly fine if runs stand alone, but it fails if runs from that ClassLoader
Howerver I do need access to many other classes within the parent ClassLoader
Therefore I want to allow me to Override, the parent classloader "jars" with my own: If a class I call is found in the child class loader (e.g. I provided a newer version of Xerces with my own jars, instead of the one users by the ClassLoader that loaded my code and jars.
Here is the System's code that loads my code + Jars (I can't change this one)
File addOnFolder = new File("/addOns");
URL url = addOnFolder.toURL();
URL[] urls = new URL[]{url};
ClassLoader parent = getClass().getClassLoader();
cl = URLClassLoader.newInstance(urls, parent);
Here is "my" code (taken fully from the Flying Sauser "Hello World" code demo):
package flyingsaucerpdf;
import java.io.*;
import com.lowagie.text.DocumentException;
import org.xhtmlrenderer.pdf.ITextRenderer;
public class FirstDoc {
public static void main(String[] args)
throws IOException, DocumentException {
String f = new File("sample.xhtml").getAbsolutePath();
System.out.println(f);
//if(true) return;
String inputFile = "sample.html";
String url = new File(inputFile).toURI().toURL().toString();
String outputFile = "firstdoc.pdf";
OutputStream os = new FileOutputStream(outputFile);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(url);
renderer.layout();
renderer.createPDF(os);
os.close();
}
}
This works standalone (running main) but fails with this error when loaded through the parent CL:
org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to
create or change an object in a way
which is incorrect with regard to
namespaces.
probably because the parent system uses Xerces of an older version, and even though I provide the right Xerces jar in the /addOns folder, since it's classes were already loaded and used by the parent System, it doesn't allow my own code to use my own jar due to the direction of the delegation. I hope this makes my question clearer, and I'm sure it has been asked
before. (Perhaps I don't ask the right question)
Today is your lucky day, as I had to solve this exact problem. I warn you though, the innards of class loading are a scary place. Doing this makes me think that the designers of Java never imagined that you might want to have a parent-last classloader.
To use just supply a list of URLs containing classes or jars to be available in the child classloader.
/**
* A parent-last classloader that will try the child classloader first and then the parent.
* This takes a fair bit of doing because java really prefers parent-first.
*
* For those not familiar with class loading trickery, be wary
*/
private static class ParentLastURLClassLoader extends ClassLoader
{
private ChildURLClassLoader childClassLoader;
/**
* This class allows me to call findClass on a classloader
*/
private static class FindClassClassLoader extends ClassLoader
{
public FindClassClassLoader(ClassLoader parent)
{
super(parent);
}
#Override
public Class<?> findClass(String name) throws ClassNotFoundException
{
return super.findClass(name);
}
}
/**
* This class delegates (child then parent) for the findClass method for a URLClassLoader.
* We need this because findClass is protected in URLClassLoader
*/
private static class ChildURLClassLoader extends URLClassLoader
{
private FindClassClassLoader realParent;
public ChildURLClassLoader( URL[] urls, FindClassClassLoader realParent )
{
super(urls, null);
this.realParent = realParent;
}
#Override
public Class<?> findClass(String name) throws ClassNotFoundException
{
try
{
// first try to use the URLClassLoader findClass
return super.findClass(name);
}
catch( ClassNotFoundException e )
{
// if that fails, we ask our real parent classloader to load the class (we give up)
return realParent.loadClass(name);
}
}
}
public ParentLastURLClassLoader(List<URL> classpath)
{
super(Thread.currentThread().getContextClassLoader());
URL[] urls = classpath.toArray(new URL[classpath.size()]);
childClassLoader = new ChildURLClassLoader( urls, new FindClassClassLoader(this.getParent()) );
}
#Override
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
try
{
// first we try to find a class inside the child classloader
return childClassLoader.findClass(name);
}
catch( ClassNotFoundException e )
{
// didn't find it, try the parent
return super.loadClass(name, resolve);
}
}
}
EDIT: Sergio and ɹoƃı have pointed out that if you call .loadClass with the same classname, you will get a LinkageError. While this is true, the normal use-case for this classloader is to set it as the thread's classloader Thread.currentThread().setContextClassLoader() or via Class.forName(), and that works as-is.
However, if .loadClass() was needed directly, this code could be added in the ChildURLClassLoader findClass method at the top.
Class<?> loaded = super.findLoadedClass(name);
if( loaded != null )
return loaded;
The following code is what I use. It has the advantage over the other answer that it doesn't break the parent chain (you can follow getClassLoader().getParent()).
It also has an advantage over tomcat's WebappClassLoader by not reinventing the wheel and not depending on other objects. It re-uses code from URLClassLoader as much as possible.
(it doesn't yet honor the system class loader, but when I get that fixed I'll update the answer)
It honors the system class loader (for java.* classes, endorsed dir, etc.). It also works when security is turned on and the classloader doesn't have access to its parent (yes, this situation is weird, but possible).
public class ChildFirstURLClassLoader extends URLClassLoader {
private ClassLoader system;
public ChildFirstURLClassLoader(URL[] classpath, ClassLoader parent) {
super(classpath, parent);
system = getSystemClassLoader();
}
#Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
if (system != null) {
try {
// checking system: jvm classes, endorsed, cmd classpath, etc.
c = system.loadClass(name);
}
catch (ClassNotFoundException ignored) {
}
}
if (c == null) {
try {
// checking local
c = findClass(name);
} catch (ClassNotFoundException e) {
// checking parent
// This call to loadClass may eventually call findClass again, in case the parent doesn't find anything.
c = super.loadClass(name, resolve);
}
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
#Override
public URL getResource(String name) {
URL url = null;
if (system != null) {
url = system.getResource(name);
}
if (url == null) {
url = findResource(name);
if (url == null) {
// This call to getResource may eventually call findResource again, in case the parent doesn't find anything.
url = super.getResource(name);
}
}
return url;
}
#Override
public Enumeration<URL> getResources(String name) throws IOException {
/**
* Similar to super, but local resources are enumerated before parent resources
*/
Enumeration<URL> systemUrls = null;
if (system != null) {
systemUrls = system.getResources(name);
}
Enumeration<URL> localUrls = findResources(name);
Enumeration<URL> parentUrls = null;
if (getParent() != null) {
parentUrls = getParent().getResources(name);
}
final List<URL> urls = new ArrayList<URL>();
if (systemUrls != null) {
while(systemUrls.hasMoreElements()) {
urls.add(systemUrls.nextElement());
}
}
if (localUrls != null) {
while (localUrls.hasMoreElements()) {
urls.add(localUrls.nextElement());
}
}
if (parentUrls != null) {
while (parentUrls.hasMoreElements()) {
urls.add(parentUrls.nextElement());
}
}
return new Enumeration<URL>() {
Iterator<URL> iter = urls.iterator();
public boolean hasMoreElements() {
return iter.hasNext();
}
public URL nextElement() {
return iter.next();
}
};
}
#Override
public InputStream getResourceAsStream(String name) {
URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
}
return null;
}
}
By reading the source code of either Jetty or Tomcat, both of which provide parent-last class loaders to implement webapp semantics.
https://github.com/apache/tomcat/blob/7.0.93/java/org/apache/catalina/loader/WebappClassLoaderBase.java
Which is to say, by overriding the findClass method in your ClassLoader class. But why reinvent the wheel when you can steal it?
Reading your various updates, I see that you ran into some classic problems with the XML SPI system.
The general problem is this: if you create a completely isolated class loader, then it's hard to use the objects it returns. If you allow sharing, you can have problems when the parent contains the wrong versions of things.
It is to deal with all this lunacy that OSGi was invented, but that's a big pill to swallow.
Even in webapps, the class loaders exempt some packages from the 'local-first' processing on the assumption that the container and the webapp have to agree on the API between them.
(see at the bottom for an update on a solution I found)
It seems that AntClassLoader has a support for parent first/last, (didn't test it yet)
http://svn.apache.org/repos/asf/ant/core/trunk/src/main/org/apache/tools/ant/AntClassLoader.java
Here is a snippet
/**
* Creates a classloader for the given project using the classpath given.
*
* #param parent The parent classloader to which unsatisfied loading
* attempts are delegated. May be <code>null</code>,
* in which case the classloader which loaded this
* class is used as the parent.
* #param project The project to which this classloader is to belong.
* Must not be <code>null</code>.
* #param classpath the classpath to use to load the classes.
* May be <code>null</code>, in which case no path
* elements are set up to start with.
* #param parentFirst If <code>true</code>, indicates that the parent
* classloader should be consulted before trying to
* load the a class through this loader.
*/
public AntClassLoader(
ClassLoader parent, Project project, Path classpath, boolean parentFirst) {
this(project, classpath);
if (parent != null) {
setParent(parent);
}
setParentFirst(parentFirst);
addJavaLibraries();
}
Update:
Found this as well, when as a last resort I started guessing class names in google (this is what ChildFirstURLClassLoader produced) - but it seems to be incorrect
Update 2:
The 1st Option (AntClassLoader) is very coupled to Ant (requires a Project context and not easy to pass a URL[] to it
The 2nd Option (from an OSGI project in google code) wasn't quite what I needed as it searched parent classloader before the system classloader (Ant class loader does it correctly by the way). The problem as I see it, think that your parent classloader includes a jar (that it shouldn't have) of a functionality that wasn't on JDK 1.4 but was added in 1.5, this has no harm as the parent last class loader (regular delegation model, e.g. URLClassLoader) will always load first the JDK's classes, but here the child first naive implementation seems to unveil the old, redundant jar in the parent class loader, shadowing the JDK / JRE own implementation.
I have yet to find a certified, fully tested, mature Parent Last / Child First correct implementation that is not coupled to a specific solution (Ant, Catalina/Tomcat)
Update 3 - I found it!
I WAS looking in the wrong place,
All I did was add META-INF/services/javax.xml.transform.TransformerFactory and restored the JDK's com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl instead of the old Xalan's org.apache.xalan.processor.TransformerFactoryImpl
The only reason I don't "accept my own answer" yet, is that I don't know if the META-INF/services approach has the same classloader delegation as regular classes (e.g. is it parent-first / child-last or parent-last / child-first?)
URLClassLoader had this constructor public URLClassLoader(URL[], ClassLoader) that allows you to override the parent classloader of an URLClassLoader. You can just load your classloader through an URLClassLoader with an overridden parent classloader.
You can override findClass() and loadClass() to implement a child first class loader:
/**
* Always throws {#link ClassNotFoundException}. Is called if parent class loader
* did not find class.
*/
#Override
protected final Class findClass(String name)
throws ClassNotFoundException
{
throw new ClassNotFoundException();
}
#Override
protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)){
/*
* Check if we have already loaded this class.
*/
Class c = findLoadedClass(name);
if (c == null){
try {
/*
* We haven't previously loaded this class, try load it now
* from SUPER.findClass()
*/
c = super.findClass(name);
}catch (ClassNotFoundException ignore){
/*
* Child did not find class, try parent.
*/
return super.loadClass(name, resolve);
}
}
if (resolve){
resolveClass(c);
}
return c;
}
}