Trouble creating custom class loader - java

I am trying to create a custom class loader to accomplish the following:
I have a class in package com.company.MyClass
When the class loader is asked to load anything in the following format:
com.company.[additionalPart].MyClass
I'd like the class loader to load com.company.MyClass effectively ignoring the [additionalPart] of the package name.
Here is the code I have:
public class MyClassLoader extends ClassLoader {
private String packageName = "com.company.";
final private String myClass = "MyClass";
public MyClassLoader(ClassLoader parent) {
super(parent);
}
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> clazz = null;
String className = name;
// Check if the class name to load is of the format "com.company.[something].MyClass
if (name.startsWith(packageName)) {
String restOfClass = className.substring(packageName.length());
// Check if there is some additional part to the package name
int index = restOfClass.indexOf('.');
if (index != -1) {
restOfClass = restOfClass.substring(index + 1);
//finally, check if the class name equals MyClass
if (restOfClass.equals(myClass)) {
// load com.company.MyClass instead of com.company.[something].MyClass
className = packageName + myClass;
clazz = super.loadClass(className, true);
}
}
}
if (clazz == null) {
// Normal clase: just let the parent class loader load the class as usual
clazz = super.loadClass(name);
}
return clazz;
}
}
And here is my test code:
public class TestClassLoader {
public void testClassLoader () throws Exception {
ClassLoader loader = new MyClassLoader(this.getClass().getClassLoader());
clazz = Class.forName("com.company.something.MyClass", true, loader );
}
public static void main (String[] args) throws Exception {
TestClassLoader tcl = new TestClassLoader();
tcl.testClassLoader();
}
}
MyClassLoader picks up the correct class (i.e. com.company.MyClass) and returns it just correctly from loadClass (I have stepped through the code), however, it throws an exception at some later point (i.e. after loadClass is called from the JVM) as follows:
Exception in thread "main"
java.lang.ClassNotFoundException:
com/company/something/MyClass at
java.lang.Class.forName0(Native
Method)
at
java.lang.Class.forName(Class.java:247)
at [my code]
Now, I realize some of you may be thinking "Why would anyone need to do this? There has to be a better way". I am sure that there is, but this is something of education for me, as I'd like to understand how class loaders work, and gain a deeper understanding into the jvm class loading process. So, if you can overlook the inanity of the procedure, and humor me, I'd be very grateful.

Your Question
This is pure speculation. The class name is stored in the java byte code. Thus the classes you manage to load by this technique will be defective. This is probably confusing the system.
The ClassLoader probably keeps a reference to com.company.something.MyClass, but the class itself probably keeps a reference to com.company.MyClass. (I use probably a lot because I don't really know for sure.) Probably everything works OK until you use the MyClass class for something. Then the inconsistency creates trouble. So when is this exception thrown?
If you are interested in learning how class loaders work, then you can use javap to get at the byte code. This would also allow you to check my hypothesis.
If my hypothesis is correct, then the solution would be to fix the byte code. There are several packages that allow you to engineer byte code. Copy a class, change the name of the copied class, and then load it.
Aside
While not relevant to your question: I find the below to be unnecessarily complicated (and it doesn't work on com.company.something.somethingelse.MyClass).
// Check if the class name to load is of the format "com.company.[something].MyClass
if (name.startsWith(packageName)) {
String restOfClass = className.substring(packageName.length());
// Check if there is some additional part to the package name
int index = restOfClass.indexOf('.');
if (index != -1) {
restOfClass = restOfClass.substring(index + 1);
//finally, check if the class name equals MyClass
if (restOfClass.equals(myClass)) {
// load com.company.MyClass instead of com.company.[something].MyClass
className = packageName + myClass;
clazz = super.loadClass(className, true);
}
}
Why not?
//Check if the class name to load is of the format "com.com // Check if the class name to load is of the format "com.company.[something].MyClass"
if ( ( name . startsWith ( packageName ) ) && ( name . endsWith ( myClass ) ) )

I don't think you can really do that via a classloader. Theoretically if some other class is attempting to load a class it assumes is called 'com.mycompany.foo.MyClass' then it's too late, someone already has a class with bytecode referencing 'com.mycompany.foo' and that class is already loaded.
Repackaging is a lot easier at the static level, by using something like ASM to repackage all the code at build time. You of course have to modify both he classes package itself and all the classes that my refer to that package.
If you use Maven, check out the shade plugin. If not I seem to recall a tool called JarJar.
You can of course do that kind of byte code manipulation at runtime via a javaagent and a ClassTransformer. The code for the maven-shade-plugin is actually pretty small -- if you grabbed it and ripped out the maven parts you'd probably have something working in 2-3 days.

Related

What is the best way to approximate class.getSimpleName() without loading class?

Given a fully qualified class name that can be loaded with Class.forName(), is there a way to transform the name into what would be the result of loading the class and invoking getSimpleName() without actually attempting to load the class?
I need this capability for reflection purposes.
I'm going to say that you can't do it simply based on the name.
You can try to split on . and $, but this example code demonstrates that it is not always obvious where the simple name begins:
class Ideone
{
private static class Bar {};
public static void main (String[] args) throws java.lang.Exception
{
class Foo$o {
class Bar$bar {}
};
class Foo$o$Bar {
class Bar$bar {}
};
class Foo$o$Bar$Bar$bar {}
print(Ideone.class);
print(Bar.class);
print(Foo$o.class);
print(Foo$o.Bar$bar.class);
print(Foo$o$Bar.Bar$bar.class);
print(Foo$o$Bar$Bar$bar.class);
}
private static void print(Class<?> clazz) {
System.out.printf("fqn=%s, sn=%s%n", clazz.getName(), clazz.getSimpleName());
}
}
Output:
fqn=Ideone, sn=Ideone
fqn=Ideone$Bar, sn=Bar
fqn=Ideone$1Foo$o, sn=Foo$o
fqn=Ideone$1Foo$o$Bar$bar, sn=Bar$bar
fqn=Ideone$1Foo$o$Bar$Bar$bar, sn=Bar$bar
fqn=Ideone$2Foo$o$Bar$Bar$bar, sn=Foo$o$Bar$Bar$bar
Ideone demo
i.e. if you were to say "the bit of the name after the final $ or .", you'd be wrong.
The only conclusive way to do this is to load the class, potentially without initializing it:
Class<?> clazz = Class.forName(className, false, someClassLoadeR);
As demonstrated by the answer of #AndyTurner you cannot derive the simple name from the qualified class string in all cases.
But if the constraint without actually attempting to load the class does not forbid to read the contents of the class file, you could do the following (for the edge cases):
Get a InputStream for the class file contents via Class.getResourceAsStream()
Parse the beginning of the class file and read the super class name from the constant pool.
(as commented by #shmosel) Implement the logic of Class.getSimpleName(). The super class name allows you to replace Class.getSimpleBinaryString() which relies on an already loaded class.

ByteBuddy fails when trying to redefine sun.reflect.GeneratedMethodAccessor1

Driven by curiosity, I tried to export the bytecode of GeneratedMethodAccessor1 (generated by the JVM when using reflection).
I try to get the bytecode of the class the following way:
public class MethodExtractor {
public static void main(String[] args) throws Exception {
ExampleClass example = new ExampleClass();
Method exampleMethod = ExampleClass.class
.getDeclaredMethod("exampleMethod");
exampleMethod.setAccessible(true);
int rndSum = 0;
for (int i = 0; i < 20; i++) {
rndSum += (Integer) exampleMethod.invoke(example);
}
Field field = Method.class.getDeclaredField("methodAccessor");
field.setAccessible(true);
Object methodAccessor = field.get(exampleMethod);
Field delegate = methodAccessor.getClass().getDeclaredField("delegate");
delegate.setAccessible(true);
Object gma = delegate.get(methodAccessor);
ByteBuddyAgent.installOnOpenJDK();
try {
ClassFileLocator classFileLocator = ClassFileLocator.AgentBased
.fromInstalledAgent(gma.getClass().getClassLoader());
Unloaded<? extends Object> unloaded = new ByteBuddy().redefine(
gma.getClass(), classFileLocator).make();
Map<TypeDescription, File> saved = unloaded.saveIn(Files
.createTempDirectory("javaproxy").toFile());
saved.forEach((t, u) -> System.out.println(u.getAbsolutePath()));
} catch (IOException e) {
throw new RuntimeException("Failed to save class to file");
}
}
}
I however get the following error when executing this class:
Exception in thread "main" java.lang.NullPointerException
at net.bytebuddy.dynamic.scaffold.TypeWriter$Engine$ForRedefinition.create(TypeWriter.java:172)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1182)
at net.bytebuddy.dynamic.scaffold.inline.InlineDynamicTypeBuilder.make(InlineDynamicTypeBuilder.java:244)
at reegnz.dyna.proxy.extractor.MethodExtractor.main(MethodExtractor.java:48)
Basically I first iterate on the method call enough times for the JVM to inflate the method (generate the GeneratedMethodAccessor) and then try to redefine the class to get the bytecode.
I tried the same method to export a generated Proxy class, and it worked flawlessly. That's what drove me to try this.
It seems that the DelegatingClassLoader of the GeneratedMethodAccessor1 class can't even reload the class when I try to load the class with the loadClass method.
Any ideas how I could retrieve the bytecode for GeneratedMethodAccessor classes?
First of all, the NullPointerException is a bug, I just fixed that. The loader should have thrown an IllegalArgumentException instead but it never got that far. Thanks for bringing this to my attention.
Boiled down, the problem Byte Buddy is facing is that
gma.getClass().getClassLoader().findClass(gma.getClass().getName());
throws a ClassNotFoundException. This is a consequence of using a DelegatingClassLoader for the accessor classes. As an educated guess, I think that this class loader intends to shield its classes from the outside in order to make them easily garbage collectable. However, not allowing the lookup of a class what somewhat breaks the contract for a ClassLoader. Apart from that, I assume that this loading routine will be refactored to use the JDK's anonymous class loaders at some point in the future (similar to classes representing lambda expressions). Strangely enough, it seems like the source code for the DelegatingClassLoader is not available in the JDK even though I can find it in the distribution. Probably, the VM treats these loader specially at some place.
For now, you can use the following ClassFileTransformer which uses some reflection magic on the class loader to locate the loaded class and to then extract the byte array. (The ClassFileLocator interface only takes a name instead of a loaded class in order to allow working with unloaded types which is normally always the case. No idea why this does not work in this case.)
class DelegateExtractor extends ClassFileLocator.AgentBased {
private final ClassLoader classLoader;
private final Instrumentation instrumentation;
public DelegateExtractor(ClassLoader classLoader, Instrumentation instrumentation) {
super(classLoader, instrumentation);
this.classLoader = classLoader;
this.instrumentation = instrumentation;
}
#Override
public Resolution locate(String typeName) {
try {
ExtractionClassFileTransformer classFileTransformer =
new ExtractionClassFileTransformer(classLoader, typeName);
try {
instrumentation.addTransformer(classFileTransformer, true);
// Start nasty hack
Field field = ClassLoader.class.getDeclaredField("classes");
field.setAccessible(true);
instrumentation.retransformClasses(
(Class<?>) ((Vector<?>) field.get(classLoader)).get(0));
// End nasty hack
byte[] binaryRepresentation = classFileTransformer.getBinaryRepresentation();
return binaryRepresentation == null
? Resolution.Illegal.INSTANCE
: new Resolution.Explicit(binaryRepresentation);
} finally {
instrumentation.removeTransformer(classFileTransformer);
}
} catch (Exception ignored) {
return Resolution.Illegal.INSTANCE;
}
}
}
To further simplify your code, you can use the ClassFileLocators directly instead of applying a rewrite which as a matter of fact might slightly modify the class file even if you do not apply any changes to a class.

Loading Inner Classes using a ClassLoader

I am writing a program that lets users type java code into a text area, then compile it and load it into the program as a sort of "plugin." I am currently able to compile the .java files and load the outer classes, but I am not able to load/instantiate inner classes written by users without errors. Currently this is what I use to load the outer classes, this code works and I am able to easily use the outer classes without complications whatsoever. (I did some editing for better readability, if you notice a typo tell me)
private ArrayList<String> execute(ArrayList<String> fileNames) {
ArrayList<String> successStories = new ArrayList();
ArrayList<Class<?>> eventHandlers = new ArrayList();
// Load all classes first...
for (int i = 0; i < fileNames.size(); i++) {
Class<?> clazz = loadClassByName2(fileNames.get(i));
if (EventHandler.class.isAssignableFrom(clazz)) {
eventHandlers.add(clazz);
successStories.add(fileNames.get(i));
} else if (InterfaceInnerClass.class.isAssignableFrom(clazz)) {
successStories.add(fileNames.get(i));
} else {
System.out.println(clazz.getName() + " couldn't be loaded");
}
}
// Then instantiate the handlers.
for (int i = 0; i < eventHandlers.size(); i++) {
try {
Object obj = eventHandlers.get(i).newInstance();
if (obj instanceof EventHandler) {
EventHandler EH = (EventHandler)obj;
EH.name = EH.getClass().getSimpleName();
CmdEvents.addEvent(EH);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return successStories;
}
public static Class<?> loadClassByName2(String name) {
try {
// My program sets up classpath environment variables so "./" is all that is needed as the URL
URLClassLoader classLoader = new URLClassLoader(
new URL[] { new File("./").toURI().toURL() });
// Load the class from the classloader by name....
Class<?> c = classLoader.loadClass("plugins.event_handlers." + name);
classLoader.close();
return c;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
The original list of file names is sent from a GUI with every .class file in the plugin directory listed. They user selects the classes they want to load, clicks a button and it sends those filenames to these methods. Within this code EventHandler is a class which you will see below, InterfaceInnerClass is simply an interface used as a tag to ensure there aren't any serious problems, and CmdEvents is a console command within my program used to manage these "plugin" classes. Like I said above, this code works fine for outer classes, the problem is when I try to load inner classes. The code for my EventHandler abstract class is as follows.
public abstract class EventHandler {
public String name; // Don't mind this being public, I have my reasons for this.
public abstract void execute(String input);
public abstract boolean condition(String input);
}
The way my program works, it recieves a String from a user, then calls condition(String) and if it returns true, it calls execute(String). I wrote some test code to try out my loader as follows.
package plugins.event_handlers;
public class Test_Handler extends events.EventHandler {
public void execute(String input) {
System.out.println("Testing...");
TestInner inner = new TestInner();
inner.test();
System.out.println("Did it work?");
}
public boolean condition(String input) {
return input.contains("testinput");
}
public class TestInner implements events.InterfaceInnerClass {
public TestInner() {
System.out.println("The inner works!");
}
public void test() {
System.out.println("Inner class has been tested");
}
}
}
I run my program, select both the Test_Handler.class and Test_Handler$TestInner.class, then click the button. When the method returns an ArrayList or successfully loaded classes, it returns BOTH the outer and inner class. However, when I run the program and pass "testinput" to the condition and execute methods, this is my output.
Testing... Exception in thread "Execute_Thread_Test_Handler"
java.lang.NoClassDefFoundError:
plugins/event_handlers/Test_Handler$TestInner at
plugins.event_handlers.Test_Handler.execute(Test_Handler.java:11) at
events.ThreadEventExecutor.run(ThreadEventExecutor.java:20) Caused by:
java.lang.ClassNotFoundException:
plugins.event_handlers.Test_Handler$TestInner at
java.net.URLClassLoader$1.run(Unknown Source) at
java.net.URLClassLoader$1.run(Unknown Source) at
java.security.AccessController.doPrivileged(Native Method) at
java.net.URLClassLoader.findClass(Unknown Source) at
java.lang.ClassLoader.loadClass(Unknown Source) at
java.lang.ClassLoader.loadClass(Unknown Source) ... 2 more
What I am wanting it to print is
Testing...
The inner works!
Inner class has been tested
Did it work?
So finally my question, how do I make the above code work? I do not want to have to make my users write their own classloaders and such to load an inner/seperate class (because not all my users will necessarily be amazing at coding) so I need to be able to reference the inner class type without the code blowing up.
Well its been a very long time since I asked but I was looking back and forth between my code and the classloader javadocs and I'm simply being dumb.
In my loadClassByName2 I call classLoader.close(); which should NOT be done if the freshly loaded class is going to load any more classes.
According to the javadocs, each class type keeps track of the classloader that loaded it. If that class type ever needs to reference an unloaded class, it calls upon its classloader to find and load it. When I closed the classloader immediately after loading the single class I made it so the classloader was not able to find/load any other classes (including local/internal classes).

How does Java find already loaded classes?

I know that Java uses the ClassLoader hierarchy for loading the classes.
For example a program:
public void test(){
A a = new A(); // Line 1 The class is accessed first time here so it should be loaded and defined
A ab = new A(); //Line 2 How can the second line be represented?
}
The first line of the code is similar to
Thread.currentThread().getContextClassLoader().loadClass("A");
So the class is loaded and defined to create instance of Class.
Now the question is when the second line is executed the Class A is referred again, will Java not lookup for the class again and return the same loaded instance of the Class?
As the Java classloader document says that every class loader should maintain the instances of loaded classes and return the same instances for the next call.
Where does Java keep the loaded classes? ClassLoader class has a Vector of classes which is called by VM to add the loaded classes.
Maybe the question is a bit confusing, basically I am trying to figure out from which method are the already loaded classes returned. I tried to keep a debug point in the loadClass() method but it is not called for the Line 2.
The loadClass() method of ClassLoader has findLoadedClass method but that too is not called.
If you want to "translate" the mention of A to any method call, then the closest you could get is not loadClass() but Class.forName().
This method call queries the classloader for the class, which may or may not trigger class loading (and the caller doesn't even care). It will simply return a fully loaded (and initialized, if you don't use the three-argument version) class back to the caller.
And once the class has been loaded, the class loader no longer get's invoked when the class is used (as the name suggests, it's job is done, once it loaded the class).
package java_language;
public class NewClass {
Java_language j;
public NewClass() throws ClassNotFoundException {
j=new Java_language();
if (Class.forName("java_language.Java_language", true, Thread.currentThread().getContextClassLoader()).equals(j.getClass())) {
System.out.println("CLass has been loaded");
}
}
public static void main(String[] args) throws ClassNotFoundException {
new NewClass();
}
}
package java_language;
public class Java_language {
static Java_language java_language = null;
public Java_language() {
System.out.println("Stack Overflow");
}
}
Ans is:
enter image description here

What's the difference between ClassLoader.load(name) and Class.forName(name) [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Difference betweeen Loading a class using ClassLoader and Class.forName
AFAIK, two ways are provided in java to init a class from its name.
Class
public static Class forName(String
className) throws
ClassNotFoundException
public static Class forName(String
name, boolean initialize, ClassLoader
loader) throws ClassNotFoundException
ClassLoader:
public Class loadClass(String
name) throws ClassNotFoundException {
return loadClass(name, false);
}
The known thing is in forName method, we can specify the flag of initialize to be false ,this will skip some static things to be initialized for this class. But what's else?
And how should I use them correctly?
It's better you can show some good examples.
Thanks!
UPDATE:
After raised question,I made some simple classLoader test.
ClassLoader cls = ClassLoader.getSystemClassLoader();
Class someClass = cls.loadClass("Test");
Class someClass0= Class.forName("Test");
Class someClass1= Class.forName("Test",false,cls);
URL[] urls = new URL[] {new File("bin/").toURL()};
ClassLoader cls2 = new URLClassLoader(urls, null);
Class someClass2 = cls2.loadClass("Test");
ClassLoader cls3 = new URLClassLoader(urls, cls);
Class someClass3 = cls3.loadClass("Test");
System.out.println(someClass.equals(someClass0));
System.out.println(someClass.equals(someClass1));
System.out.println(someClass.equals(someClass2));
System.out.println(someClass.equals(someClass3));
The result is
true,true,false,true
UPDATE
Here is my answer about
Difference between loadClass(String name) and loadClass(String name, boolean resolve)
Consider this code
class X
{
static{ System.out.println("init class X..."); }
int foo(){ return 1; }
Y bar(){ return new Y(); }
}
The most basic API is ClassLoader.loadClass(String name, boolean resolve)
Class classX = classLoader.loadClass("X", resolve);
If resolve is true, it will also try to load all classes referenced by X. In this case, Y will also be loaded. If resolve is false, Y will not be loaded at this point.
There doesn't seems to be any good reason for resolve=true. If nobody calls X.bar(), Y will never be needed, why should we load it at this point? And if Y is missing or corrupt, we'll get an error trying to load X, which is totally unnecessary.
Interestingly, this method is protected, so it's not easy to invoke it.
Another method loadClass(name) simply calls loadClass(name,false). It's public, and it takes the sensible choice of resolve=false. So it is exactly what's needed by developers.
ClassLoader only loads classes, it does not initialize classes. We can inspect the class metadata, e.g. its super class, its annotations, its methods and fields, etc. without triggering the static initialization execution. This fact is very important for frameworks.
Now, Class.forName
Basically, Class.forName(String name, boolean initialize, ClassLoader loader) calls loader.loadClass(name). And if initialize=true, the class is initialized - in the X example, we'll see "init class X..." printed.
Class.forName(name) is the same as forName(name, true, currentLoader).
Now, why would anyone want to initialize the class at this point? Wouldn't it be better if the class is initialized only when necessary? A famous use case is JDBC initializing:
Class.forName("com.mysql.jdbc.Driver");
The convention is, a JDBC driver class registers itself in its static initializer. The above code will trigger the static initialization, making the driver available for subsequent uses.
From today's point of view, that design is really odd. We usually don't rely on static initializers. So there isn't much justification for initialize=true, and Class.forName(name) should be avoided.
A "class literal" will return the class, without initializing it
Class c = com.mysql.jdbc.Driver.class;
// actually compiled to
Class c = Class.forName("com.mysql.jdbc.Driver", false, currentLoader);
Now, what the heck is the "currentLoader"? It is the class loader of the current class
class A
{
void foo()
{
currenLoader == THIS_A_CLASS.getClassLoader()
}
}
When X.bar() is invoked for the first time, a "Y" class is needed. what's happening is roughly
class X
bar()
// new Y();
Class classY = currentLoader.loadClass("Y");
Constructor cst = classY.getConstructor();
// next line will initialize Y (if not yet)
// creating an instance of a class requires it be initialized
Object y = cst.newInstance();
ClassLoader.loadClass(String name) will attempt to load the class using the specified classloader. Class.forName(String name) will attempt to load the class using the default system classloader hierarchy.

Categories

Resources