1.2. Conflicting Classes
Overview
One of the main aims of the OSGi class loading architecture is to avoid loading conflicting class definitions into the same bundle class space. This section explains how such conflicts can arise in practice.
Symbolic references
To explain the difficulties that can be caused by having multiple copies of a loaded class, we need the concept of a symbolic reference to a class or interface. A symbolic reference is a point in your code where a class name is used, except the actual definition of the class. For example, when a class name is used to specify a return type, a parameter type, or a variable type, the class name is a symbolic reference.
At run time, the JVM must resolve each symbolic reference by linking it to a specific instance of a loaded class. Symbolic references are resolved using the same class loader as the class in which they appear. For example, Example 1.1, “Symbolic Reference Used in a Class Cast” shows the definition of the
TestHello
class, which is loaded by the class loader, A
.
Example 1.1. Symbolic Reference Used in a Class Cast
// Java package org.bar; // TestHello loaded by ClassLoader 'A' public class TestHello { public void useHelloObj(Object hello) { org.foo.Hello h = (org.foo.Hello) hello; System.out.println(h.getGreeting()); } }
Important
Although the symbolic reference,
org.foo.Hello
, is initially loaded by class loader A
, this does not imply that the symbolic references must be resolved to A/org.foo.Hello
. If the initial class loader A
decides to delegate to another class loader, B
, the symbolic reference could be resolved to B/org.foo.Hello
instead (so that B
is the effective class loader). The delegation mechanism is crucial, because it enables an OSGi bundle to re-use existing loaded classes and avoid class space inconsistencies.
Class cast exceptions
When multiple class loaders are used in parallel (as happens in OSGi), there is a danger that a class could be loaded more than once. This is undesirable, because it almost inevitably causes class cast exceptions at run time.
For example, consider the
TestHello
class shown in Example 1.1, “Symbolic Reference Used in a Class Cast”, where the org.foo.Hello
symbolic reference has been resolved to A/org.foo.Hello
. If you also have a Hello
object that is an instance of B/org.foo.Hello
type, you will get a class cast exception when you pass this object to the TestHello.useHelloObj(Object)
method. Specifically, in the line of code that performs the following cast:
org.foo.Hello h = (org.foo.Hello) hello;
The
org.foo.Hello
symbolic reference has been resolved to the A/org.foo.Hello
type, but the hello
object is of B/org.foo.Hello
type. Because the types do not match, you get the class cast exception.