override java class loader - java

I am having the following example
public class Tester
{
/**
* #param args
* #throws ClassNotFoundException
*/
public static void main(String[] args) throws ClassNotFoundException
{
new Tester().execute();
}
private void execute() throws ClassNotFoundException
{
//Java Class Loader
ClassLoader baseClassLoader = Thread.currentThread().getContextClassLoader();
//Java custom Class Loader
ClassLoader customClassLoader = new CustomClassLoader();
Class<?> customClass = Class.forName("a.b.c.d.class", true, customClassLoader);
//Java custom Class Loader
ClassLoader customClassLoader = customClass.getClassLoader();
Thread.currentThread().setContextClassLoader(customClassLoader);
//Java custom Class Loader
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
//Java Class Loader?????
ClassLoader classLoader = this.getClass().getClassLoader();
}
}
Why after invoke
Thread.currentThread().setContextClassLoader(customClassLoader);
once I execute
this.getClass().getClassLoader();
I still get the java class loader and not my custom class loader.
How I can do this?
Thanks

Thread.setContextClassLoader just sets a variable in Thread. Linking classes is still done from each class' class loader. It certainly doesn't change the class loader of any already loaded classes. All that it changes is the class loader returned by Thread.getContextClassLoader.
I'd suggest staying away from thread context class loader, and other thread-globals.

Related

deserialize dynamically loaded class java

I am loading in classes from a jarfile using a URLClassLoader and I then serialize objects of these classes and save it in a file. Then when I go to deserialize the classes the ObjectInputStream throws a java.lang.ClassNotFoundException, and it isn't finding the classes I have loaded in dynamically. How would I deserialize the loaded in classes?
To use a custom ClassLoader you have to override the resolveClass method. Here is an example from the JDK source (which is not public but you can use it for inspiration)
/**
* This subclass of ObjectInputStream delegates loading of classes to
* an existing ClassLoader.
*/
class ObjectInputStreamWithLoader extends ObjectInputStream
{
private ClassLoader loader;
/**
* Loader must be non-null;
*/
public ObjectInputStreamWithLoader(InputStream in, ClassLoader loader)
throws IOException, StreamCorruptedException {
super(in);
if (loader == null) {
throw new IllegalArgumentException("Illegal null argument to ObjectInputStreamWithLoader");
}
this.loader = loader;
}
/**
* Use the given ClassLoader rather than using the system class
*/
#SuppressWarnings("rawtypes")
protected Class resolveClass(ObjectStreamClass classDesc)
throws IOException, ClassNotFoundException {
String cname = classDesc.getName();
return ClassFinder.resolveClass(cname, this.loader);
}
}
apache commons provides this in org.apache.commons.io.input.ClassLoaderObjectInputStream. the code is the same as what peter suggests, but public
from the javadocs:
A special ObjectInputStream that loads a class based on a specified
ClassLoader rather than the system default.
This is useful in dynamic container environments.

Need a Java system-only class loader

I would like to find or create a Java class loader that loads only system classes, excluding any classes on the application defined class path. My goal is to use that class loader to build a class loader that loads from a specific JAR file, resolving system classes using the system-only class loader, but not contaminating the classes from the JAR file with any classes defined by my application.
So far I have not found any way to create a system-only class loader that does not either use private APIs or make assumptions about the Java implementation. See code below that works in my current environment but makes undesirable assumptions.
If there is no way to create an implementation independent system-only class loader, is there a reason why not? Is what I am trying to do a bad idea?
private ClassLoader createJarClassLoader(File f)
{
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
if (systemClassLoader instanceof URLClassLoader) {
URLClassLoader cl = (URLClassLoader) systemClassLoader;
URL[] urls = cl.getURLs();
List<URL> wantedURLs = new ArrayList<>();
for (URL u : urls) {
if (isSystemURL(u)) {
wantedURLs.add(u);
}
}
try {
wantedURLs.add(f.toURI().toURL());
} catch (MalformedURLException ex) {
return null;
}
return new URLClassLoader(wantedURLs.toArray(new URL[0]), null);
}
return null;
}
private boolean isSystemURL(URL u)
{
return u.getPath().contains("/jre/");
}
You need to set the parent ClassLoader to be the bootstrap ClassLoader. Call getClassLoader() on a java.lang.String object to get the bootstrap ClassLoader and use that in your ClassLoader constructor.
public class MyClassLoader extends URLClassLoader {
protected MyClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
static MyClassLoader getInstance(URL[] urls) {
ClassLoader parent = "".getClass().getClassLoader();
return (new MyClassLoader(urls, parent));
}
}
The representation of the bootstrap classloader is documented as implementation dependent
Some implementations may use null to represent the bootstrap class loader. This method [getClassLoader] will return null in such implementations if this class was loaded by the bootstrap class loader.
but the same wording applies to parent ClassLoader in the ClassLoader constructor, which means that this solution is portable.

Why isn't there a .getURLs() in ClassLoader?

In another answer, I wrote the following code (Java 7):
public final class Foo
{
public static void main(final String... args)
{
final ClassLoader loader = Foo.class.getClassLoader();
final URL[] urLs = ((URLClassLoader) loader).getURLs();
for (final URL url : urLs) {
System.out.println(url);
System.out.println(Files.probeContentType(Paths.get(url.toURI())));
}
}
}
I had to cast the ClassLoader to URLClassLoader in order to have the .getURLs() method.
This is not the first time I've been doing it, and this cast has never failed for me.
Looking at the type hierarchy, I see that:
URLClassLoader -> SecureClassLoader -> ClassLoader
My question is why doesn't ClassLoader define .getURLs()? Are there concrete examples of class loaders being unable to return URLs?
Just search through grepcode. For example JBoss' ModuleClassLoader does not extend URLClassLoader

findLoadedClass() returns null

According to the JVM spec, the class loader that initiates loading of a class is recorded as the initiating class loader by the JVM. Furthermore, according to the JavaDoc of ClassLoader#findLoadedClass() the method
Returns the class with the given binary name if this loader has been recorded by the Java virtual machine as an initiating loader of a class with that binary name.
(emphasis mine)
Consider a simple class loader
class SimpleClassLoader extends ClassLoader {
void foo() {
System.err.println(loadClass("foo.Bar"));
System.err.println(findLoadedClass("foo.Bar"));
}
}
Given that foo.Bar actually exists in the class path, new SimpleClassLoader().foo() prints
class foo.Bar
null
According to the reasons given above, SimpleClassLoader should be the initiating class loader and findLoadedClass("foo.Bar") should just return the successfully loaded class.
Now consider this second version:
class SimpleClassLoader2 extends ClassLoader {
SimpleClassLoader2() {
super(null); // disables delegation
}
protected Class<?> findClass(String name) {
try {
byte[] b = IOUtils.toByteArray(new FileInputStream("path/to/foo/Bar.class"));
return defineClass("foo.Bar", b, 0, b.length);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
void foo() {
System.err.println(loadClass("foo.Bar"));
System.err.println(findLoadedClass("foo.Bar"));
}
}
This makes SimpleClassLoader2 both the initiating as well as the defining class loader of foo.Bar. Indeed, now new SimpleClassLoader2().foo() prints the desired
class foo.Bar
class foo.Bar
So either the documentation is wrong or I don't understand why SimpleClassLoader is not regarded as the initiating class loader of foo.Bar. Can someone please shed some light into this?
I did some more tests and I'm fairly sure the spec is correctly implemented. My mistake was thinking that reflectively loading a class is the same as having it load as part of the resolution step. It makes sense: Both the spec and the JavaDoc mention "recording" of a class loader as the initiating class loader. If I call loadClass() myself, the VM has no way of knowing what class loader should be the initiating class loader, so the defining class loader trivially becomes the initiating class loader as well.
This can be demonstrated by having the loaded class trigger loading of another class (foo.Baz) as part of dependency resolution but have another class loader do the actual loading.*
*I'm pretty sure this is not correct behavior of a valid class loader. I just do it to illustrate a point.
Consider the following classes (they are all in package foo):
public class Bar {
public Bar() {
new Baz();
}
}
and
public class Baz {
}
My custom class loader is now slightly modified:
public class SimpleClassLoader extends ClassLoader {
static final String PATH = "/path/to/classes";
public SimpleClassLoader() {
// disable parent delegation
super(null);
}
public void printLoadedClass(String name) throws Exception {
Class<?> cls = findLoadedClass(name);
System.err.println("findLoadedClass(" + name + ") = " + cls
+ ", has class loader " + cls.getClassLoader());
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name.equals("foo.Baz")) {
// don't want to be defining class loader of foo.Baz
return getSystemClassLoader().loadClass(name);
}
// now we're loading foo.Bar
try {
byte[] b = IOUtils.toByteArray(new FileInputStream(PATH + "/foo/Bar.class"));
return defineClass(name, b, 0, b.length);
} catch (ClassFormatError | IOException e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
}
The test is straight forward:
public static void main(String[] args) throws Exception {
SimpleClassLoader cl = new SimpleClassLoader();
Class<?> cls = cl.loadClass("foo.Bar");
cls.newInstance(); // this triggers resolution
cl.printLoadedClass("foo.Bar");
cl.printLoadedClass("foo.Baz");
}
Output is
findLoadedClass(foo.Bar) = class foo.Bar, has class loader foo.SimpleClassLoader#3a65724d
findLoadedClass(foo.Baz) = class foo.Baz, has class loader sun.misc.Launcher$AppClassLoader#1a2b2cf8
As can be seen: SimpleClassLoader initiates loading of and also defines foo.Bar. Creating the instance triggers resolution of foo.Baz. This time, definition of the class is delegated to the
system class loader so it becomes the defining class loader. The output shows that SimpleClassLoader is initiating class loader for both classes but defines only the first class.

Java ClassLoader change

I have some class A:
public class A {
public A(String str) {
System.out.println("Create A instance: " + str);
}
public void methodA() {
System.out.println("#methodA1()");
}
}
And my class loader implementation:
public class MyClassLoader extends ClassLoader {
public MyClassLoader() {
super();
}
#Override
public synchronized Class<?> loadClass(String name)
throws ClassNotFoundException {
System.out.println("Load: " + name);
return super.loadClass(name);
}
}
And now I try to change default class loader in current thread:
import java.util.ArrayList;
import java.util.List;
public class ChangeLoaderTest {
public static void main(String[] args) {
// Save class loader so that we can restore later.
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
MyClassLoader newLoader = new MyClassLoader();
try {
// Set new classloader.
Thread.currentThread().setContextClassLoader(newLoader);
// My class.
A a = new A("1");
a.methodA();
// Standard Java class.
List<Integer> list = new ArrayList<Integer>();
list.add(2);
list.add(3);
} finally {
// Restore.
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
}
And ChangeLoaderTest output:
Create A instance: 1
#methodA1()
No one
Load: ...
Why? How I can change ClassLoader into some thread?
As Marko Topolnik points out the context classloader is for use by frameworks. To use the classloader yourself you have to call loadClass("somepackage.A") and then use the reflection API to create a new instance of A (Class.newInstance()).
You wont be able to use A or its methods in your source directly since the calling code does not know A - it uses a different classloader. An interface or baseclass of A that can be loaded by the normal classloader can be used to avoid reflection.
interface AIF{
void someMethod();
}
class A implements AIF{
public void someMethod(){}
}
public void test(){
MyLoader loader = new MyLoader();
Class cla = loader.loadClass("A");
AIF a = (AIF) cla.newInstance();
a.someMethod();
}
The contextClassLoader mechanisms is not used by the basic Java operations like new. It's only there so various frameworks can access the context class loader in charge and load resources, classes, etc. Java will always use the classloader that loaded the code that is executing. It's the one that you access via ChangeLoaderTest.class.getClassLoader() -- and there is nothing you can do about this one.
I think that what happens is that your application's class loader which is also your classloader's "parent" can locate A and load it. As a result your classloader will not be searched or used for loading A.
To be honest, I haven't much experience with classloaders but if you subclassed one that uses a URL for the path of the class (so that it can locate the class file) and the parent classloader can not load it (not part of classpath), your custom one will be used.

Categories