1

My application uses the Standard Widget Toolkit (SWT) for it's GUI. My problem is that the 32-bit SWT library does not work on a 64-bit JVM. But I don't want to make people select the correct architecture when getting the software. So, I want to bundle both the 32-bit and 64-bit libraries, and auto-detect the architecture during runtime. I found out I can get the correct architecture of the JVM like so:

if (System.getProperty("os.arch").contains("64")) {
    // ...
}

Now all that's left is to load the jar. But the problem is, all the examples I found require that you manually load the class before using it.

Class.forName("MyClass", false, myClassLoader);

So my question is, is it possible to "register" my class loader, so that I don't have to load classes beforehand?


Update: I created my own child class of URLClassLoader and set it as the default class loader with the command line argument -Djava.system.class.loader; but I get this error:

Error occurred during initialization of VM
java.lang.Error: java.lang.NoSuchMethodException: com.program.LibraryLoader.<init>(java.lang.ClassLoader)
    at java.lang.ClassLoader.initSystemClassLoader(Unknown Source)
    at java.lang.ClassLoader.getSystemClassLoader(Unknown Source)

I think LibraryLoader.<init> refers to the constructor... but it's there (public LibraryLoader(URI[] urls)).


Update 2: Almost there, the JVM runs now. I added this constructor to make it work:

public LibraryLoader(ClassLoader classLoader) {
    super(new URL[0], classLoader);
}

But after adding the jars with addPath() (file:lib/jars/swt.jar), it only produces a NoClassDefFoundError. Yes, I double-checked that the file exists.

Jonah
  • 9,991
  • 5
  • 45
  • 79

2 Answers2

1

You could try to inject your custom class loader by means of the "java.system.class.loader" property (see ClassLoader#getSystemClassLoader). However, I'd recommend to use OSGi and let the framework do the complicated stuff.

Sebastian Zarnekow
  • 6,609
  • 20
  • 23
  • If I inject it with `java.system.class.loader`, are other classes still loaded with the default `ClassLoader`? – Jonah Apr 26 '11 at 20:36
  • You'll have to make sure that your own class loader delegates to its parent for any non-swt class. – Sebastian Zarnekow Apr 26 '11 at 20:44
  • Alright, I found some more information here: http://www.coderanch.com/t/384068/java/java/Adding-JAR-file-Classpath-at . How do I set that property (`java.system.class.loader`) before the program starts? – Jonah Apr 26 '11 at 20:49
  • You can set a system property by adding the following command line argument: -Djava.system.class.loader=com.mycompany.MyClassLoader . Note that this argument has to be set before your own program arguments (if any) but after the Xmx/Xms stuff. – Sebastian Zarnekow Apr 26 '11 at 20:59
  • Can I set it in the MANIFEST? If so, how? – Jonah Apr 26 '11 at 21:13
  • I'm afraid this is not possible. – Sebastian Zarnekow Apr 26 '11 at 21:24
  • @Sebastian: thanks a lot for the help so far, although I've encountered another error: see the update on my question. – Jonah Apr 26 '11 at 22:01
  • You'll have to provide a constructor without any arguments (a default constructor). The mechanism cannot know which URLs to pass into the constructor that takes parameters. – Sebastian Zarnekow Apr 26 '11 at 22:49
  • @Sebastian: hm, I added a default constructor, and a compile error popped up that it had to call another constructor. So I set it to call `super(new URL[0])`. But the `NoSuchMethodException` error persists at runtime. – Jonah Apr 26 '11 at 23:05
  • I'm sorry, according to the docs you'll have to define a constructor that takes another ClassLoader as it's only argument. I misread the message of your NoSuchMethodException. I think you'll have to pass the parent classloader to the super constructor. – Sebastian Zarnekow Apr 26 '11 at 23:09
  • @Sebastian: It seems that there is no `URLClassLoader(ClassLoader)` defined. But there is a `URLClassLoader(URL[], ClassLoader)`, and I've created it. – Jonah Apr 26 '11 at 23:26
  • @Sebastian: you rock man! It almost works, the JVM starts now. The only issue is, it doesn't seem to be loading the jars I tell it to (via `addPath()`). I give it `file:lib/jars/swt.jar`, but it gives me a `NoClassDefFoundError` exception (that's the relative path from the program jar to the library). – Jonah Apr 27 '11 at 01:44
  • @Sebastian: I tried explicitly loading the class with `Class.forName("org.eclipse.swt.graphics.Point", true, myLoader);`, but it didn't improve things. – Jonah Apr 27 '11 at 18:48
  • Can you confirm that the swt.jar file contains the respective class files? Did you try to use an absolute url to point to the swt.jar? – Sebastian Zarnekow Apr 27 '11 at 19:01
  • @Sebastian: yes, I even tried just pointing to the class file by itself. Since this strays somewhat from the original question, I made a new one here: http://stackoverflow.com/questions/5809139 – Jonah Apr 27 '11 at 19:02
0

As part of the constructor for your custom ClassLoader, call definePackage with the appropriate information, with the URL pointing to the desired jar file.

This example shows that the custom class loader is called when I try to instantiate a class from swing, because I defined my class loader as the loader of that package.

import java.net.URL;

public class junk extends ClassLoader {

  byte[] dummy = new byte[0];

  public static void main(String[] args) throws Exception {
    new junk();

    new javax.swing.JPanel();

  }

  public junk() throws Exception {
    definePackage("javax.swing","","","","","","",new URL("file://junk.class"));
  }

  public Class<?> findClass(String s) throws java.lang.ClassNotFoundException{
    Class<?> retVal = super.findClass(s);

    System.out.println("delegated responsibility for "+s+" to superclass");

    return retVal;
  }

  public Package getPackage(String s) {
    Package retVal = super.getPackage(s);

    System.out.println("delegated responsibility for "+s+" to superclass");

    return retVal;
  }

}

Result:

delegated responsibility for javax.swing to superclass

toadaly
  • 647
  • 3
  • 6