Friday, March 30, 2012

How to use JDBC driver in OSGi container

One frequently asked question on the FuseSource forum and several Apache projects like Apache servicemix/Karaf/Camel/CXF which I'm working on is that how to use JDBC driver in OSGi container? why I always encounter ClassNotFoundException?

Users usually use some third party libs like spring-jdbc or commons-dbcp to maintain the connection pool, so they configure it like
<bean id="databaseConnection" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/schema" />
<property name="username" value="***" />
<property name="password" value="***" />
</bean>

or

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver.class}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>

Those beans always work in standalone  mode, but when users deploy it in OSGi container like FUSE ESB or Apache Servicemix/Karaf, they will see the JDBC driver ClassNotFoundException.

This error actually is from how OSGi classloader works, say, if bundleA need use some package, it need explicitly import it before hand.

However,  if a bundle(let's say it bundle A) using some code like classForName(jdbc_driver_classname) to init a jdbc driver class, this is a big issue in OSGi world, as if BundleA not import package for jdbc_driver_classname, you'll see the problem. Unfortunately BundleA actually can't know about package name for jdbc_driver_classname beforehand, as the jdbc_driver_classname generally is passed in through configuration during runtime, it can't be determined during build time, so you can't add correct package import when you generate bundle A.

Back to our original issue, spring-jdbc and  commons-dbcp is Bundle A here.

How to resolve it?

Well, we have several options

1.  Put all possible necessary packages in Import-Package OSGi metadata header, this is exactly how spring-jdbc do .  spring-jdbc optionally import packages like
    com.ibm.websphere.rsadapter;resolution:=optional;version=0,
    com.ibm.ws.rsadapter.jdbc;resolution:=optional;version=0,
    com.mchange.v2.c3p0;resolution:=optional;version="[0.9.1, 2.0.0)",
    oracle.jdbc;resolution:=optional;version=0,
    oracle.sql;resolution:=optional;version=0,
    org.apache.derby.impl.io;resolution:=optional;version="[10.5.0, 11.0.0)",
    org.apache.derby.jdbc;resolution:=optional;version="[10.5.0, 11.0.0)",
    org.h2;resolution:=optional;version="[1.0.0, 2.0.0)",
    org.hsqldb;resolution:=optional;version="[1.8.0, 2.0.0)",
    org.jboss.resource.adapter.jdbc;resolution:=optional;version=0,
    weblogic.jdbc.extensions;resolution:=optional;version=0   
    com.sun.rowset;resolution:=optional;version="[1.0.1, 2.0.0)",
pretty much all popular used JDBC drivers is available, but not include  mysql.
Of course we can hack the bundleA to add any Import-Package we want,  it's not a big deal as we always hack, but it's not very graceful, especially sometimes we can't hack  some bundles for license reason.

2. Make bundleA have DynamicImport-Package OSGi headers,  this is the way how Servicemix wrapped commons-dbcp do.It works, but we all know using DynamicImport-Package isn't the best practice in OSGi world. To support JDBC out of the box, we have to do some trade-off.

3. What I prefer to is the fragment bundle way, that said, you create JDBC driver as a fragment bundle, and attach it to bundle A(bundle A is host bundle now), so that all resource from fragment bundle is available for the host bundle. This way you needn't hack bundleA.

Finally, I'd say the fragment bundle is very useful when you try to get around some legacy libs in OSGi, it allow you pass the classname during runtime between bundle(like the JDBC driver we discuss here), it also can allow you to pass some resource files between bundles, I hope I can find some time to elaborate this issue later one.

15 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hello,

    I was struggling with the same problem, and your article gave me some hope that it can be solved somehow. However, none of your three solutions helped. The only thing how I managed to fix it was by using SimpleDriverDataSource instead of DriverManagerDataSource. I have not investigated what is the difference among them, I am happy that is is working.

    Ivan

    ReplyDelete
    Replies
    1. exactly i dnt know anything what he is talking about

      Delete
  3. Thanks so much for the resolution.below is the Import-packages I used as suugested in the your article to solve the problem.

    oracle.jdbc;resolution:=optional;version=0,
    oracle.sql;resolution:=optional;version=0,
    oracle.jdbc;resolution:=optional;version=0,
    oracle.sql;resolution:=optional;version=0,

    ReplyDelete
  4. I read your article, I could understand the explanation you have given. It would be great if you could help me on understanding what is OSGi Compliance and how to achieve it for the java jdbc driver?

    I am in a project wherein, my driver to have the OSGi compliance and give it to customer. I appreciate any help on this.

    Attaching my problem statement and requirement for your reference.


    Problem Statement
    Currently the Java tamil drivers ( packaged as tamiljdbc.jar, tamiljdbc_licnese__3.63.56) are not OSGi compliant. They do not have OSGi headers in the manifest file. In order to reference these drivers from a Redhat Fuse application container (Service Mix) the jar files need to be manually installed into the container using the following steps:

    1. osgi:install -s wrap:mvn:com.ibm.tamil/tamiljdbc/9.7
    2. osgi:install -s wrap:mvn:com.ibm.tamil/tamiljdbc_license_/9.7 ( “wrap” is issued to modified the manifest file of each bundle to be OSGi compliant , this exports all classes within the bundle)
    3. dev:dynamic-import ( This setting allows OSGi to resolve dependencies using any of the bundles already installed in the container, effectively bypassing the usual dependency resolution mechanism (based on the Import-Package header). This is not recommended for normal deployment, because it bypasses version checks: you could easily pick up the wrong version of a package, causing your application to malfunction.)
    4. osgi:start

    Existing applications such as GIO have to run these steps manually for each Service Mix instance. GIO currently has more than 50 Service Mix instances.

    Requirement :

    Provide OSGi compliant tamil drivers. This would enable the following functionality:
    · OSGi management of the tamil drivers within a Service Mix container.
    · A more accurate dependency resolution of the drivers at install time.
    · Multiple versions of the driver can also be deployed into the same Service Mix instance.

    ReplyDelete
  5. I am trying to connect database with fuse using blueprint.
    I am getting the same problem but for "org.springframework.jdbc.datasource.DriverManagerDataSource" after deploying my Jboss Fuse is showing Error: Unable to load class org.springframework.jdbc.datasource.DriverManagerDataSource
    Steps i did to resolve it:
    1. I have added/installed the bundle "org.apache.servicemix.bundles.spring-jdbc" manually which is having above class.
    So ideally it should get the above class now but its not getting resolve.
    Please suggest the necessery steps to resolve it.

    ReplyDelete
  6. Hi,
    I was facing same issue. I have fixed with below steps.

    (1) Copy postgres.sql jar in ${SERVICEMIX_HOME}/lib/ext and restart servicemix.

    (2) Deploy your bundle.


    Thanks,
    Divyang Patel

    ReplyDelete
  7. Really Good blog post.provided a helpful information.I hope that you will post more updates like this Big data hadoop online Training Bangalore


    ReplyDelete
  8. Aw, this was an extremely good post. Taking a few minutes and actual effort to create a good article… but what can I say… I put things off a whole lot and never seem to get nearly anything done. onsite mobile repair bangalore I’m amazed, I must say. Seldom do I come across a blog that’s both educative and amusing, and let me tell you, you've hit the nail on the head. The issue is an issue that too few folks are speaking intelligently about. I'm very happy I came across this in my search for something regarding this. asus display replacement Oh my goodness! Awesome article dude! Many thanks, However I am having problems with your RSS. I don’t know the reason why I am unable to join it. Is there anybody having the same RSS issues? Anyone that knows the answer will you kindly respond? Thanks!! huawei display repair bangalore

    ReplyDelete

  9. Howdy! This post could not be written much better! Looking at this article reminds me of my previous roommate! He always kept preaching about this. I will forward this post to him. Pretty sure he's going to have a great read. Many thanks for sharing! vivo charging port replacement Hi there, I think your blog could be having browser compatibility problems. When I look at your website in Safari, it looks fine but when opening in IE, it's got some overlapping issues. I simply wanted to provide you with a quick heads up! Besides that, great website! lg service center Bangalore Having read this I believed it was really informative. I appreciate you finding the time and effort to put this information together. I once again find myself spending way too much time both reading and commenting. But so what, it was still worth it! motorola display repair bangalore

    ReplyDelete
  10. Charm Windows offers a large group of answers for upvc windows manufacturers gurugram which incorporate - Commotion dropping windows, Robber safe windows, sun based control windows, Security glass windows and modified benefit windows. Anything that the plan you have at the top of the priority list, anything that your practical need, Joy Windows has a superior exhibition answer for you.

    ReplyDelete