Red Hat Training

A Red Hat training course is available for Red Hat Fuse

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.