Hibernate in OSGi bundle

This is my third post about OSGi technology. This time we’re going to do something more advanced. We will create Hibernate-based application working in OSGi bundles. It can be used as a basis for more complex multi-layered SOA killer-app ;-)

In a few words… We will create a Maven project that will consist of two modules. I think that it would be nice to have some kind of a starter for bigger Hibernate applications. That’s why I’m going to separate Hibernate libraries from the mappings and configuration. Both modules are to be packaged as bundles:

  • osgi-hibernate-classes – will be a wrapper bundle for Hibernate JAR and all its dependencies
  • osgi-hibernate-mysql-test – will contain configuration, mappings the application itself

Both these projects will have a common parent POM file.

Parent POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.lukaszbaran.simpleosgi.db</groupId>
	<artifactId>parent-pom</artifactId>
	<version>1.0</version>
	<packaging>pom</packaging>
	<name>Hibernate on OSGi</name>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<modules>
		<module>osgi-hibernate-classes</module>
		<module>osgi-hibernate-mysql-test</module>
	</modules>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.5</source>
					<target>1.5</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.6</version>
		</dependency>
		<dependency>
			<groupId>sax</groupId>
			<artifactId>sax</artifactId>
			<version>2.0.1</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.16</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-annotations</artifactId>
			<version>3.3.0.ga</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-commons-annotations</artifactId>
			<version>3.3.0.ga</version>
		</dependency>
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.2.2</version>
		</dependency>
		<dependency>
			<groupId>javax.persistence</groupId>
			<artifactId>persistence-api</artifactId>
			<version>1.0</version>
		</dependency>
		<dependency>
			<groupId>javax.annotation</groupId>
			<artifactId>jsr250-api</artifactId>
			<version>1.0</version>
		</dependency>
	</dependencies>
</project>

osgi-hibernate-classes module

This wrapper bundle is very simple, as it won’t have any Java code. It will only consist of POM file which should be located in osgi-hibernate-classes sub-directory and look like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.lukaszbaran.simpleosgi.db</groupId>
		<artifactId>parent-pom</artifactId>
		<version>1.0</version>
		<relativePath>../pom.xml</relativePath>
	</parent>
	<groupId>com.lukaszbaran.simpleosgi.db</groupId>
	<artifactId>osgi-hibernate-classes</artifactId>
	<packaging>bundle</packaging>
	<version>1.0</version>
	<name>osgi-hibernate-classes</name>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<extensions>true</extensions>
				<version>1.4.3</version>
				<configuration>
					<instructions>
						<manifestLocation>META-INF</manifestLocation>
						<Import-Package>
							org.apache.commons.logging,
							javax.sql,
							javax.naming*,
							org.w3c.*,
							org.xml.sax,
							org.xml.sax.ext,
							org.xml.sax.helpers,
							!*
						</Import-Package>
						<Embed-Transitive>true</Embed-Transitive>
						<Embed-Dependency>*</Embed-Dependency>
						<DynamicImport-Package>
							com.lukaszbaran.simpleosgi.db.test.entities.*
 						</DynamicImport-Package>
						<_exportcontents>
							org.hibernate*
						</_exportcontents>
					</instructions>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate</artifactId>
			<version>3.2.6.ga</version>
			<exclusions>
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>
</project>

The most important part of this POM file is the configuration of maven-bundle-plugin. What we want to achieve is to have Hibernate JARs embedded in an OSGi bundle. There are several ways of achieving our goal. It means that the solution presented here is not the ultimate and best solution. But it’s the working one! Here, we are putting all the JARs necessary for Hibernate into a bundle. The JARs are not be unpacked and they will appear in the main path inside of the bundle. This is achieved by the two sections:

	<Embed-Transitive>true</Embed-Transitive>
	<Embed-Dependency>*</Embed-Dependency>

The * character in Emded-Dependency tells plugin to include all dependencies from POM file (of course, it includes dependencies from our parent POM). The Embed-Transitive section causes that all the transitive dependencies will be included as well.

Another important point is Import-Package. This is where we declare what other (external) packages will be referenced by our code. In other words, all external dependencies have to be listed here.

OK, now comes the most tricky part in this tutorial – the packages that our wrapper will be exposing to other bundles. In this case we want to expose Hibernate to other bundles. To achieve this we can use Export-Package, but it may complicate things as we don’t want to have the classes inlined in the bundle [inlined means here *.class files taken out of JAR and put in the bundle’]. That’s why we’re using little documented feature of the maven-bundle-plugin called <_exportcontents> – it will place all the sub-packages of given packages in exported packages section in MANIFEST file.

Another thing worth mentioning is the way the Hibernate dependency is declared in POM dependencies part. We manually exlude one of its transitive dependencies – commons-logging, as we will be using this dependency from FUSE ESB’s bundles. This reduces the final size of the resulting bundle. Btw, I’m pretty sure that it is possible to make this bundle even smaller..

osgi-hibernate-mysql-test module

Now, let’s prepare a sample application that will use of the wrapper bundle. We’ll use MySQL table which could like this:

 CREATE TABLE IF NOT EXISTS `honey` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(250) default NULL,
  `taste` varchar(250) default NULL,
  PRIMARY KEY  (`id`)
 ) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;

What we need is POJO representation of the honey table, Hibernate configuration and the code that will make use of it.

The project structure will look like this:

│   pom.xml
│
└───src
    ├───main
    │   ├───java
    │   │   └───com
    │   │       └───lukaszbaran
    │   │           └───simpleosgi
    │   │               └───db
    │   │                   └───test
    │   │                       ├───entities
    │   │                       │       Honey.java
    │   │                       │
    │   │                       └───osgi
    │   │                               Activator.java
    │   │                               BundleUtils.java
    │   │                               HibernateApplication.java
    │   │
    │   └───resources
    │           hibernate.cfg.xml
    │           Honey.hbm.xml
    │           log4j.properties
    │
    └───test
        └───java

The POM file for the project will be really simple:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>com.lukaszbaran.simpleosgi.db</groupId>
		<artifactId>parent-pom</artifactId>
		<version>1.0</version>
		<relativePath>../pom.xml</relativePath>
	</parent>
	<groupId>com.lukaszbaran.simpleosgi.db</groupId>
	<artifactId>osgi-hibernate-mysql-test</artifactId>
	<packaging>bundle</packaging>
	<version>1.0</version>
	<name>osgi-hibernate-mysql-test</name>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.felix</groupId>
				<artifactId>maven-bundle-plugin</artifactId>
				<extensions>true</extensions>
				<version>1.4.3</version>
				<configuration>
					<instructions>
						<manifestLocation>META-INF</manifestLocation>
						<Import-Package>
							org.osgi.framework,
							org.hibernate,
							org.hibernate.cfg,
							org.hibernate.classic,
							javax.*,
							org.w3c.dom,
							org.xml.sax,
							org.apache.log4j
						</Import-Package>
						<Private-Package>
							com.lukaszbaran.simpleosgi.db.test.osgi.*
						</Private-Package>
						<Export-Package>
							com.lukaszbaran.simpleosgi.db.test.entities.*
						</Export-Package>
						<Bundle-Activator>
							com.lukaszbaran.simpleosgi.db.test.osgi.Activator
						</Bundle-Activator>
						<DynamicImport-Package>
							org.hibernate.proxy.*
						</DynamicImport-Package>
						<Require-Bundle>
							com.lukaszbaran.simpleosgi.db.osgi-hibernate-classes
						</Require-Bundle>
					</instructions>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate</artifactId>
			<version>3.2.6.ga</version>
		</dependency>
		<dependency>
			<groupId>org.apache.felix</groupId>
			<artifactId>org.osgi.core</artifactId>
			<version>1.0.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.1</version>
		</dependency>
	</dependencies>
</project>

The configuration of Bundle plugin may seem a little bit complicated but in fact it’s just a compilation of previous tutorials. We’re declaring that we require our wrapper bundle named: com.lukaszbaran.simpleosgi.db.osgi-hibernate-classes. The only package exported from this bundle is com.lukaszbaran.simpleosgi.db.test.entities – it is where we have our Honey POJO.

This POJO looks like this (it’s really nihil novum sub sole):

package com.lukaszbaran.simpleosgi.db.test.entities;
public class Honey {
	private Integer id;
	private String name;
	private String taste;
 	public Honey() {}
 
	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getTaste() {
		return taste;
	}

	public void setTaste(String taste) {
		this.taste = taste;
	}
	
	@Override
	public String toString() {
		return "id=" + getId() + " \tname=" + getName() + " \ttaste=" + getTaste();
	}
}

The Hibernate mapping (Honey.hbm.xml):

<?xml version="1.0" encoding="UTF-8"?>
<hibernate-mapping>
	<class name="com.lukaszbaran.simpleosgi.db.test.entities.Honey" table="honey">
		<id name="id" column="id" type="java.lang.Integer">
			<generator class="increment"/>
		</id>
		<property name="name" column="name" type="java.lang.String"/>
		<property name="taste" column="taste" type="java.lang.String"/>
	</class>
</hibernate-mapping>

hibernate.cfg.xml file:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
	"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		<property name="hibernate.connection.driver_class">
			com.mysql.jdbc.Driver</property>
		<property name="hibernate.connection.url">
			jdbc:mysql://localhost/firsthibernate</property>
		<property name="hibernate.connection.username">root</property>
		<property name="hibernate.connection.password">password</property>
		<property name="hibernate.connection.pool_size">10</property>
		<property name="show_sql">true</property>
		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
		<property name="hibernate.hbm2ddl.auto">update</property>
	</session-factory>
</hibernate-configuration>

Eventually, we have three Java classes that form our application:

Activator.java this class should only store reference to the bundle in BundleUtils instance, however, I’ve decided to run the test method here:

package com.lukaszbaran.simpleosgi.db.test.osgi;
import org.apache.log4j.Logger;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator {
	private static final Logger LOGGER = Logger.getLogger(Activator.class);
	public void start(BundleContext arg0) throws Exception {
		LOGGER.info("Activator start() START");
		
		// because we need to access to the current bundle elsewhere
		if (arg0 != null) {
			BundleUtils.getInstance().setBundle(arg0.getBundle());
		}

		// perform some test connection
		HibernateApplication app = new HibernateApplication();
		app.test();
		LOGGER.info("Activator start() END");
	}
	public void stop(BundleContext arg0) throws Exception {
		LOGGER.info("Activator stop()");
	}
}

HibernateApplication.java configures Hibernate library using the config files that are embedded in the bundle and it invokes simple database access test:

package com.lukaszbaran.simpleosgi.db.test.osgi;
import java.util.Iterator;
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.lukaszbaran.simpleosgi.db.test.entities.Honey;
public class HibernateApplication {
	private static final Logger LOGGER = Logger.getLogger(HibernateApplication.class);
	public HibernateApplication() {	}

	@SuppressWarnings("unchecked")
	public void test() throws Exception {
		LOGGER.info("Application test() START");
		Session session = null;
		try {
			Configuration cfg = new Configuration();
			cfg.configure(BundleUtils.getInstance().loadResourceURL("/hibernate.cfg.xml"));
			cfg.addDocument(BundleUtils.getInstance().loadResourceXML("/Honey.hbm.xml"));
			SessionFactory sessionFactory = cfg.buildSessionFactory();
			session = sessionFactory.openSession();
			List<Honey> list = session.createCriteria(Honey.class).list();
			for (Iterator<Honey> iter = list.iterator(); iter.hasNext();) {
				Honey element = iter.next();
				LOGGER.debug(element);
			}
			session.close();
		} catch (Exception bhe) {
			LOGGER.error("Exception caught: ", bhe);
			throw bhe;
		} 
	}
}

BundleUtils.java is an ugly singleton class (it really should be done other way as I guess it is against the notion of OSGi) but this code was not the most important part of the show. We only needed to store a reference to a bundle somewhere. Besides that, the class provides methods to access bundle resources:

package com.lukaszbaran.simpleosgi.db.test.osgi;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.osgi.framework.Bundle;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class BundleUtils {
	private static BundleUtils instance = null; 
	private Bundle bundle;
        private final DocumentBuilder builder;
	private BundleUtils() {
	    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	    factory.setNamespaceAware(true);
	    try {
	        builder = factory.newDocumentBuilder();
	    } catch (ParserConfigurationException ex) {
	    	throw new RuntimeException("Unable to instantiate DocumentBuilder.", ex);
	    }		
	}
	public static synchronized BundleUtils getInstance() {
		if (instance == null)
			instance = new BundleUtils();
		return instance;
	}
	public Bundle getBundle() {
		return bundle;
	}
	public void setBundle(Bundle bundle) {
		this.bundle = bundle;
	}
	public Document loadResourceXML(final String resourceName) {
	    URL url = loadResourceURL(resourceName);
		try {
			InputStream is = url.openStream();
			return builder.parse(is);
		} catch (SAXException e) {
	    	throw new RuntimeException("Unable to parse resource.", e);
		} catch (IOException e) {
	    	throw new RuntimeException("IO exception while parsing resource.", e);
		}
	}
	public URL loadResourceURL(final String resourceName) {
	    URL url = null;
		if (bundle == null) { 
			url = this.getClass().getResource(resourceName);
		} else {
			url = bundle.getResource(resourceName);
		}
		if (url == null) {
			throw new RuntimeException("Unable to access resource.");
		}
		return url;
	}		
}

osgi-hibernate.zip contains all the source code presented here.

Summary

Of course, you can now build those two bundles and deploy them into FUSE ESB container (I guess it would be better to start with deploying Hibernate wrapper bundle in the first place). If your MySQL database is properly configured and if there was anything in the honey table, you would see some results in data/log/servicemix.log file.