For example, it would be possible to access an object's private data using a spoofed version of a class with the private data declared public. Vijay Saraswat found just such a type-safety bug in an early version of Java which was fixed by the introduction of 'loading constraints'.
So now that Java is type-safe, why need OSGi be concerned? Well, although OSGi can't break Java's type-safety, its support for versioning increases the risk that exceptions, such as ClassCastException, will be thrown to preserve type-safety. This usually occurs where two loaded versions of a particular class come into 'contact'.
The most common case is where an attempt is made to cast an object of one version of a class to another version of the same class. There isn't necessarily anything malicious going on - it's simply that two legitimate versions of a class loaded in the same JVM have accidentally come into contact.
OSGi increases the risk of such exceptions because it supports versioning of bundles and their contents: two (or more) versions of a given bundle can run in a JVM at the same time. These bundles will typically export similar classes. Since each OSGi bundle has its own class loader and a class is uniquely identified at runtime by the combination of its fully qualified class name and the class loader instance that defined the class, there can be two versions of each exported class present in the JVM at the same time.
So, to minimise these problems, OSGi works hard to wire together bundles so that they will consistently use a particular version of each class. At a basic level, when OSGi is resolving the dependencies of a bundle, it uses resolved bundles to satisfy those dependencies in preference to unresolved bundles. This avoids gratuitously introducing two or more versions of a given bundle, together with its exported classes, into the system.
But sometimes multiple versions of a class can, and indeed must, legitimately coexist. This can occur when assembling a large system from pieces which have been developed independently. The separate pieces can pre-req. distinct versions of common bundles. If these common bundles don't 'leak out' of the pieces of the system that use them, there is no problem.
But if they do leak out, OSGi's 'uses' directive enables the bundles importing from the common bundles to specify the amount of 'leakage'. For example, the following method signature exposes a common type PType:
package org.bar.q;
...
public org.foo.common.p.PType someMethod() {...}
and the following manifest statements record the leakage:
import-package: org.foo.common.p
export-package: org.bar.q,uses:="org.foo.common.p"
OSGi always resolves bundle dependencies so as to respect the 'uses' constraints. There's even support in Peter Kriens' bundle manifest creation tool, bnd, which spots the most straightforward leakages, like the example above, and create the corresponding 'uses' clauses automatically.
More subtle leakages need to be handled by the bundle programmer, but usually it is fairly obvious that something tricky is going on. For example, the following method take a parameter and casts it to the common type PType:
package org.bar.q;
...
public void anotherMethod(Object o) {
... (org.foo.common.p.PType)o ...
}
To avoid class cast exceptions due to o being an instance of the wrong version of PType, the same manifest statements as in the first example above should be specified, although these could not be inferred automatically by bnd.
So with appropriate 'uses' clauses, OSGi is able to manage multiple versions of bundles and minimise type-safety exceptions.