dynamic loading class error NoClassDefFoundError - java

I'm writing program witch loads .class files in run time and calls main method. Code below.
File classDir = new File(pathToClass);
URL pathTo = classDir.toURL();
URL[] urls = new URL[]{pathTo};
URLClassLoader cl = new URLClassLoader(urls);
_class = cl.loadClass(className);
Method m = _class.getMethod("main", String[].class);
It builds and in execution I get this error :
java.lang.NoClassDefFoundError: [LComplex;
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2693)
at java.lang.Class.privateGetMethodRecursive(Class.java:3040)
at java.lang.Class.getMethod0(Class.java:3010)
at java.lang.Class.getMethod(Class.java:1776)
What I'm doing wrong
Files are placed in this pattern:
C:/dir/a/ccc.class
C:/dir/a/ccc.java
C:/dir/b/ccc.class
C:/dir/b/ccc.java

Like isnot2bad told, Complex class was missing. After loading it error vanished

Related

javassist throws ClassNotFoundException when loading external classes from jar

I'm trying load an external jar file with javassist & call its main method at runtime, however when i try to do this with the below code:
File file = new File("C:\\Users\\MainPC\\Desktop\\test.jar");
ClassPool cp = ClassPool.getDefault();
cp.insertClassPath(file.getAbsolutePath());
Class<?> MainClass = cp.get("TestPackage.MainClass").toClass();
MainClass.getMethod("main", String[].class).invoke(null, new Object[] {args});
It throws the following exception:
Exception in thread "main" java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at ReflectionTests.main(ReflectionTests.java:99)
Caused by: java.lang.NoClassDefFoundError: TestPackage/OtherClass
at TestPackage.MainClass.main(Unknown Source)
... 5 more
Caused by: java.lang.ClassNotFoundException: TestPackage.OtherClass
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
... 6 more
When i attempt the same thing only using the built in java reflection api it works with no problems:
File file = new File("C:\\Users\\MainPC\\Desktop\\test.jar");
URLClassLoader cl = new URLClassLoader(new URL[] {new URL("jar:file:"+file.getAbsoluteFile()+"!/")});
Class<?> clazz = cl.loadClass("TestPackage.MainClass");
clazz.getMethod("main", String[].class).invoke(null, new Object[] {args});
(the above throws no exceptions & calls the main method of the jar file as expected)
This leaves me to believe i'm doing something wrong in javassist (specifically with the loading of the jar file classes). Can someone explain to me what it is?
I should mention the jar file only contains 2 classes: MainClass.class & OtherClass.Class. Both reside in a package called TestPackage. It seems the error has something to do with the MainClass class not being able to find the OtherClass class, when javassist loads it.
The problem was that when i call ct.toClass() it only exposes the class itself to my runtimes classloader (not the entire classpool's classpath). When i then later attempt to invoke the main method of this class, my runtimes classloader tries to execute the part that loads the other class which it obviously doesn't know about and so throws a ClassNotFoundException.
The solution is to use the javassist provided classloader (javassist.Loader) which takes a classpool as argument in the constructor and then is able to load & resolve classes from the classpools classpath properly.
Here's a working code example of what i was trying to achieve:
File file = new File("C:\\Users\\MainPC\\Desktop\\test.jar");
ClassPool cp = ClassPool.getDefault();
cp.insertClassPath(file.getAbsolutePath());
Loader loader = new Loader(cp);
Class<?> MainClass = loader.loadClass("TestPackage.MainClass");
MainClass.getMethod("main", String[].class).invoke(null, new Object[] {args});

WILDFLY - Loading jar dynamically java.lang.ClassNotFoundException

I have an EAR deployed in wildfly and I'm loading a jar from source with this code:
File file = new File("C:\\XXXX\\XXXX\\ProcessTest.jar");
String lcStr = "com.package.test.TestProcess";
URLClassLoader cl = URLClassLoader.newInstance(new URL[]{file.toURL()});
Class<?> loadedClass;
try {
loadedClass = cl.loadClass(lcStr);
IProcess data = (IProcess)loadedClass.newInstance();
data.start();
} catch (Exception e) {
e.printStackTrace();
}
The TestProcess class implements IProcess that is loaded in another jar with the EAR.
When I run the server code and the class is being casted I receive:
java.lang.NoClassDefFoundError: com/package/test/process/IProcess
If I added the interface in the JAR that I'm loading the problem is a CastException because com/package/test/process/IProcess loaded by wildfly is different of the loaded with the JAR.
I need receive the IProcess (casting the object), because a solution is call directly the method with Mehtod.invoke but it's not solution for my problem.
Thanks in advanced.
With this tip works perfectly:
URLClassLoader.newInstance(new URL[]{file.toURL()}, IProcess.class.getClassLoader())
Thanks to Steve C!

How to run a java class from a jar file dynamically

I'm working on a java project that needs a third-party java program running as a server to work.
Normally, I'd do:
java -cp jarfile1.jar:jarfile2.jar className arg1 arg2
And then I'd run my java code. This way it works.
I'd like to know if there is any way to, including the two .jars required into my project, run the class directly from my code instead of having to manually start it.
I've tried to use URLClassLoader as I saw in some examples, but either I'm doing it wrong or none cover this specific use case.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{new URL("file:///tmp/jarfile1.jar"),new URL("file:///tmp/jarfile2.jar")});
Class<?> cls = classLoader.loadClass("className");
Method method = cls.getDeclaredMethod ("main");
Object instance = cls.newInstance();
Object result = method.invoke (instance);
yields
Exception in thread "main" java.lang.NoClassDefFoundError: alice/tuprolog/lib/InvalidObjectIdException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2615)
at java.lang.Class.getDeclaredMethod(Class.java:2007)
at pkg1.MainClass.main(MainClass.java:54)
Caused by: java.lang.ClassNotFoundException: alice.tuprolog.lib.InvalidObjectIdException
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)
... 4 more
Please note that I copied the .jars to /tmp to isolate the failure cause. The files exist and are accessible.
How can I make that run the class as specified above within java code?
Thanks!
If the class exists in a different ClassLoader, you need to use reflection to get to it:
ClassLoader classLoader = new URLClassLoader(
new URL[] { firstJarURL, secondJarURL });
String[] args = { arg1, arg2 };
try {
Class<?> mainClass = classLoader.loadClass("com.somepackage.ClassName");
mainClass.getMethod("main", String[].class).invoke(null, args);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
I finally fixed it! Things done:
I forgot to add the second jar to the classpath of the project (duh!)
Since everything was on the project classpath, I just reused the current classloader
Final working code:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> cls = classLoader.loadClass("className");
Method method = cls.getDeclaredMethod("main", String[].class);
Object instance = cls.newInstance();
Object result = method.invoke(null, (Object)args);
Thanks everyone and specially to VGR and Joop Eggen in the comments for pointing out the error with the second jar!
EDIT: As JB Nizet pointed out in the comments, calling the class's main() method directly is simpler:
className.main(args);
And you're done

load class from another class within same jar in java web start fails

I've a javafx application which is run by web start. In my fx application, I try to load the classes using ClassLoader as in below code. The parameter passed is a package name like "com.example.project.abcd"
public final static List<Class<?>> find(final String scannedPackage)
{
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final String scannedPath = scannedPackage.replace(DOT, SLASH);
final Enumeration<URL> resources;
try {
resources = classLoader.getResources(scannedPath);
} catch (IOException e) {
throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage), e);
}
final List<Class<?>> classes = new LinkedList<Class<?>>();
while (resources.hasMoreElements()) {
final File file = new File(resources.nextElement().getFile());
classes.addAll(find(file, scannedPackage));
}
return classes;
}
Now I'm not able to get all the classes present inside "com.example.project.abcd" package when I run it thru java web start but through IDE it is working fine.
I'm using JDK 7, JavaFX 2.
As per http://docs.oracle.com/javase/7/docs/technotes/guides/javaws/developersguide/faq.html#s211 Thread.currentThread().getContextClassLoader() should work fine but it is not!.
Tried searching on net/googling but in vain. Checked http://lopica.sourceforge.net/faq.html#customcl as well and tried using URLClassLoader as suggested. But that didn't work as well (Though did not know what should be passed to the parameter 'urls')
Any help is much apreciated.
I think this works in IDE because your BIN/classes directory is used to get all the files.
In Webstart-Mode, all your classes are inside JARs.

How to load a jar file at runtime [duplicate]

This question already has answers here:
How to load JAR files dynamically at Runtime?
(20 answers)
Closed 7 years ago.
I was asked to build a java system that will have the ability to load new code (expansions) while running.
How do I re-load a jar file while my code is running? or how do I load a new jar?
Obviously, since constant up-time is important, I'd like to add the ability to re-load existing classes while at it (if it does not complicate things too much).
What are the things I should look out for?
(think of it as two different questions - one regarding reloading classes at runtime, the other regarding adding new classes).
Reloading existing classes with existing data is likely to break things.
You can load new code into new class loaders relatively easily:
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { yourURL },
getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<? extends Runnable> runClass = clazz.asSubclass(Runnable.class);
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
doRun.run();
Class loaders no longer used can be garbage collected (unless there is a memory leak, as is often the case with using ThreadLocal, JDBC drivers, java.beans, etc).
If you want to keep the object data, then I suggest a persistence mechanism such as Serialisation, or whatever you are used to.
Of course debugging systems can do fancier things, but are more hacky and less reliable.
It is possible to add new classes into a class loader. For instance, using URLClassLoader.addURL. However, if a class fails to load (because, say, you haven't added it), then it will never load in that class loader instance.
This works for me:
File file = new File("c:\\myjar.jar");
URL url = file.toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("com.mypackage.myclass");
I was asked to build a java system that will have the ability to load new code while running
You might want to base your system on OSGi (or at least take a lot at it), which was made for exactly this situation.
Messing with classloaders is really tricky business, mostly because of how class visibility works, and you do not want to run into hard-to-debug problems later on. For example, Class.forName(), which is widely used in many libraries does not work too well on a fragmented classloader space.
I googled a bit, and found this code here:
File file = getJarFileToLoadFrom();
String lcStr = getNameOfClassToLoad();
URL jarfile = new URL("jar", "","file:" + file.getAbsolutePath()+"!/");
URLClassLoader cl = URLClassLoader.newInstance(new URL[] {jarfile });
Class loadedClass = cl.loadClass(lcStr);
Can anyone share opinions/comments/answers regarding this approach?
Use org.openide.util.Lookup and ClassLoader to dynamically load the Jar plugin, as shown here.
public LoadEngine() {
Lookup ocrengineLookup;
Collection<OCREngine> ocrengines;
Template ocrengineTemplate;
Result ocrengineResults;
try {
//ocrengineLookup = Lookup.getDefault(); this only load OCREngine in classpath of application
ocrengineLookup = Lookups.metaInfServices(getClassLoaderForExtraModule());//this load the OCREngine in the extra module as well
ocrengineTemplate = new Template(OCREngine.class);
ocrengineResults = ocrengineLookup.lookup(ocrengineTemplate);
ocrengines = ocrengineResults.allInstances();//all OCREngines must implement the defined interface in OCREngine. Reference to guideline of implement org.openide.util.Lookup for more information
} catch (Exception ex) {
}
}
public ClassLoader getClassLoaderForExtraModule() throws IOException {
List<URL> urls = new ArrayList<URL>(5);
//foreach( filepath: external file *.JAR) with each external file *.JAR, do as follows
File jar = new File(filepath);
JarFile jf = new JarFile(jar);
urls.add(jar.toURI().toURL());
Manifest mf = jf.getManifest(); // If the jar has a class-path in it's manifest add it's entries
if (mf
!= null) {
String cp =
mf.getMainAttributes().getValue("class-path");
if (cp
!= null) {
for (String cpe : cp.split("\\s+")) {
File lib =
new File(jar.getParentFile(), cpe);
urls.add(lib.toURI().toURL());
}
}
}
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (urls.size() > 0) {
cl = new URLClassLoader(urls.toArray(new URL[urls.size()]), ClassLoader.getSystemClassLoader());
}
return cl;
}

Categories