In this last article of the series, we look at some of the reasons why you may not be able to exploit OSGi services. While these examples are no substitute to a well-designed application, Ivan explores some potential workarounds which can be useful when code is outside of your control.
 

Ivan with latte in right hand

Summary

  • Real world reasons why you might not be able to exploit OSGi services.
  • Potential workarounds for your coding restrictions.
     

     

3) A spanner in the works – exploiting OSGi Services

Despite all the benefits, there are real world reasons why you may not be able to exploit OSGi services. If you have third party code outside of your control, or if re-structuring an existing app is above your pay-grade, then you won’t be able to make the necessary code changes. For those unfortunate enough to have their shoe-laces tied in these matters, there are workarounds – but expect to be compromised. The following approaches are potential workarounds – in the absence of better application architecture.

i) Swap out third-party JARs for third-party bundles

On the face of it, this is a no-brainer. Many vendors and open-source communities offer OSGi aware versions of their libraries. Sometimes it can be as simple as tracking down an OSGi friendly bundled version of the library and swapping the old JAR for the equivalent bundle.

ii) DynamicImport-Package

If you know the dynamically loaded classes will belong to a specific package, you can use DynamicImport-Package: com.mycompany.package in the MANIFEST.MF of the bundle doing the class loading. This allows OSGi to look up the classes to load on-the-fly, but it also negates some of the benefits of using OSGi. If the problem is confined to a very small number of stable classes this may be a pragmatic solution to keep disruption minimal, but use with care.
If you don’t know the package from which classes will be dynamically loaded, you could use the more generic DynamicImport-Package: *". Doing so effectively turns your OSGi framework into one big classpath, negates all the benefits of OSGi, and causes potentially CPU intensive operations every-time the bundle attempts to load classes. It is to be avoided where possible!
Put simply, DynamicImport-Package and buddy class loading are not a viable alternative – if you browse the web you find lots of well meaning people pointing to these “solutions”. Unfortunately, these “solutions” move you right back all the problems of the class path.

Further reading:
Use of ‘DynamicImport-Package: *’ in OSGi

iii) Thread Context Class Loader (TCCL)

Another potential solution might be to set the Thread context class loader to the bundle’s class loader. Many legacy and/or third-party libraries attempt to load application classes by name at runtime: for example, Hibernate reads class names from hbm.xml files and then creates instances of those classes for each database record. When you move such a library to a modular environment, it breaks – the name of a class is not sufficient to uniquely identify it. The identity of a class consists of its fully qualified name AND the class loader that defined it, which in OSGi usually corresponds to the bundle that contains it. So, in addition to the name, we need to know the class loader from which to load the class. Due to the wide variety of class loading environments created by various application servers, many libraries attempt to solve this problem with a set of heuristics. Consulting the TCCL is usually one of the heuristics, along with checking the library’s own class loader or the JRE extension class loader, etc.

If you want to gamble and take the TCCL heuristic approach, you would do something like this:

• save away the current TCCL
• set the current TCCL to be the class loader of the bundle
• call the third-party code which attempts to load the class
• hope you get lucky!

Don’t forget to set the TCCL back to the original on the way out. Here’s the pseudo-code:

ClassLoader tccl = Thread.currentThread().getContextClassLoader();Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
try
{
    // execute third-party code
}
finally
{
    Thread.currentThread().setContextClassLoader(tccl);
}

 

Further reading:
What You Should Know about Class Loaders

vi) Leverage the OSGi frameworks PackageAdmin utilities

Rather than using Class.forName() and hoping your current class loader has the right visibility to the target package/class, you can invoke the OSGi packageAdmin class. By supplying the package name from which your class is exported, you let it do the donkey work. If there is a bundle in the system that exports the class you require from the intended package, this convenience call finds and loads the class on your behalf. For example:

Class clazz = packageAdmin.getExportedPackage(packageName).getExportingBundle().loadClass(className);

vii) Centralize

If you really need class loaders, then centralize and isolate your code in a different package. Make sure your class loading strategies are not visible in your application code. Module systems will provide an API that allows you to correctly load classes from the appropriate module. So when you move to a module system you only have to port one piece of code and not change your whole code base.

Conclusion

This series of articles have discussed dynamic class loading, the problems it causes, and how to achieve the same flexibility with OSGi services without the pain. We’ve also provided a set of real-world workarounds for convenience, but these are no substitute for well-designed applications.

For more on OSGi by Invoke Ivan, take a look at this page and keep an eye out for the next section instalments on our Facebook and Twitter pages.
 

Check out some of the previous series of OSGi Demystified blogs by Invoke Ivan here.

Join The Discussion

Your email address will not be published. Required fields are marked *