Zevin Zenph Zambori
Zevin Zenph Zambori

Reputation: 323

Keep getting NoClassDefFoundError while loading a class with URLClassLoader

Recently I'm creating something that have to load/unload external jar packages dynamically. I'm now trying to do this with URLClassLoader, but I keep getting NoClassDefFoundError while trying to make new instances.

It seems that the external class is loaded successfully since the codes in the constructor are executed, but ClassNotFoundException and NoClassDefFoundError still keep being thrown.

I made an small package that recreates the error and here are the codes:

The codes below are in ExternalObject.class ,which is put in a .jar file, that I'm trying to load dynamically:

package test.outside;

import test.inside.InternalObject;

public class ExternalObject
{
    private final String str;

    public ExternalObject()
    {
        this.str = "Creating an ExternalObject with nothing.";
        this.print();
    }

    public ExternalObject(InternalObject inObj)
    {
        this.str = inObj.getString();
        this.print();
    }

    public void print()
    {
        System.out.println(this.str);
    }
}

And the codes below are in InternalObject.class:

package test.inside;

public class InternalObject
{
    private final String str;

    public InternalObject(String s)
    {
        this.str = s;
    }

    public String getString()
    {
        return this.str;
    }
}

I tested the file with Main.class below:

package test.inside;

import java.io.File;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;

import test.outside.ExternalObject;

public class Main
{
    public static void main(String[] args)
    {
        try
        {
            File externalJar = new File("F:\\Dev\\ext.jar");
            URLClassLoader uclTest = new URLClassLoader(new URL[]{externalJar.toURI().toURL()});
            Class<?> clazz = uclTest.loadClass("test.outside.ExternalObject");

            InternalObject inObj = new InternalObject("Creating an ExternalObject with an InternalObject.");

            try
            {
                System.out.println("Test 1: Attempt to create an instance of the ExternalObject.class with an InternalObject in the constructor.");
                Constructor<?> conTest = clazz.getConstructor(InternalObject.class);
                ExternalObject extObj = (ExternalObject)conTest.newInstance(inObj);
            }
            catch(Throwable t)
            {
                System.out.println("Test 1 has failed. :(");
                t.printStackTrace();
            }

            System.out.println();

            try
            {
                System.out.println("Test 2: Attempt to create an instance of the ExternalObject.class with a void constructor.");
                Constructor<?> conTest = clazz.getConstructor();
                ExternalObject extObj = (ExternalObject)conTest.newInstance();
            }
            catch(Throwable t)
            {
                System.out.println("Test 2 has failed. :(");
                t.printStackTrace();
            }

            uclTest.close();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

Both InternalObject.class and Main.class are in a jar pack which is included in the classpath while launched. And I got this in the console: Console output screenshot

As the codes this.print() in both constructors of ExternalObject.class are executed, I have really no idea what's wrong. Help! :(

UPDATE: Thank you wero!!! But I actually want to make an instance of ExternalObject for further usage such as accessing methods in it from other classes. Is there any way that I can return the created instance as an ExternalObject? Or I have to use getMethod() and invoke() to access the methods?

Sincerely, Zevin

Upvotes: 2

Views: 1382

Answers (4)

Zevin Zenph Zambori
Zevin Zenph Zambori

Reputation: 323

Hooray!! I just found a better way for my codes! What I did is creating an abstract class ExternalBase.class with all abstract methods I need, then inherit ExternalObject.class from ExternalBase.class. Therefore dynamically loaded class have to be neither loaded into the custom loader nor imported by the classes that use the object, and the codes work totally perfect for me. :)

Upvotes: 1

Ravindra Devadiga
Ravindra Devadiga

Reputation: 692

How you are running the Main class is causing the problem.

As you said, I have created jar called ext1.jar with ExternalObject and InternalObjct class files inside it. And created ext.jar with Main and InternalObject class files.

If I run the following command, it throws Exception as you mentioned

java -classpath .;C: \path\to\ext.jar test.inside.Main

But, If I run the following command, it runs fine without any Exception

java -classpath .;C: \path\to\ext1.jar;C: \path\to\ext.jar test.inside.Main

Upvotes: 0

wero
wero

Reputation: 32980

Your Main class references ExternalObject and therefore the compiled Main.class has a dependency on ExternalObject.

Now when you run Main and ExternalObject is only available in ext.jar but not in the classpath used to run Main the following happens:

The uclTest classloader successfully loads ExternalObject from ext.jar. Also creation succeeds (seen by the print statement in the constructor).

But what fails are the assignments to local variables ExternalObject extObj. Main cannot use the loaded class ExternalObject since it is loaded by a different classloader. There is also no ExternalObject in the classpath of Main and you get a NoClassDefFoundError.

Your test should run without problems when you remove the two assignments ExternalObject extObj = (ExternalObject).

Upvotes: 2

Adrian Duta
Adrian Duta

Reputation: 81

I think because there are two classLoaders involved, and you try to cast an object from a classLoader to an object from another class loader. But is just a guess.

Upvotes: 1

Related Questions