2

I try to create a jar, use it for compiling but do not ship the jar. I expect the user to provide the jar and pointing my application to this jar.

Imagine the following:

This is my source tree:

main
  Main.java
test
  Test.java

I create two jars:

main.jar
  main
    Main.class
test.jar
  test
    Test.class

My source looks like this:

Main.java

package main;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;

import test.Test;

public class Main {
    public static void main(String[] args) throws Exception {
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[] { new File("test.jar").toURI().toURL() }, null);
        Thread.currentThread().setContextClassLoader(urlClassLoader);
        Test.test();
    }
}

Test.java

package test;

public class Test {
    public static void test() {
        System.out.println("Hello world!");
    }
}

I call the main.jar but without test.jar on the classpath. I would expect the URLClassLoader to load the test.jar and the program to print Hello world!.

But it seems not to use my URLClassLoader but the system classloader instead. I get:

Exception in thread "main" java.lang.NoClassDefFoundError: test/Test
    at main.Main.main(Main.java:14)
Caused by: java.lang.ClassNotFoundException: test.Test
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    ... 1 more

What is my mistake? How to do it right?

Arne Deutsch
  • 14,629
  • 5
  • 53
  • 72
  • 1
    `NoClassDefFoundError` has absolutely nothing to do w/ `Thread.contextClassLoader`. When loading a class by the VM it uses the current class' classloader – bestsss Aug 05 '11 at 16:20
  • 1
    so, to solver the issue load class Test by `Class.forName("...Test", true, classloader)` and call test() via reflection. Then you will get more or less what you need. – bestsss Aug 05 '11 at 16:23
  • Yes, I'm aware that I can do it by reflection. But what I try to achieve is NOT to use reflection. The hint that the contextClassLoader is not used here is good ... but question remains open ... still not sure how to achieve the effect. – Arne Deutsch Aug 05 '11 at 18:26
  • 1
    It seems you miss how the class loading works. You need some form of NON compiled code to make the system go. Be it Class.newInstance() (which is also reflection), invoking smth like `main` method or so. Otherwise there is no way to change the context loader (not thread, current class context). For instance if that makes it easier for you: implement Runnable in class Test and do. ((Runnable) Class.forName(...).newInstance()).run(). and bootstrap your code off there. – bestsss Aug 05 '11 at 18:54
  • Have you found the answer? I mean, is this code works for now? – Ahmet Karakaya Dec 27 '12 at 12:46
  • I am trying to achieve the same thing by defining a custom class loader, however my class loader's findClass never being called. Still not sure what's wrong or maybe I misunderstood how the thread context class loader works? I would expect to use the specified class loader to load class in the thread if the class has not been loaded yet. – LiuWenbin_NO. May 23 '19 at 05:13
  • @bestsss if still use reflection in the thread, why not just create a new classloader instance inside the `run()` and use it directly there? What's the difference between this approach and `setContextClassLoader()`? – LiuWenbin_NO. May 23 '19 at 05:20

1 Answers1

0

For those looking for an answer have a look at How do you change the CLASSPATH within Java? for a workaround and more explanations

Community
  • 1
  • 1
NeeL
  • 720
  • 6
  • 20