Question

I am trying to understand how to access/make available a jar file using URLClassLoader.

Firstly I am loading the jar file with

    package myA;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import org.jgroups.JChannel;



public class loader {

    JChannel channel;

        String user_name=System.getProperty("user.name", "n/a");


        private void start() throws Exception {

            channel=new JChannel(); // use the default config, udp.xml

            channel.connect("ChatCluster");

        }

    public void loadMe()throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        URL classUrl;
        classUrl = new URL("file:///home/myJars/jgroups-3.4.2.Final.jar");
        URL[] classUrls = { classUrl };
        URLClassLoader ucl = new URLClassLoader(classUrls);
        Class<?> c = ucl.loadClass("org.jgroups.JChannel");
        for(Field f: c.getDeclaredFields()) {
            System.out.println("Field name=" + f.getName());
            }
        Object instance = c.newInstance();  
        //Method theMethod = c.getMethod("main");
        //theMethod.invoke(instance);
        }

     public static void main(String[] args) throws Exception {
         new loader().loadMe();
         new  loader().start();

     }
}

the printout shows the declared fields that are in jgroups-3.4.2.Final.jar, however it then throws a classnotfound error.

java -cp myA.jar myA.loader
Field name=DEFAULT_PROTOCOL_STACK
Field name=local_addr
Field name=address_generator
Field name=name
Field name=cluster_name
Field name=my_view
Field name=prot_stack
Field name=state_promise
Field name=state_transfer_supported
Field name=flush_supported
Field name=config
Field name=stats
Field name=sent_msgs
Field name=received_msgs
Field name=sent_bytes
Field name=received_bytes
Field name=probe_handler
Exception in thread "main" java.lang.NoClassDefFoundError: org/jgroups/JChannel
        at myA.loader.start(loader.java:23)
        at myA.loader.main(loader.java:45)
Caused by: java.lang.ClassNotFoundException: org.jgroups.JChannel
        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)
        ... 2 more

I don't understand why the printout shows that the class is loaded but then it is not found?

thx Art

Was it helpful?

Solution

Your code has maybe a couple of problems. First, you instantiate the loader 2 times within main, so the second instance is independent from the first one and might not be aware that the first one loaded the class file definition of JChannel.

Moreover you've defined JChannel as a member of loader before, therefore the JRE should require the class definition for it at startup - else it should not know what this field should be. I've replaced it with the class you've loaded via the URLClassLoader which you should instantiate in start().

package myA;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import org.jgroups.JChannel;

public class Loader 
{
    Class<?> clazz;
    String user_name=System.getProperty("user.name", "n/a");

    private void start() throws Exception 
    {
        if (this.clazz == null)
            throw new Exception("Channel class was not loaded properly");

        Object channel  = this.clazz.newInstance(); // use the default config, udp.xml
        Method chatCluster = this.clazz.getDeclaredMethod("connect", new Class[] { String.class });
        chatCluster.invoke(channel, "ChatCluster");
    }

    public void loadMe() throws Exception
    {
        URL classUrl;
        classUrl = new URL("file:///home/myJars/jgroups-3.4.2.Final.jar");
        URL[] classUrls = { classUrl };
        URLClassLoader ucl = new URLClassLoader(classUrls);
        Class<?> c = ucl.loadClass("org.jgroups.JChannel");
        for(Field f: c.getDeclaredFields()) 
        {
            System.out.println("Field name=" + f.getName());
        }
        this.clazz = c;
        Object instance = c.newInstance();  
        //Method theMethod = c.getMethod("main");
        //theMethod.invoke(instance);
     }

     public static void main(String[] args) throws Exception 
     {
         Loader loader = new Loader();
         loader.loadMe();
         loader.start();
     }
}

You should further add some error handling to the code.

OTHER TIPS

Exception in thread "main" java.lang.NoClassDefFoundError: org/jgroups/JChannel
    at myA.loader.start(loader.java:23)

The code fails on this line:

       channel=new JChannel(); // use the default config, udp.xml

The type JChannel is not visible to loader's ClassLoader. This will be obvious if you try:

       loader.class
             .getClassLoader()
             .loadClass("org.jgroups.JChannel");

You should not have any compile-time references to a dependency that will not be on the type's classpath at runtime.

Loading with a new child ClassLoader does not add that type to some global class pool. The loaders are hierarchical with child-parent relationships.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top