Chris Chris - 4 months ago 13
Java Question

JDBC/OSGi and how to dynamically load drivers without explicitly stating dependencies in the bundle?

This is a biggie.

I have a well-structured yet monolithic code base that has a primitive modular architecture (all modules implement interfaces yet share the same classpath). I realize the folly of this approach and the problems it represents when I go to deploy on application servers that may have different conflicting versions of my library.

I'm dependent on around 30 jars right now and am mid-way though bnding them up. Now some of my modules are easy to declare the versioned dependencies of, such as my networking components. They statically reference classes within the JRE and other BNDded libraries but my JDBC related components instantiate via Class.forName(...) and can use one of any number of drivers.

I am breaking everything up into OSGi bundles by service area.


  • My core classes/interfaces.

  • Reporting related components.

  • Database access related components (via JDBC).

  • etc....



I wish for my code to be able to still be used without OSGi via single jar file with all my dependencies and without OSGi at all (via JARJAR) and also to be modular via the OSGi meta-data and granular bundles with dependency information.


  • How do I configure my bundle and
    my code so that it can
    dynamically utilize any driver on the
    classpath and/or within the OSGi
    container environment
    (Felix/Equinox/etc.)?

  • Is there a run-time method to detect if I am running in an OSGi container that is compatible across containers (Felix/Equinox/etc.) ?

  • Do I need to use a different class loading mechanism if I am in a OSGi container?

  • Am I required to import OSGi classes into my project to be able to load an at-bundle-time-unknown JDBC driver via my database module?

  • I also have a second method of obtaining a driver (via JNDI, which is only really applicable when running in an app server), do I need to change my JNDI access code for OSGi-aware app servers?


Answer
  • Utilizing any driver within the OSGi environment requires you using a DynamicImport-Package: * statement so your bundle can resolve these packages when you load a driver with Class.forName(..).
  • Probably the easiest way is to try to access a class that is in the org.osgi.framework package. Those should at least be always around in an OSGi environment (see snippet below). There are more sophisticated mechanisms, so let me know if you need something more advanced. Also, take a look at the OSGi R4.2 core spec, paragraph 3.8.9 which shows some methods of finding the Bundle and BundleContext of a class and therefore indirect helps in determining if you're in a framework or not.
  • That depends on what you're doing, no generic "yes" or "no" answer here. OSGi uses classloaders and does so in a way that is not "typical" for a standard Java application, but depending on what you're doing, you might not notice.
  • No.
  • Take a look at the recently released OSGi enterprise specs. They have a chapter on JNDI integration in OSGi which probably allows you to leave your code (largely) unmodified.

A simple example snippet:

 public static boolean inOSGi() {
  try {
   Class.forName("org.osgi.framework.FrameworkUtil");
   return true;
  }
  catch (ClassNotFoundException e) {
   return false;
  }
 }

Just make sure that you if you put this code in a bundle, the bundle should import org.osgi.framework (otherwise it will never find that class).