Wednesday, July 20, 2011

Hibernate Antlr and OSGi Fragments

From time to time, you'll run across instances where a bundle of yours in OSGi needs to have access to a class, but can't. The error you'll get is "ClassNotFoundException: foo.Bar" where foo.Bar is the package and class your bundle is trying to access. Then, after reviewing your OSGi environment, you see that the package of that class is available, but for some reason your bundle can't see it. What the heck? Why can't it see it?

This usually happens when you use a pre-bundled .jar file that calls "Class.forName(bar.Foo)". Calling Class.forName() isn't a good practice inside of OSGi, because it requires your bundle to be able to perform "wiring" after it has been active and started. But, seriously, you can't change over a decade of programming practices overnight, so folks still do this. Seeing that this would be an issue, the good folks at OSGi created a neat way of fixing it, the bundle fragment! (applause, Woo HOO!)

What a bundle fragment does is add functionality to an existing bundle. In the OSGi reference the purpose is usually listed as providing localization support. However, it is also a very power mechanism for adding and changing the contents of a bundle's MANIFEST.MF file. For those of you unaware of what this file does, it includes a set of directions for the OSGi environment on how to treat a bundle.

In the above example, you would create a fragment adding the foo package to the Import-Package section of the MANIFEST.MF file.

Most bloggers would stop there. I identified an issue and then told you how to fix it. But not me, nope, I want to show you how a real-life example of this using Hibernate and Antlr.

Hibernate uses a parsing service called AST. This, in turn uses Antlr to help with its parsing. Unfortunately, Antlr needs to use a hibernate class called "org.hibernate.hql.ast.HqlToken". And, of course, Antlr does a Class.forName() at runtime to get an instance of it. This kind of makes sense, Antlr wasn't written just for use with Hibernate. As such, it needs to be told at runtime what token it should use for parsing.

To fix this issue, you create a fragment that adds an Import-Package entry for the package HqlToken is in, "org.hibernate.hql.ast". Doing this is pretty simple. First, you create a normal java project. It doesn't matter what tool you use, as long as you have the following basic structure.

- antlr-hibernate-fragment
   pom.xml
   - src
     - main
       - java
       - resources
         - META-INF

Because we're only adding something to the MANIFEST.MF file, the only file in this project that will have anything in it will be the Maven pom.xml. There are a ton of places folks can go to get smart on Maven, so I'm not going to review how the pom.xml file should look other than the maven-bundle-plugin.

In your build section of your pom.xml, add the following

<plugins>
   <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-bundle-plugin</artifactId>
     <extensions>true</extensions>
     <configurations>
       <instructions>
         <Fragment-Host>com.springsource.antlr</Fragment-Host>
         <Import-Package>org.hibernate.hql.ast</Import-Package>
       </instructions>
     </configurations>
   </plugin>
<plugins>

Then, run the following from your console, or compile it with your IDE.

mvn clean install

This will create a bundle fragment ready for use with OSGi. In Karaf, after you deploy your original antlr bundle and the fragment, you can run the following console command to see the new import package directive added to the rest of antlr's import-package section. The bundleId referred to in this code is the bundleId of the original antrl bundle, not the new fragment.

headers (bundleId)


I added the new antlr fragment to my hibernate features.xml document (from a previous blog entry) right after my antlr bundle and then deployed it as part of my Hibernate feature.

Please let me know if that helps!

3 comments:

  1. Thanks for the blog. I was making no headway with spring data jpa / hibernate / osgi. This made it work. I had to change the group id to org.apache.felix, and it wasn't happy with an empty jar, but adding a dummy class got it to work.

    ReplyDelete
  2. Hi Mark, thank you for the tutorial. Do you have an example of the produced META-INF\manifest.mf file... I'm wondering on how to do this for my desktop blog editor, Girasol Editor.

    My problem is this one, the main Jar (the Editor itself) is the one that is most commonly updated and it is already 15m which for me is too much considering how little functionality is in there.

    Half its weight is hibernate and derby related jars. About 2m of the other half are from the libraries that have some persistence related tasks and the main editor is only 2m + 3m of GUI.

    https://code.google.com/p/girasol/source/browse/trunk/Editor/build.xml

    Currently I have a being quiet effectively using Felix as my container and I'm launching it through a main method via installer.

    https://code.google.com/p/orquidea/source/browse/trunk/Simpodial/src/com/psiqueware/orquidea/simpodial/SimpodialActivator.java

    So after the instalation the user has the jvm and the OSGI Launcher jar.

    My goal is to be able to separete some of this 7m (hbm+derby) to another bundle and maybe also the Manzanilla and Alheli persistent objects for 2m less. Therefore everytime I will need to send new functionality for the GUI users will be downloading around 5m.

    If posible I don´t want to merge the full persistance bundle, but what I might do is to attach the 7m to the download manager get them started above the osgi container (as opossed to inside) and then somehow feed HBM with the objects.

    One more thing, I'm using traditional HBM startup and definition with hbm.xml files.

    https://code.google.com/p/girasol/source/browse/trunk/Editor/src/hibernate.cfg.xml

    Which strategy will you recommend for doing so?

    Thank you
    Ángel

    PD: If you want a free copy of Girasol Editor for your blogging needs you can go to http://www.psiqueware.com/2012/09/girasol-editor-eng.html

    ReplyDelete
  3. Great post thank you for sharing this information. This is very much useful for me...!
    Application development company India

    ReplyDelete