# Friday, 09 February 2007
« New Snapshot | Main | DST Update »
Class Loading Architecture

As I previously blogged, 0.32 has a new class loading architecture. I promised to explain the architecture, but I accidentally deleted the blog post I wrote about it a while ago and I didn't feel like rewriting it straight away, but I still need to do it, so here it is ;-)

The Basic Idea

Every .NET assembly has its own corresponding ClassLoader instance. This is true for assemblies generated with ikvmc as well as other .NET languages. The class loader instance corresponding to an assembly is known as the assembly class loader. It does not have a parent class loader (ClassLoader.getParent() returns null), but it does do a form of delegation. When the assembly class loader is used to load a class or resource, first it will search the corresponding assembly, if that doesn't result in the requested item, the assemblies directly referenced by the assembly are searched (and if that also doesn't find the item, the IKVM.GNU.Classpath assembly is searched).

The System Class Loader

By default the system class loader will be set to the assembly class loader of the main application executable assembly. If there is no main executable assembly (e.g. the main executable is unmanaged or the AppDomain was created without one) or the java.class.path or java.ext.dirs properties are set, then a URLClassLoader that searches the classpath is used. This means that in most scenarios no dynamic Java class loading is enabled by default. This also means that you don't have to worry about your application being affected by the CLASSPATH environment variable as set on the system where the application runs (which previously could -- and did -- result in hard to diagnose problems on some systems with a weird CLASSPATH setting.)

Dynamically Loading a Class

Class.forName() just works if the class loader it uses (and, like Java, it uses the class loader that loaded the calling class) can find the class. It can also be used to load classes from assemblies that are not referenced by any class loader that is already available. This is done by specifying the assembly qualified name of the type (in .NET form, without the cli prefix). Here's an example:

class MsgBox
{
  public static void main(String[] args) throws Exception
  {
    Class c = Class.forName("System.Windows.Forms.MessageBox, " +
                "System.Windows.Forms, " +
                "Version=1.0.5000.0, " +
                "Culture=neutral, " +
                "PublicKeyToken=b77a5c561934e089");
    c.getMethod("Show", String.class).invoke(null, "Hello, World!");
  }
}

Versioning

One nice benefit of the new architecture is that it is now possible for two (or more) different versions of a jar to be loaded and used in the same AppDomain. Previously that would sort of work, but it would break down if the code used Class.forName() or tried to load resources. However, now the class loader namespaces can be fully seperated (you still can't have a single assembly that directly references two versions of the same code, but indirectly it works fine.) Here's an example of an application that uses two different assemblies that both depend on different versions of the same assembly:

The rectangles represent assemblies and the ovals represent class loaders. The pink oval is the assembly class loader for App.exe and can see classes in both Foo.dll and Bar.dll, but won't be able to see any classes from either of the Widgets assemblies. The yellow oval is the assembly class loader of Foo.dll and it allows Foo.dll to see the correct version of Widgets.dll, but not the other one (nor any classes in App.exe, unless it uses the system class loader). Each Widgets.dll assembly will also have its own assembly class loader (not shown in the diagram) and that will only be able to load classes from the corresponding Widgets assembly.

JSR 277

The limitations of the Java class loading architecture have long been known and several hacks have been developed to work around some of those limitations, but it is clearly desirable (at least to some people) to replace the current architecture with something better. JSR 277 is trying to define that new architecture. I believe that the architecture that IKVM 0.32 introduced is very similar to what JSR 277 is going to end up like, so in the future it should be possible to support JSR 277 without any major changes. Also, if your Java code requires changes to be compatible with the IKVM architecture, it almost certainly will require similar changes to deal with JSR 277 and making these changes now will make it easier to migrate to JSR 277 based modules in the future.

Friday, 09 February 2007 09:27:03 (W. Europe Standard Time, UTC+01:00)  #    Comments [3]
Friday, 09 February 2007 23:52:54 (W. Europe Standard Time, UTC+01:00)
Jeroen, thanks for providing the details. It certainly helped answer a lot of my questions. However, I still have problem with the following scenario (which I accidentally posted to a previous blog entry referenced by this one):

a.dll and b.dll contains junit test cases and were compiled referencing junit.

Now if I compile junit alone into junit.exe (with the text running as the main class and with the intention to use to to run any test case in a loaded assembly), I get a ClassNotFoundException when running junit.exe given a test case in either a.dll or b.dll.

Thus I will have to separately compile both a.dll and b.dll with junit in them. This is definitely not ideal given that junit should be able to use reflection to run what ever test cases that are available in an assembly.
Peilin Zhang
Saturday, 10 February 2007 12:12:35 (W. Europe Standard Time, UTC+01:00)
One option is to take advantage of the ability to pass an assembly qualified name to Class.forName():

ikvmc -main:org.junit.runner.JUnitCore junit-4.1.jar

ikvmc -target:library junit\samples\SimpleTest.class -r:junit-4.1.exe

junit-4.1 "junit.samples.SimpleTest, SimpleTest"

Another solution would be to write your own AllTests driver that loads an assembly and adds all tests in it to a TestSuite and pass that to JUnit. It would only be a couple of lines of code.
Jeroen Frijters
Friday, 29 June 2007 10:06:42 (W. Europe Daylight Time, UTC+02:00)
Hi there. Im trying to test an example of ikvm web concepts that tries to load oracle jdbc driver from oracle JAR converted to DLL. My code has 2 dll, oracle and my code of DB access but i can't make it work. I've make a big JAR with oracle driver and my classes and that works in .NET but i need to use it with separate dll's.

If I try to load an assembly into the application domain with a code like this my app raise an exception:
// Load the assembly into the current appdomain:
System.Reflection.Assembly newAssembly = System.Reflection.Assembly.LoadFrom(@"C:\Documents and Settings\IntentoOracle\dist\pruebaOracle.dll");

// Instantiate RemoteObject:
newAssembly.CreateInstance("pruebaOracle.ConectaConOracle");

Could you give me an idea?
Sorry about my awful english
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview