views:

189

answers:

1

Hi,

First of all, this is Java 1.4 (project restrictions). I'm trying to create a application manager. It loads each application's main class using it's own instance of a custom classloader. After that, it creates an instance of the main class using reflection. Each application implements a common interface so after the instance is created, it runs a predefined method of the application.

However, I'm having some trouble at CRASH POINT 1 (see code). The class is not recognized as one implementation of it's interface. If I coment this code chunk, I get ClassCastException at CRASH POINT 2.

I suppose both errors are related to the same issue (of course).

Can anyone help me? The relevant part of the code follows (imports are removed)...

Thanks very much.

Marcus

// AppManager.java

public class AppManager {
    public ThreadGroup threadGroup;
    private Class appClass;
    private AppInstance appInst;
    public AppContextImpl context;

    private AppManager(CustomClassLoader cl, String mainClass) throws ClassNotFoundException {
        final String className = mainClass;
        final CustomClassLoader finalLoader = cl;

        appClass = cl.loadClass(mainClass);

        // DEBUG CODE:
        Class[] k1 = AppInstance.class.getInterfaces();
        System.out.println(k1.length + " interfaces for AppInstance.class:");
        for (int ii = 0; ii < k1.length; ii++) {
            System.out.println("   " + ii + " - " + k1[ii].getName() + " (" + k1[ii].getClassLoader() + ")");
        }

        Class[] k2 = appClass.getInterfaces();
        System.out.println(k2.length + " interfaces for appClass instance:");
        for (int ii = 0; ii < k2.length; ii++) {
            System.out.println("   " + ii + " - " + k2[ii].getName() + " (" + k2[ii].getClassLoader() + ")");
        }

        // CRASH POINT 1
        if (!(AppInstance.class.isAssignableFrom(appClass))) {
            throw new IllegalArgumentException("Attempt to run a non-AppInstance class: " + appClass);
        }

        context = new AppContextImpl(mainClass, this);
        cl.setAppManager(this);
        Constructor m;
        try {
            m = appClass.getConstructor(new Class[0]);
           // CRASH POINT 2
            appInst = (AppInstance) m.newInstance(new Object[0]);
            appInst.init(context);
        } catch (Exception e) {
            System.out.println("Got ClassCastException here!\n");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        App app1;

        String path1 = "/home/user/workspace/MultiTaskTest/bin/";
        String app1Name = "App1";

        Vector v1 = new Vector();
        try {
            v1.add(new URL(path1));
        } catch (MalformedURLException e1) {
            final File file1 = new File(path1);
            try {
                URL path1aux = (URL) AccessController.doPrivileged(
                    new PrivilegedExceptionAction() {
                        public Object run() throws IOException {
                            if (!file1.exists()) {
                                System.out.println("Warning: \"" + file1.getPath() + "\" not found");
                                return null;
                            }
                        return file1.toURI().toURL();
                        }
                    });

                if (path1aux != null) {
                    v1.add(path1aux);
                }
            } catch (PrivilegedActionException e) {
                e.getException().printStackTrace();
            }
    }

        final URL[] array1 = (URL[]) v1.toArray(new URL[v1.size()]);
        CustomClassLoader cl1 = (CustomClassLoader) AccessController.doPrivileged(
            new PrivilegedAction() { public Object run() {
                return new CustomClassLoader(array1);
            }});
        System.out.println("ClassLoader 1 created: " + cl1);
        try {
            app1 = new App(cl1, app1Name);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("Cannot find class App1.");
        }
    }
}

// AppInstance.java

public interface AppInstance {
    public void init(ContextImpl context);
}

// App1.java

public class App1 implements AppInstance {
    private AppContextImpl contextObj;

    public void init(AppContextImpl context) {
        this.contextObj = context;
        System.out.println("Running App1...");
    }
}

// AppContextImpl.java

public class AppContextImpl {
    public String mainClass;
    public AppManager app;

    public AppContextImpl(String mainClass, AppManager app) {
        this.mainClass = mainClass;
        this.app = app;
    }
}

// CustomClassLoader.java

public class CustomClassLoader extends URLClassLoader {
    AppManager appInst;

    public CustomClassLoader(URL[] paths) { super(paths, null); }
    public void setAppManager(AppManager app) { this.appInst = app; }
}

The output for the Debug code in the AppManager.java file is:

0 interfaces for AppInstance.class:
1 interfaces for appClass instance:
   0 - AppInstance (CustomClassLoader@480457)
+3  A: 

Your AppInstance class is probably loaded separately by each custom classloader. Since class objects depend on the actual class AND on the classloader, they are really different classes. So AppInstance from classloader 1 is not the same as AppInstance from classloader 2.

What you need to do is using the standard classloader hierarchy: use a root classloader for your application, and male sure that AppInstance is loadable by the classloader. Then make your custom classloader children from the root. Whenever they need to access the AppInstance class, they will use what is loaded from the root.

So, instead of this:

public CustomClassLoader(URL[] paths) { super(paths, null); }

You need to give a parent to your CustomClassLoader

Guillaume
The isolation provided by separated classloaders is exactly the goal.If I set the parent classloader for them, the isolation would be broken, woundn't?I'll research a little more about classloaders to make sure I'm not misunderstanding something. I'll get back very soon.Meanwhile, thanks for your answer.
Marcus
You need to decide what part of your application will be the common ground and what part will be module specific. It seems like you're trying to reinvent OSGi, you might want to look into it ;)
Guillaume
I've checked what OSGi is. What I need to do is just that. However, I can't use OSGi because my application is based on a public defined specification that imposes some restrictions. I need to load each application using its own custom classloader. After that, I'll keep a monitoring thread in the common ground. Apps will use a special API to interact between themselves. Do you know if it is possible to solve the issues in the CRASH POINTs (1 and 2) without setting the parent classloader for each custom classloader? Thanks again.
Marcus
From what you're saying, the common API should be loaded by a single classloader, and that classloader should the parent of your modules' classloaders. Since the parent classloader won't see module classes (only the API), it won't be able to load them; they will be loaded at module level.
Guillaume
I think the same as you do. However, I could find a code example that does exactly what I want to do, I just cant find how it is doing this. The code I provided in the example is almost the same.Have a look at the code in the next comment I'm going to send.
Marcus
Click each file and click "ver" or "baixar" do view or download it.http://ginga.lavid.ufpb.br/projects/ginga-j/repository/revisions/master/show/gingaj/jvm/src/share/basis/classes/common/com/sun/xletThe rest of the files are located in here:http://ginga.lavid.ufpb.br/projects/ginga-j/repository/revisions/master/show/gingaj/jvm/src/share/basis/classes/common/javax/microedition/xlet
Marcus
If I modify this code, inserting the same DEBUG code I provided in the beginning of the question (AppManager.java file), it will print:0 javax.microedition.xlet.Xlet.class:1 interfaces for xletClass instance: 0 - javax.microedition.xlet.Xlet (null)I simply can't understand it.
Marcus
I suspect the magic has something related to security properties. I'm reading a little about that right now.
Marcus
I'm not an expert by Xlet seems to be part of the JME base classes, it is probably loaded by the bootstrap classloader. Classes from the base JRE are always loaded by the bootstrap so they can't possibly be duplicated.
Guillaume
That's exactly the catch. Xlet is a system class, so loaded by the bootstrap classloader. My apps must implement a system class. I'll manage that. Thank you very much for your help and sorry for taking too long to answer, I've been out for a day.
Marcus