7

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?

Chris
  • 4,450
  • 3
  • 38
  • 49

3 Answers3

8
  • 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).

Marcel Offermans
  • 3,313
  • 1
  • 15
  • 23
  • Thanks for the information, especially the DynamicImport-Package: * tip which surprisingly I couldn't find via internet search. With regard to the second answer, a snippet might be nice if you have time. I will flag this as the approved answer anyway though as your answered my main questions. Thank you. – Chris Apr 30 '10 at 00:35
1

I made a JDBC driver manager for OSGI in an Eclipse RCP and I will take you through how to play nice with OSGI. First, forget about DynamicImport-Package, the only good way to use OSGI is to install/start/stop bundles and use the OSGI mechanism the way it was designed.

  1. You have your JDBC bundle, and create another "Driver bundle" which has the initialization of the DriverClass, the Connection logic and add the necessary commons libraries such as dbcp2 and pool2.

  2. Export the Driver bundle as a JAR/ZIP and include it in your JDBC bundle as a resource.

  3. Let your JDBC bundle unzip the Driver bundle in its work area.

    String workdir= Platform.getStateLocation(jdbc_bundle).toPortableString();
    
  4. Programmatically add driver jars and modify the Driver bundle's MANIFEST.MF file accordingly.

  5. Load the Driver bundle programmatically from the work area

    getBundleContext().installBundle("file:/"+workdir);
    
  6. Use bundle.start(), stop(), uninstall() as necessary when programmatically modifying the list of drivers.

Per Digre
  • 346
  • 3
  • 8
0

The pax-jdbc can be used to delegate dataSources via declarative way, means you can create a config entry in ConfigAdmin service, and the dataSource can be accessed via JNDI. The JDBC driver is deployed as bundle. (most of them have OSGi version)

For example:

The config entry PID is org.ops4j.datasource-test

Properties:

osgi.jdbc.driver.name=H2
databaseName=test
user=sa
password=
dataSourceName=testds-h2

The service is identified by the given dataSourceName. So you can filter for it with (&(objectClass=javax.sql.DataSource)(dataSourceName=test2)).

And you can access the datasource via JNDI:

osgi:service/javax.sql.DataSource/(osgi.jndi.service.name=test2)