Can't find Class with Class.forName() but it exists - java

I have a program in which I am generating classes at runtime (included only variable and associated getters and setters methods). Later I want to fill the classes.
To get the class - I know its Name, but its not in the classpath - I tried .forName() but I always get a ClassNotFoundException.
Here is my example:
Exception in thread "main" java.lang.ClassNotFoundException: com.test.wam.business.wsobjects.Testclass
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 java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at gui.Application.main(Application.java:94)
And the code:
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
DynamicURLClassLoader dynamicURLClassLoader = new DynamicURLClassLoader(urlClassLoader);
dynamicURLClassLoader.addURL(new URL("file://C:\\dev\\Eclipse_was\\guitest\\generated"));
Class c = Class.forName("com.test.wam.business.wsobjects.Testclass");
Object classInstance = c.newInstance();
The ClassLoader:
public class DynamicURLClassLoader extends URLClassLoader {
public DynamicURLClassLoader(URLClassLoader classLoader) {
super(classLoader.getURLs());
}
#Override
public void addURL(URL url) {
super.addURL(url);
}
}
The full qualified Name to the file (created with eclipse -> copy full qualified Name)
/guitest/generated/com/test/wam/business/wsobjects/Testclass.java
What is wrong here?

I can see two problems:
1) You are creating a DynamicURLClassLoader and adding the URL to it, but you are not actually using it. This statement:
Class c = Class.forName("com.test.wam.business.wsobjects.Testclass");
will use the classloader that loaded the current class. That is probably the application's default classloader, but it is certainly NOT the classloader you just created. The javadoc says:
[Class.forName(className)] returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of the current class.
So .... the solution is:
Class c = Class.forName("com.test.wam.business.wsobjects.Testclass",
true, dynamicURLClassLoader);
2) This string:
"file://C:\\dev\\Eclipse_was\\guitest\\generated"
is not a valid "file:" URL. The correct URL for the path you are trying to reference would be:
"file:///C:/dev/Eclipse_was/guitest/generated"
The way that you wrote the URL might work, but it is not the correct way to do it.
Reference:
File URIs in Windows

The method Class.forName(String) uses the ClassLoader of the caller class, if you want to use a specific ClassLoader to load your class you need to use Class.forName(String name, boolean initialize, ClassLoader loader) instead as next:
Class c = Class.forName(
"com.test.wam.business.wsobjects.Testclass", true, dynamicURLClassLoader
);
NB: This will work if and only if the URL that you provide to your DynamicURLClassLoader is valid and is the path to the parent folder in which you have your class

Related

Get class properties from class absolute path using reflection in java

im generating new classes using file package but when i want to get the class properties using reflection it doesn't work and it gave me the error bellow but when i refresh my package by clicking on the right button of my mouse and i click on refresh and i rerun my function it works.
So now i think that i need to change my method to get those properties by using absolut path instead of name of package.
my code
import java.lang.reflect.Field;
public class Main7{
public static void main(String[] args) throws Exception {
Class classe = Class.forName("com.test.model.Client");
// affiche tous les attributs
System.out.println("Attributs -------------------------------------");
for (Field attribut : classe.getDeclaredFields()) {
// System.out.print(" "+Modifier.toString(attribut.getModifiers()));
String type = attribut.getType().getName();
if(type.contains(".")) {
String[] tab = type.split("\\.");
type=tab[2];
}
System.out.println(type+" "+attribut.getName());
}
}
}
error
Exception in thread "main" java.lang.ClassNotFoundException: ma.dxc.generator.model.Client
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at generatortest.Main7.main(Main7.java:16)
good result
Attributs -------------------------------------
long id
String name
String city
The easiest way to get class properties is like this:
Class klass = com.test.model.Client.class;
Or simply:
Class klass = Client.class;
If you've already imported the class above.
Or if you have an intance of an object:
Class klass = obj.getClass();
Class.forName() is used to dynamically load new classes into the system. And is not necessary to obtain the class properties through reflection.
Once you have the Class object you can obtain more info like getDeclaredFields() and others.
If you want to load something outside the current classpath you could use URLClassLoader like this:
URLClassLoader loader = new URLClassLoader(new URL[] { "file://path/to/jar/or/directory"});
Class klass = loader.loadClass("com.test.model.Client");
This article may be relevant as well: How to use URLClassLoader to load a *.class file?
Class.forName() won't look beyond your classpath. Its intended to dynamically load something that has already been added to your classpath.

Loading of imported classes of loaded classes using Reflection in Java

I am trying to load classes from a jar file. Basically, I want to call a method in a particular class in a package of that jar. The problem I am facing here is that after the class is successfully loaded from the jar and when I try to instantiate I get exception : ClassNotFound for classes imported in my class.
Here is the class which loads the class:
inputs: D:\Myjar.jar , com.vendor.epbroker.VNFLCMCommunicator
public Class<?> loadClass(String libPath, String pkgName) {
LogManager.getLogger().info("Adding Class");
File jarFile = null;
try {
jarFile = new File(libPath);
URL fileURL = jarFile.toURI().toURL();
String jarURL = "jar:" + fileURL + "!/";
URL urls[] = { new URL(jarURL) };
URLClassLoader ucl = new URLClassLoader(urls);
Class<?> beanClass = ucl.loadClass(pkgName);
ucl.close();
return beanClass;
} catch (Exception ex) {
LogManager.getLogger().error("Given Library: " + libPath + " or Class name: " + pkgName + " is not Valid");
LogManager.getLogger().error("Exception occurred : ", ex);
}
LogManager.getLogger().error("Class loading Error: Returning NULL");
return null;
}
The code snippet which receives this Class:
Object instance = classToLoad.newInstance();
// To get the list of methods exist in the Class
Method[] listOfMethods = classToLoad.getMethods();
The following error is encountered:
SEVERE: Servlet.service() for servlet [spring] in context with path [/vnflcm] threw exception [Handler processing failed; nested exception is java.lang.NoClassDefFoundError: com/vendor/epbroker/exception/EPBrokerException] with root cause
java.lang.ClassNotFoundException: com.vendor.epbroker.exception.EPBrokerException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getConstructor0(Class.java:3075)
Any help would be appreciated?
Have a look at the following lines:
URLClassLoader ucl = new URLClassLoader(urls);
Class<?> beanClass = ucl.loadClass(pkgName);
ucl.close();
and consider the documentation of URLClassLoader.close():
Closes this URLClassLoader, so that it can no longer be used to load new classes or resources that are defined by this loader.
In other words, you should only close a class loader if you are really done using the classes of that loader. Even if all required classes had already been loaded at this point, there was still the possibility that a required resource needs to be accessed. Note that some frameworks have there own reflection library requiring access to the byte code of the classes, which will be accesses like a resource.
In your specific case, it’s even simpler. You have just loaded one class, which only triggered resolving of the required minimum set of classes (e.g. the direct super class), but no other dependencies. Then you close the class loader, preventing subsequent loading of any other class from your jar file, which hits you when resolving the constructors needs resolving more referenced classes.
There are a few considerations you have to take when trying to use Reflection.
Your URLClassLoader must contain the URL of the jar that you want to reflect into.
If the desired jar depends on any other jars, you must load the URLs for those jars as well.
As #VGR pointed out, you cannot simply use the file path for the jar to use as a URL. One thing you can do is :
File myJar = new File("path/to/myJar.jar");
URL myJarUrl = myJar.toURI().toURL();
A simple example to demonstrate the issue:
Let's call your jar myToolProject. And let's say while developing this tool you created a class, call it JsonMaker, that converts a POJO to a JSON and you accomplish this via the gson.jar. When you build your jar, let's call it myjar.jar, you mention that in the manifest that it depends on gson.
When trying to reflect on myjar, you reflect on each class in your jar, until you reach JsonMaker.class. When trying to reflect here we notice that there is com.google.Gson type object here. The URLClassLoader looks through the URLS in its array and tries to find com.google.Gson in some class. If it cannot find any com.google.Gson class, it cannot reflect on that class, and throws a ClassNotFoundException.

Use object created by Classloader without interface call or reflect invoke?

I'm using java to do some thing as same as C++ Dynamic Library usage.
I didn't find the way to directly use the Same Class Object without reflect invoke style code.
this is my dynamic library code, I make it a jar.
package com.demo;
public class Logic {
public String doWork() {
System.out.println("Hello from Dll");
return "Dll";
}
}
In my main application, I can create the instance by URLClassLoader, invoke by reflect is fine:
public class Main {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
File file = new File("C:\\plugin.jar");
URL url = file.toURI().toURL();
URL[] urls = {url};
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls, parentLoader);
Thread.currentThread().setContextClassLoader(loader);
Class<?> clazz = loader.loadClass("com.demo.Logic");
System.out.println("New Instance!!");
Object logic = clazz.newInstance();
Method method = logic.getClass().getMethod("doWork");
method.invoke(logic);
}
Output:
New Instance!!
Hello from Dll
But when I change the code without using Reflect invoke:
public class Main {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
File file = new File("C:\\plugin.jar");
URL url = file.toURI().toURL();
URL[] urls = {url};
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls, parentLoader);
Thread.currentThread().setContextClassLoader(loader);
Class<?> clazz = loader.loadClass("com.demo.Logic");
Logic logic = (Logic)clazz.newInstance();
logic.doWork();
}
}
Compile success(compile with external modules), but when I run the program, it failed at the line Logic logic = (Logic)clazz.newInstance();
Exception:
Exception in thread "main" java.lang.NoClassDefFoundError: com/demo/Logic
at Main.main(Main.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: java.lang.ClassNotFoundException: com.demo.Logic
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 6 more
Is there any way to make it work? Without reflect/interface.(In C++ I can easily achieve this, share same struct/class declare, make sure using same compiler compile two parts. IMHO Java would do it also)
additional explanation 1
I want change the current classloader behavior to make it recognize the dynamic loaded class, this try is to simple and naive, can't find other direction:
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls, parentLoader);
Thread.currentThread().setContextClassLoader(loader);
In order to make this work, you have to logically split the classes up into three sets:
Your main class
Your plugin classes
Your plugin-dependent classes
When you creating a new class loader, you have to ensure that classes #2 and #3 are both loaded by the same class loader because URLClassLoader delegation only goes towards the parent. That means classes in the JVM application class loader, which loaded your main class, cannot see the class in your new class loader. In order to work like C, you have to update the classpath of your main class, and this is not supported (it is possible, but it is not supported; I've read that Java 9 will remove this capability).
In practice, you should split your main class into two pieces (#1 and #3), and then use reflection to load/invoke the plugin-dependent class (one reflection call, and if your plugin-dependent class implements Runnable, you could use ((Runnable)loadClass("PluginDependent").newInstance()).run() to reduce even that). However, you need to ensure your URLClassLoader does not delegate the load of the plugin-dependent class, for example:
Split your application into the three discrete sets listed above (main.jar, plugin.jar, and main-plugin-dependent.jar), and list all of them on the URLClassLoader.
Change the creation of the URLClassLoader to specify an explicit null parent so that it will not delegate to the JVM application class loader, and then specify both plugin.jar and your main JAR.
Write a custom URLClassLoader that overrides loadClass to ensure that your plugin-dependent classes are loaded by that class loader rather than being delegated to the JVM application class loader.
I tested your code with a small change (used default class loader and placed the Logic in my classpath).
This works:
public class Main {
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
Class<?> clazz = parentLoader.loadClass("com.demo.Logic");
Logic logic = (Logic)clazz.newInstance();
logic.doWork();
}
}
Your problem lies in retrieving the class from the plugin.jar.
Updated:
I also tried retrieving the class from the jar with the code from the second example.
I put the compiled Logic.class in com\demo and built the jar with jar cvf plugin.jar .\com\demo\Logic.class and put in the same path as you did.
It worked without an issue as well.
Also, you don't need to set the current's thread class loader to loader.
At least not for the purposes of this example.
Your plugin.jar may actually not contain the class.
Update to:
additional explanation 1
I want change the current classloader behavior to make it recognize
the dynamic loaded class, this try is to simple and naive, can't find
other direction:
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
ClassLoader loader = new URLClassLoader(urls, parentLoader);
Thread.currentThread().setContextClassLoader(loader);
You are doing it right with child class loader.
But if you want to "change the current classloader behavior" you should read this answer
Classloaders are meant to be immutable; you shouldn't be able to
willy-nilly add classes to it at runtime.
thus the solution you came up with a child class loader.
That is why i said "you don't need to set the current's thread class loader to loader (child classloader)."

Classloader java.lang.NoClassDefFoundError?

So I have a classloader loading a class like so:
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("modules.Test");
Method method = cls.getDeclaredMethod("getModule", noparams);
Class<?> type = method.getReturnType();
if(type.newInstance() instanceof Module){
System.out.println("Accessed field with type: Module");
}
The class Module is in another jar at runtime. And the Test.class was generated within that main jar then i unarchived it, so the dependency would be there.
How can I access other dependencies from the external .class file I have loaded?
The exception:
java.lang.NoClassDefFoundError: com/xxxxxxx/xxxx/objects/Module
Caused by: java.lang.ClassNotFoundException: com.xxxxxxx.xxxx.objects.Module
I think that the problem is happening because your modules.Test class depends the Modules class, but your custom class loader can't find that class.
I think that is because you have instantiated the custom classloader incorrectly. You wrote:
ClassLoader cl = new URLClassLoader(urls);
That creates a classloader whose parent classloader is the default system classloader. But the error implies that the default classloader is not the one that knows about Modules. Try this instead:
Classloader cl = new URLClassLoader(
urls, this.getClass().getClassLoader());
This should at least give you a classloader that knows about Modules.
Note: adding the URL for the JAR containing Modules to the urls array is a non-solution. You are liable to end up loading the Modules class twice, and that is liable to lead to other problems. (The instanceof won't work, for example.)

java.lang.ClassNotFoundException: A

I was testing an example of accessing private method from another class and got an exception
public class WithoutMain
{
public static void main(String args[]) throws Exception
{
Class c = Class.forName("A");
Object o = c.newInstance();
Method m = c.getDeclaredMethod("message", null);
m.setAccessible(true);
m.invoke(o, null);
}
}
public class A {
private void message(){
System.out.println("This is a private method.");
}
}
Getting following exception
Exception in thread "main" java.lang.ClassNotFoundException: A
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
These 2 class resides in same package. Can anyone tell me why this exception shows?
You need to use the fully qualified name of a class to load it using Class.forName()
Now you may argue why? As in your case both the classes are in the same directory/package.
I would argue the other way, there is no rule defined in java that the class loader will look in the same directory first.
If you want to learn how class loading works, I would suggest you source code for the class java.lang.ClassLoader class
So when you invoke the Class.forName it uses delegation and the class loader which is assigned the job to load this class will not know the current package or any location. Hence, it needs the fully qualified class name.
One more tip, in java a fully qualified class name of a loaded class is <ClassLoader><packageName><className>. This is how the classes in JVM are uniquely identified.
Hope this helps
EDIT
Your code will work only in one condition and that is, if both the classes are in default package.
You have to provide the fully qualified name of the class, not only the simple class name.
Class c = Class.forName("<package>.A");
Try changing
Class c = Class.forName("A");
to
Class c = Class.forName("yourPackagePath.A");
the forName method does not take in account the package of the user call.
You need the FQN as per the docs:
Parameters:
className - the fully qualified name of the desired class.
The Java Class class isn't in your package, so it needs to know where the class is located otherwise it doesn't know where it is in order to load it(just like the classloader loads it from a full file path).
so
Class<? extends Object> c = Class.forName("A");
needs to be
Class<? extends Object> c = Class.forName("package.A");
where package is the full qualified name of the package so if the package is
foo.bar
it would become
Class<? extends Object> c = Class.forName("foo.bar.A");
As already stated in answers before, needs full name.
To avoid manually typing it you can simply use:
Class<?> clazz = Class.forName(A.class.getName());

Categories

Resources