In my OSGi environment I am trying to preload a database driver for further usage. Normally, this can be done like that:
Class.forName("com.mysql.jdbc.Driver");
After that, a connection can be created. However, if I use that in OSGi under Felix, he says that the class cannot be found (ClassNotFoundException) and the connection cannot be created. But when I do something like that (try-catch is omitted):
com.mysql.jdbc.Driver d = new com.mysql.jdbcDriver
Class.forName("com.mysql.jdbc.Driver");
Then everything works fine and the connection is created. However, this is not very pretty because the driver class cannot be exchanged.
Is there a way to load the class with the first method? I assume that I have to provide the correct class loader. But where do I get that from?
The MySQL driver is provided as an OSGi wrapper bundle.
How exactly do you create your bundle manifest? If you use tools to automatically resolve the OSGi import statements of your bundle, they will fail on the first method since they do not recognize a simple string as a package dependency. The second method expresses the dependency as a hard Java dependency, so it's recognized by the tooling which adds the required OSGi import statement (and thus by the OSGi runtime to the classpath of your bundle).
So for your first method to work you must add the dependency to the package com.mysql.jdbc to the OSGi import statements of your bundle. How this is achieved is tool specific, Bnd uses an Import-Statement configuration parameter.
Everything #Heri said in his answer was correct. However if you want to introduce more flexibility into this system, use OSGi Services.
You want to make a database connection but you don't want to tightly couple your code to the specific database or JDBC driver. Why not write a small JDBC wrapper bundle that publishes a javax.sql.DataSource service? Your logic bundle can then bind to the service when it wants to query the database, and it needn't know anything about the physical database connection.
Note that the JDBC wrapper bundle would need to know about a specific JDBC driver, however it would be an extremely thin bundle and you could produce alternative wrappers for each of the drivers that you might wish to use.
Related
I am migrating a legacy project to a new server. Previously the project used a Oracle DB but now i want it to use Postgress. The queries are simple enough and work the same in Postgres.
However the project is missing a Postgres jdbc-driver. Can i somehow add this dependency sideways to the jar without recompiling?
Can i somehow add this dependency to the jar without recompiling?
It depends.
If you are running the server as java -jar myserver.jar ..., then you will at least need to modify the manifest in the JAR file. Strictly speaking this doesn't entail recompiling, but you do need to explode, modify and repack the JAR file.
If the server uses Class.forName to explicitly load an Oracle Driver class, then you will need to change that code to load the Postgres Driver class instead. (There are other ways to use JDBC that avoid this, but this depends on how your legacy server is implemented.)
If your server uses Oracle specific database classes, or Oracle specific SQL features (or it needs to do the same in the Postgres world) then more extensive changes will be required.
But without actually examining your codebase in detail, we can't predict what is required.
My advice is to replace the Oracle driver JAR with a Postgres driver JAR, and see what happens when you run your server against a Postgres database with the appropriate schemas and data.
But I wouldn't do this "in production". Do it in a test environment. If you can't set up a suitable test environment ... forget it.
And if you don't have the source code for your server, I would forget it too. If anything goes wrong you will most likely need source code to figure out the problem and fix it.
I would like my bundle to be either configurable via System properties or via the OSGi Compendium Config Admin.
I am compiling my bundle against the org.osgi:osgi.cmpn:6.0.0 bundle, which as made clear in the OSGi Alliance blog is meant to be used at compile-time only, with the framework providing the actual implementations at runtime.
My code obviously needs to use the ConfigAdmin package (to handle the case in which the ConfigAdmin Service is present)... which means that if the runtime does not export the ConfigAdmin package, my bundle will not resolve properly.
But I wanted this resolution to be optional... so I added this to the manifest:
org.osgi.service.cm;resolution:=optional;version="[1.5,2)"
Now, the bundle will resolve but will crash at runtime with a java.lang.NoClassDefFoundError: org/osgi/service/cm/ManagedService even if the user will not actually use ConfigAdmin for configuration. So this forces the user to install the config-admin bundle just to make my bundle work.
I guess the secret here is to not instantiate any classes that force the JVM to load a class that uses the org.osgi.service.cm package... but I can't see how I can achieve that without ugly hacks with reflection...
Does anyone know how I can check if the package is available at runtime, and if not, avoid the java.lang.NoClassDefFoundError at runtime, making this package dependency truly optional?
The way to tell if the package is available at runtime is to attempt to load a class from it and be prepared for the NoClassDefFoundError. You could do this in a central place and then decide to avoid code paths which require the optional but absent package.
The PackageAdmin can be used to inspect package metadata of the system. It has been deprecated and you are expected to use the BundleWiring instead.
So, in your DS, you would have a dependency to the PackageAdmin/BundleWiring and check if the cm package is exported...
I've been developing OSGi modules but so far I've come across a number of issues when I've had to wrap existing jars. An example of this is the use of the Oracle database driver which, even though I've wrapped the jar as bundle, just refuses to work (cannot find the driver class even though its present). This is just a single example but I've had issues with other 3rd party libraries and was wondering if there's a best practice approach to using 3rd party libraries which works every time?
Jlove
The problem in your case is that jdbc uses a class from the java runtime to find the database driver (DriverManager.getConnection). This can not work as the database driver is not accessible from the system classloader (that loaded the DriverManager class).
A way that works in OSGi is to use a DataSource instead: http://docs.oracle.com/javase/tutorial/jdbc/basics/sqldatasources.html . There you simply create the data source using new and this of course works. The problem is that it makes your user bundle depend on the specific DB driver. So the best practice is to create the DataSource centrally and publish it as service.
You can find some more details in my Apache Karaf DB Tutorial (http://www.liquid-reality.de/display/liquid/2012/01/13/Apache+Karaf+Tutorial+Part+6+-+Database+Access).
Btw. In general this kind of factories are tpyically where libraries fail in OSGi. Every lib invents another and different factory system and most of the are incompatible with the restricted classloaders of OSGi. Luckily most libs are made OSGi ready nowadays. Most times this simply means that you can also call the factory with a concrete object that you can retrieve using an OSGi service.
My preferred approach is not to wrap the library, but to unjar it, add a manifest, and re-jar it. Jars-inside-jars tend to cause issues that are hard to debug. Unjar and re-jar can be automated with a simple ant script.
Also, I like to write MANIFEST.MF manually. If the library being wrapped is small, then it's easy enough to do that. Tools like bnd that generate MANIFEST.MF for you do not always give the right results, and if you rely on them too much you don't know what is going on under the hood.
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?
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).
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.
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.
Export the Driver bundle as a JAR/ZIP and include it in your JDBC bundle as a resource.
Let your JDBC bundle unzip the Driver bundle in its work area.
String workdir= Platform.getStateLocation(jdbc_bundle).toPortableString();
Programmatically add driver jars and modify the Driver bundle's MANIFEST.MF file accordingly.
Load the Driver bundle programmatically from the work area
getBundleContext().installBundle("file:/"+workdir);
Use bundle.start(), stop(), uninstall() as necessary when programmatically modifying the list of drivers.
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)
Inside my host application I tried implement a simple pushService, which
shall be used to transfer an instance of a class named Vehicle to the OSGi
world, by providing a set and get method. To be able to use the service I
exported both the service interface and the Vehicle class to a jar file and
imported that file within the bundle, which should use the service.
Everytime I tried to use the Vehicle class within my host application,
which instanciates the felix framework, and the bundle, I got a linkage
error. After reading the following blog entry
(http://frankkieviet.blogspot.com/2009/03/javalanglinkageerror-loader-constraint.html)
I understood why this error occurs. But I have no clue how to solve my problem.
Is it possible to share a class between the host application and an OSGi
instance? Maybe I have to use reflection instead of import the jar file? I had a look at that library (http://code.google.com/p/transloader/) and I'm don't really sure whether this lib is able to solve my problem or not ...
BR,
Markus
At one time I was using Felix to do EXACTLY what you're asking in a custom client-server application. I've since switched to Equinox (they correctly implement framework fragments which I needed for swing LAF as osgi bundles). I THINK the following will work in Felix, I KNOW it works in Equinox.
UPDATE: I started down a very similar path with my host application. I realized early that I needed to move as much code as possible into real OSGi bundles to truly take advantage of the platform. My host application sets up client/server comms and synchronizes bundles; that's it. The few classes I used to share have been moved into bundle and I haven't look back. If you design/application can support having the majority of code in bundles I would definitely go that route. Even if some redesign is required, it's worth it.
Before initializing the OSGi runtime, set this property "org.osgi.framework.system.packages" to include you packages (no wildcards) separated by semi-colons ";". You may additionally need to include the base osgi packages, "org.osgi.framework" and the base services "org.osgi.packageadmin", "org.osgi.startlevel", "org.osgi.url".
I just dug through my version control and found a snippet when I was still using Felix (the setup is almost the same for Equinox)
Map<String, String> configMap = new HashMap<String, String();
configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
"your.package;other.package;org.osgi.framework");
// setup other properties
Bundle systemBundle = new Felix(configMap, null);
systemBundle.start();
// Now you can use classes from "your.package" with explicity
// declaring them as imports in bundles