Loading an array with a classloader - java

I'm trying to run this code:
public class ClassLoaderTest
{
public static void main(String[] args) throws Exception
{
Object[] obj = new Object[]{};
String cname = obj.getClass().getName();
System.out.println(cname);
ClassLoaderTest.class.getClassLoader().loadClass(cname);
}
}
But it throws a ClassNotFoundException. Interestingly, if I instead use this line:
Class.forName(cname);
it works just fine.
Whats going on here?
edit:
I'm using Java 6. The println prints this:
[Ljava.lang.Object;

They are not the same at all,
Class.forName return the Class object associated with the class of the given name.
In your example, you give to loadClass a String that represent the name of a class, instead of giving it directly a class.
This method does allow you to give a name, however it must be the binary name of the class, not just the class name.
Any class name provided as a String parameter to methods in ClassLoader must be a binary name as defined by The Java™ Language Specification.

First, using a class loader to try and load java.lang.Object array is unlikely to work (since java.lang.Object is loaded by the default class loader). Next, the name given by
Object[] obj = new Object[]{};
String cname = obj.getClass().getName();
System.out.println(cname);
is [Ljava.lang.Object;. Clearly that isn't a class that can be resolved by a ClassLoader - the javadoc says (in part) A class loader is an object that is responsible for loading classes; note it does not say it's responsible for loading arrays. In reflection arrays are handled with java.lang.reflect.Array which says, in part, The Array class provides static methods to dynamically create and access Java arrays. which seems to be what you're looking for.

Looking at the source code around the line that the exception is thrown on, it looks like it is trying to build the filename of the class like this:
String path = name.replace('.', '/').concat(".class");
Given that the value of cname is [Ljava.lang.Object;, I am not especially surprised that the .class file can't be found.

Related

The ClassLoader can replace the array by anything

When I run the following Java code:
ClassLoader c = new ClassLoader() {
#Override
public Class<?> findClass(String name) {
return Object.class;
}
};
Class<?> cc = c.loadClass(Object[][].class.getName());
System.out.println(cc.getName());
I get java.lang.Object in the display terminal, even if I replace Object[][].class.getName() by [[Ljava.lang.Object in the code. The problem is that I was expecting the console to show [[Ljava.lang.Object.
In effect, in the JVM specification, I can read the following:
An array class is created directly by the Java Virtual Machine (§5.3.3), not by a class loader. However, the defining class loader of D is used in the process of creating array class C.
Since Object[][] is an array class, I assumed that my findClass wouldn't be called with the argument [[Ljava.lang.Object but with its element type java.lang.Object.
Further, in the section "Creating Array Classes", the recursive algorithm is actually described:
If the component type is a reference type, the algorithm of this section (§5.3) is applied recursively using class loader L in order to load and thereby create the component type of C.
So my questions are:
Why am I getting this output? Does it mean that I have to manually include this recursive algorithm inside my ClassLoader, rather than letting the JVM doing it for me? If this is what it means, what is the best way to do it?
Am I misinterpreting the "created" in the first quotation? Does it only mean that I can't create the runtime array class, but that I still can patch its loading?
You're asking about the JVM specification, but your test demonstrates the behavior of java.lang.ClassLoader, an independent class which is "invoked by the Java virtual machine to resolve class references". If the JVM is loading an array class, it will bypass the class loader entirely. This can be demonstrated by letting the JVM try to load the class with a custom class loader:
Class<?> clazz = Class.forName("[[Lcom.foo.Test;", true, new ClassLoader() {
#Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
System.out.println("Loading " + name);
return super.loadClass(name, resolve);
}
});
System.out.println(clazz);
Output:
Loading com.foo.Test
class [[Lcom.foo.Test;
As you can see, the component type is initially loaded via the class loader, but the array types are loaded implicitly.

Dynamically creating a subclass at runtime

I'm currently developing a custom ORM framework and utilising ASM to dynamically generate sub classes at runtime. The generation process seems to complete OK, however when I try to instantiate the resulting class I'm getting a "NoClassDefFoundError".
The error seems to pertain to the Super class rather then the actual subclass. Here is an excerpt from the subclass generation method:
private Class generateProxyClass(Class anEntitySuperClass,
List<Field> fieldsToIntercept) throws ClassNotFoundException{
String entitySuperClassName = this.convertToInternalName(anEntitySuperClass.getName());
//String entityProxySubClassName = "com/flux/dynamic/".concat(anEntitySuperClass.getSimpleName()).concat("Proxy");
String entityProxySubClassName = anEntitySuperClass.getSimpleName().concat("Proxy");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_6,ACC_PUBLIC+ACC_SUPER,entityProxySubClassName,null,entitySuperClassName,null);
cw.visitSource(entityProxySubClassName.concat(".java"),null);
//create constructor
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC,"<init>","()V",null,null);
mv.visitCode();
//have our consturctor initailise its super class.
mv.visitVarInsn(ALOAD,0);
mv.visitMethodInsn(INVOKESPECIAL,entitySuperClassName,"<init>","()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0,0);
mv.visitEnd();
this.generateAndAppendProxyAccessorMethods(anEntitySuperClass,fieldsToIntercept, cw);
cw.visitEnd();
//at this point our class should be fully generated an include all required fields. next we
//convert the class to a byte array and pass it in to our helper method to load an
//actual class from the byte array.
return this.loadProxyClass(cw.toByteArray(),entityProxySubClassName);
}
The "loadProxyClass" method called above is a helper method that basically instantiates
and calls a custom ClassLoader in order to load the dynamically created class:
/**loads the generated proxy class from the provided bytes. */
private Class loadProxyClass(byte[] aGeneratedProxyClass,String proxyClassName) throws ClassNotFoundException{
return new ProxyClassLoader(Thread.currentThread().getContextClassLoader(),aGeneratedProxyClass)
.loadClass(this.convertToExternalName(proxyClassName));
}
The ProxyClassLoader simply extends ClassLoader and overrides the "findClass" method in order to load the Dynamically Generated class bytes:
public class ProxyClassLoader extends ClassLoader {
private byte[] rawClassBytes;
public ProxyClassLoader(ClassLoader parentClassLoader,byte[] classBytes){
super(parentClassLoader);
this.rawClassBytes = classBytes;
}
#Override
public Class findClass(String name) {
return defineClass(name,this.rawClassBytes, 0,this.rawClassBytes.length);
}
}
The error I get is: Exception in thread "main" java.lang.NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy)
Where the DummyEntity is the super class I pass into the generateProxyClass method and the DummyEntityProxy is the class I'm attempting to generate. I'm stumped, any help would be greatly appreciated.
Generally, it isn’t a good idea to implement a ClassLoader that tries to return the same class regardless of what it has been asked for. This is perfectly illustrated by the error you get: NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy). The system asked your ClassLoader for a class named DummyEntity and you returned a class named DummyEntityProxy.
The remaining question is why your loader has been asked for that class as usually the parent loader is asked first. It seems that the parent loader has not found the super class which indicates that the parent class loader you have used (Thread.currentThread().getContextClassLoader()) has no access to your super class. It would have been easier if you used anEntitySuperClass.getClassLoader() as parent loader.
Of course, you have to ensure that all other classes used by your generated proxy are accessible by anEntitySuperClass’s class loader. If not, you might need a very complex loader delegation structure to make both group of classes available. It might even be impossible (that depends on what your proxy actually ought to do).
The problem is revealed by your exception's message:
Exception in thread "main" java.lang.NoClassDefFoundError: DummyEntity (wrong name: DummyEntityProxy)
Your class loader expected to load a class DummyEntity but the linked resource contained a class named DummyEntityProxy. How could that happen? It is your class loader's findClass method's implementation:
#Override
public Class findClass(String name) {
return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
}
You do not distinguish what class is attempted to be loaded but you define any class of name with the only class it knows, the DummyEntityProxy's byte representation. Rather implement:
#Override
public Class findClass(String name) {
if (!name.equals(entityProxySubClassName)) {
throw new ClassNotFoundException(name);
}
return defineClass(name, this.rawClassBytes, 0, this.rawClassBytes.length);
}
This way, you are making sure that you are not defining a class of another name. It seems however as if the ProxyClassLoader should not be queried for the class in the first place but that one of its parents should have successfully resolved it.
It seems like ASM is quite a low-level API for your needs. Have you considered a more high-level API like for example my library Byte Buddy? Other ORMs like Hibernate or Eclipse link also use an API on that level, simply because the things you are struggling with are difficult to get right.
Thank you all very much for your suggestions. After many hours of tinkering I managed to resolve the error. It appears that the error was attributed to the method:
this.generateAndAppendProxyAccessorMethods(anEntitySuperClass,fieldsToIntercept, cw);
More specifically, some of the code generated by this method incorrectly referenced the super class by its simple name rather than its internal fully qualified class name. I omitted the implementation of this method from my question for brevity and also because I genuinely didn't expect that the problem was associated with this method. Generally, when errors occur in dynamically generated byte code logic it can be immensely difficult to pinpoint the cause, simply because JVM error messages are so ambiguous.

Class & ClassLoader

I have a Custom Classloader : CustomClassLoader(extends ClassLoader)
I have a class : IntegerPrint
I load my class with my Custom ClassLoader. I was expecting SOPs in the below code to return the same value. But the first SOP prints "sun.misc.Launcher$AppClassLoader#.." & second SOP prints "CustomClassLoader#.."
Why it happens so? Please advise.
public class IntegerPrinterTest {
public static void main(String[] args) throws Exception {
CustomClassLoader loader = new CustomClassLoader(IntegerPrinterTest.class.getClassLoader());
Class<?> clazz = loader.loadClass("IntegerPrinter");
System.out.println(IntegerPrinter.class.getClassLoader());
System.out.println(clazz.getClassLoader());
}
}
What did you expect?
In
System.out.println(IntegerPrinter.class.getClassLoader());
you create a
Class<IntegerPrint>
object, and surely, its class (Class) must have been loaded by some class loader. It takes no genius to imagine that Class must have been loaded very early, even before your code even gains control.
Please run your example with
java -verbose:class ....
to see which classes are laoded in what order.
The first call:
IntegerPrinter.class.getClassLoader()
Will actually do:
IntegerPrinterTest.class.getClassLoader().loadClass("IntegerPrinter")
So it totally ignores your custom classloader.
In other words: your own classloader is not actually used for any objects you create using native calls like "new" etc. To do that it should be responsible for loading the IntegerPrinter class as well.
It is rather circumspect (and in general useless) to do it in the same class but you could do:
Class<?> clazz = loader.loadClass("IntegerPrinterTest");
clazz.getMethod("main").invoke(null);
(note this code is not tested but should approximate something that works)

How to invoke methods of a class (in java) when class name and method name are stored in two different strings

Well, not sure if the question sounds a little weird but let me try to put forth the clarification :
I have a JSP page. On this JSP page, I am calling a java class defined in one of my packages under my projects. This class connects to database and access a table which has got fields namely - functionname, function class. Now I am able to retrieve in my JSP the two strings, lets say -
String funName = "ComFunctions";
String className = "funLog");
Now, I want to invoke this function using this class name i.e. basically something like - className.funName
Is it possible in Java? Actually, these functions and class names will be retrieved in a for loop, so I can't directly call using real classname but have to use strings.
Kindly suggest if there is a way or worl around or if the question is still unclear.
I tried the following approach so far but no luck -
Class c = Class.forName(className);
Object o = c.newInstance();
Method m = c.getMethod(funName, String.class); // Not sure what is supposed to be second parameter here i.e. after funName
Error - the above code gives " No class found error". And i made sure that class is there under the package. Even adding package name i.e. packge.classname didnt help and it says "Symbol not found" for package name.
Any pointers please?
Example class that I am trying to invoke -
package mypackage;
public class ComFunctions extends WDriverInitialize{
public static void main(String[] args){
}
public static void funLog(String username){
System.out.println(userName);
}
}
You need to make sure the compiled class is in the webapp's classpath (ie, WEB-INF/classes) and use the FQN (ie, add the package name). You could also make a JAR file of your classes and add that to the WEB-INF/lib folder.
Also, the extra parameter in getMethod is to fetch a method with the matching parameters (ie, in your example, one that takes a String
You're missing one piece of the puzzle, and that's the method arguments. Without it, you can't really be sure what method funName is referring to, and what arguments to pass to it.
And of course, the class needs to be in the classpath.

How to Read Java File Structure using Java?

I'm trying to read a java file and display in console the package, class and method name. something like this:
File: Test.java
package tspec.test;
public class Test {
public void addTest () {}
public void deleteTest () {}
}
Output:
package name: tspec.test
class name: Test
method name:
addTest
deleteTest
Thanks in advance :)
This can be accomplished using the Java Compiler API (introduced in Java 6). Unfortunately, this solution is limited to Sun's JDK. Therefore, you will have to have that JDK installed and you must include its tools.jar file in your class path.
public void displayInformation(File javaSourceFile) throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// The file manager locates your Java source file for the compiler. Null arguments indicate I am comfortable with its default behavior.
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// These will be parsed by the compiler
Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(javaSourceFile);
// Creates a new compilation task. This doesn't actually start the compilation process.
// Null arguments indicate I am comfortable with its default behavior.
CompilationTask task = compiler.getTask(null, null, null, null, null, fileObjects);
// Cast to the Sun-specific CompilationTask.
com.sun.tools.javac.api.JavacTaskImpl javacTask = (com.sun.tools.javac.api.JavacTaskImpl) task;
// The Sun-specific JavacTaskImpl can parse the source file without compiling it, returning
// one CompilationUnitTree for each JavaFileObject given to the compiler.getTask call (only one in our case).
Iterable<? extends CompilationUnitTree> trees = javacTask.parse();
CompilationUnitTree tree = trees.iterator().next();
// Create a class that implements the com.sun.source.tree.TreeVisitor interface.
// The com.sun.source.util.TreeScanner is a good choice because it already implements most of the logic.
// We just override the methods we're interested in.
class MyTreeVisitor extends TreeScanner<Void, Void> {
#Override
public Void visitClass(ClassTree classTree, Void p) {
System.out.println("class name: " + classTree.getSimpleName());
System.out.println("method name:");
return super.visitClass(classTree, p);
}
#Override
public Void visitMethod(MethodTree methodTree, Void p) {
System.out.println(methodTree.getName());
return super.visitMethod(methodTree, p);
}
}
tree.accept(new MyTreeVisitor(), null);
}
When I pass this method a File whose content is your sample, I receive this output:
class name: Test
method name:
addTest
deleteTest
Unfortunately, I haven't yet figured out where the package name is stored.
Reflection and Introspection Java API's.
It's purpose is to introspect Java code and report back about it's contents. With Reflection you can do things like :
Class.forName(className).getDeclaredMethods();
Java also has the Java Mirror API with similiar functionality, but is not as commonly used.
Both of these solutions require no 3rd party libraries or tools.
The only difficult is the java code may not be well formatted. like the function declaration can be spread on multiple lines.
The ultimate solution is to create an automata to tokenize the source code first and then apply some compiler technique to grab what you want from the parsed data.
We use PMD Java code analyzer to solve similar problem.
It is useful.
http://pmd.sourceforge.net/
You don't have to do this by parsing the Java file yourself! Java already contains a way of getting information about its own classes, methods, and packages: it's called reflection.
Have a look at the java.lang.Class class. Each instance of this class represents a particular Java class, and contains methods to return the class name, the package it lives in, the methods it contains, and lots more information.
Also worth looking at is the java.lang.reflect package, since some of the methods of Class return types from this package. The package contains classes to represent things like methods, types, fields, and so on.
To obtain a Class instance of your Test class, you can use the following code:
Class<?> testclass = Class.forName("tspec.test.Test");
This returns a class of an unknown type, which is what the question mark inside the angle brackets means if you're not familiar with generics. Why the type of the class instance is unknown is because you specify the class name with a string, which is parsed at runtime. At compile-time, Java cannot be sure that the string passed to forName even represent a valid class at all.
However, testclass as defined above will be fine for getting the class's name, methods, and containing package.

Categories

Resources