Thursday, July 19, 2007

Why do Class.forName and ClassLoader.loadClass behave different?

ClassLoader.loadClass or Class.forName seem to be synonyms for the same basic operation: request a dynamic class load. Yet calling Class.forName does additional "checking" which is not very useful (certainly in OSGi).

When doing a dynamic class load, the returned type has no implied type by the code. The code must use reflection to access static members or to create an instance. A created instance can either be reflected upon or cast to a type which must already implicitly know by the code. This cast will result in a runtime type check which will ensure type safety.

This is very different than an implicit class load done by the VM to resolve a class constant pool entry. Since it is the goal of these implicit class loads to avoid runtime type checks, the loader constraints are used to ensure type safety.

It does not seem necessary or reasonable to impose loader constraint checks on some dynamic class load requests. That is, code calling Class.forName (or ClassLoader.loadClass) and the VM resolving a class constant pool entry have different type safety needs. The former does not require loader constraint checks since that will be done at runtime by a type cast if needed. The latter does require loader constraint checks to avoid the need for runtime type checks.

So it does seem reasonable to have Class.forName behavior altered to avoid loader constraint checks. Only internal VM class load requests need to check loader constraints.

[2013-10-14 - Updated links to fix link rot from Oracle acquisition of Sun.]

Wednesday, July 18, 2007

ContextFinder in Eclipse is broken

[Updated (19 July 2007): Further discussion with Glyn Normington and Tom Watson indicates it is the Class.forName(...,TCCL) form of loading a class from the TCCL that presents the issues since it triggers the class loading constraints while the ClassLoader.loadClass form does not.]

I was doing some reading and thinking about the ThreadContextClassLoader (TCCL) issue in OSGi environments. Many libraries use the TCCL to load classes. The real problem comes when the library ONLY uses Class.forName(...,TCCL) to load a class and doesn't try it's own class loader first or TCCL.loadClass.

Eclipse created the ContextFinder to try and address the TCCL issue in OSGi. It's goal is to find a bundle's class loader on the call stack and then delegate to that class loader to handle the load request. The normal OSGi bundle class loader rules as well as a further Buddy Policy extension is then used to find the requested class.

However, it turns out there are problems with this approach when Class.forName(...,TCCL) is used. One of which is class loader constraint violation. The other is inadvertent pinning of classes in memory as they are added to the constraint table. After reading a classloader paper (from 1998!), I think the ContextFinder model (that is a single, framework-wide shared TCCL) used by Eclipse is fatally flawed. It is virtually guaranteed to violate class loading constraints in the face of multiple package versions. Not to mention the fact that it can pin classes and class loaders in memory for as long as the ContextFinder is reachable.

Specifying a way to handle TCCL in OSGi may not be possible. Given 3 bundles: A, B, C having two versions of each: A1, A2, B1, B2, C1, C2. Imagine C1 imports a package from B1 and B1 imports a package from A1. Further C2 imports a package from B2 and B2 imports a package from A2. A type from A1 does not appear in the signature of B1 and a type from A2 does not appear in the signature of B2. Thus via B1, C1 cannot "see" A1 and via B2, C2 cannot "see" A2. Also imagine that C1 import the package from A2 used by B2 and C2 imports the package from A1 used by B1. We now have the wiring depicted below:

C1 C2
^^ ^^
| \ / |
| \ / |
B1 \/ B2
^ /\ ^
| / \ |
| / \ |
A1 A2

This is entirely possible and works fine in normal (non-TCCL) class loading. C1 is not exposed to A1 via B1, so it's use of the package from A2 results in no conflict. If C1 loads a class from B1 which results in the load of a class from A1, then B1 is the initiating class loader of the request to A1. When C1 loads a class from A2, C1 is the initiating class loader. So C1 is never the initiating class loader for loads from both A1 and A2.

However, when we have a single, shared TCCL and bundle D calls Class.forName(...,TCCL) to load a class, the shared TCCL will be the initiating class loader for loads from both A1 and A2 (or B1 and B2, or C1 and C2) depending upon whatever context is used to select the defining class loader. C1 or C2 may have caused D to request the class load. Since the TCCL is the initiating class loader, any class load initiated by it for some class P must always return the same class object. In the presence of multiple versions (and even without multiple versions if two bundles are unlucky enough to choose the same fully qualified class name), loader constraints will eventually be violated.

Thus the Eclipse ContextFinder model is broken and we obviously should not spec it in OSGi. However, I am currently at a loss for a reliable solution to the TCCL problem for OSGi. We can certainly recommend TCCL.loadClass is used instead of Class.forName(...,TCCL) but there is a large body of code already out there which already uses the latter form.

Tuesday, July 03, 2007

Eclipse Europa on Java 1.4

Like half the rest of the programming universe, I downloaded Eclipse Europa yesterday to update my development environment with the latest and greatest released version of the Eclipse IDE. I spent a few moments figuring out what kind of developer I was given the choices offered. Since I develop OSGi code, I figured RCP/Plug-in developer was the best choice to start with. It would have been nice if OSGi bundle development was mentioned somewhere in there :-)

In any case, the "installation" (unzip is my favorite installer!) went fine. Since I deal with a few XML files, I like WST for the XML editors, etc. it provides. However, when I started Eclipse, even though WST is included in the RCP/Plug-in developer download, none of the WST stuff was functional! After some time trying to figure out what was up, I eventually figured out that it was because the default JRE on my system is 1.4! (Yes, I know it is rather retro, but I still use it as the default. I have Java 5 installed which I use when I need it.) WST just silently does not work when you start Eclipse with 1.4. No warnings or errors in the log view. I guess the WST bundles simply don't resolve because they probably require the Java 5 execution environment. Starting Eclipse with Java 5 solved the problem (and even opened the Welcome screen which I realized did not show up when I started with 1.4!).

I eventually found the Eclipse JRE page which states that the Java EE developer version requires Java 5 but I picked the RCP developer version. And while the RCP version started on 1.4, some significant function was not operational. Perhaps the website should just state that Java 5 is required for all downloads or provide some warning to the user that on Java 1.4 functions X, Y and Z don't work in Eclipse 3.3.

2007 OSGi Community Event was a Success

Last week I was in Munich, Germany for the 2007 OSGi Community Event as well as a face 2 face OSGi Enterprise Expert Group meeting.

The OSGi Alliance had held congresses in the past (2005 and prior) but did not hold one in 2006. This year we tried for a smaller and more intimate event. I think it was a great meeting. 100+ people over the span of two days heard many interest presentations on OSGi technology. Some sessions were more business oriented and some more technical. It was a pretty decent blend. You can view the presentations online.

Overall the Community Event was an excellent complement to the OSGi sessions at EclipseCon and JavaOne each year. I am sure the OSGi Alliance will have another Community Event next year and I hope to see you there!