HOWTO Use Datanucleus with OSGi and Spring DM

This guide is based on my personal experience and is not the authoritative guide to using DataNucleus with OSGi and Spring DM.
This guide is work in progress

This guide assumes the reader is familiar with concepts like OSGi, Spring, JDO, DataNucleus etc.. This guide only explains how to wire these technologies together and not how they work.

Technologies used in this guide are:

  • IDE (Eclipse 3.5.1)
    • Spring IDE Plugin
    • Datanucleus IDE Plugin
  • OSGi (Equinox 3.5)
  • JDO (DataNucleus 2.0.0)
  • Dependency injection (Spring / Spring DM 2.0.0M1)
  • Datastore (PostgreSQL 8.3, altough any datastore could be used)

Create a target platform

We are going to start by creating a clean OSGi target platform. Start by creating an empty directory which is going to house all the bundles for our target platform.

Step 1 - OSGi

It is important to realize that the Datanucleus plugin system uses the Eclipse extensions system and NOT the plain OSGi facilities. For DataNucleus' plugin system to work correctly we need to have the Eclipse extension system bundles in our target platform. So we need Eclipse Equinox to create the standardized OSGi platform and we need some 'non-standard' Eclipse bundles for the registry platformDataNucleus uses for its plugin system. This means we need the following bundles:

  • org.eclipse.core.contenttype_3.4.1.R35x_v20090826-0451.jar (Grab from your Eclipse install or from the Eclipse Core project)
  • org.eclipse.core.jobs_3.4.100.v20090429-1800.jar (Grab from your Eclipse install or from the Eclipse Core project)
  • org.eclipse.core.runtime_3.5.0.v20090525.jar (Grab from your Eclipse install or from the Eclipse Core project)
  • org.eclipse.equinox.app_1.2.0.v20090520-1800.jar (Download from the Eclipse Equinox project)
  • org.eclipse.equinox.common_3.5.1.R35x_v20090807-1100.jar (Download from the Eclipse Equinox project)
  • org.eclipse.equinox.preferences_3.2.300.v20090520-1800.jar (Download from the Eclipse Equinox project)
  • org.eclipse.equinox.registry_3.4.100.v20090520-1800.jar (Download from the Eclipse Equinox project)
  • org.eclipse.osgi.services_3.2.0.v20090520-1800.jar (Download from the Eclipse Equinox project)
  • org.eclipse.osgi-3.5.1.R35x_v20090827.jar (Part of the Spring DM with Deps distribution or download from the Eclipse Equinox project)

Step 2 - Adding DI

We are now going to add the Spring, Spring ORM, Spring JDBC, Spring Transaction and Spring DM bundles to our target platform.

The following bundles come from the Spring distribution (The 'real' Spring Framework that is, not the Spring Dynamic Modules sub project). The Spring DM 2.0.0M1 distribution comes with some of the bundles described below, however these are release candidates and not final. Its advisable to use the non-RC versions from the 'real' Spring distribution:

  • org.springframework.aop-3.0.0.RELEASE.jar
  • org.springframework.asm-3.0.0.RELEASE.jar
  • org.springframework.beans-3.0.0.RELEASE.jar
  • org.springframework.context-3.0.0.RELEASE.jar
  • org.springframework.context.support-3.0.0.RELEASE.jar
  • org.springframework.core-3.0.0.RELEASE.jar
  • org.springframework.expression-3.0.0.RELEASE.jar
  • org.springframework.orm-3.0.0.RELEASE.jar
  • org.springframework.jdbc-3.0.0.RELEASE.jar
  • org.springframework.transaction-3.0.0.RELEASE.jar
  • org.springframework.expression-3.0.0.RELEASE.jar

The following bundles come from the Spring DM distribution:

  • spring-osgi-annotation-2.0.0.M1.jar
  • spring-osgi-core-2.0.0.M1.jar
  • spring-osgi-extender-2.0.0.M1.jar
  • spring-osgi-io-2.0.0.M1.jar

Step 3 - Adding ORM

We are now going to add JDO and DataNucleus to our target platform.

  • datanucleus-core-2.0.0-release.jar
  • datanucleus-rdbms-2.0.0-release.jar
  • jdo2-api-2.3-ec.jar

Step 4 - Adding miscellaneous bundles

The following bundles are dependencies of our core bundles and can be downloaded from the Spring Enterprise Bundle Repository ( http://www.springsource.com/repository/app/ ):

  • com.springsource.org.aopalliance-1.0.0.jar (Dependency of Spring AOP, the core AOP bundle. )
  • com.springsource.org.apache.commons.logging-1.1.1.jar (Dependency of various Spring bundles, logging abstraction library.)

Overview

This is how the directory housing the target platform looks on my PC:

$ ls -las
total 9228
   4 drwxrwxr-x  2 siepkes siepkes    4096 2010-01-13 22:17 .
   4 drwxrwxr-x 12 siepkes siepkes    4096 2010-01-13 21:42 ..
   8 -rw-rw-r--  1 siepkes siepkes    4615 2009-09-27 23:56 com.springsource.org.aopalliance-1.0.0.jar
  68 -rw-rw-r--  1 siepkes siepkes   61464 2009-12-31 10:50 com.springsource.org.apache.commons.logging-1.1.1.jar
1928 -rw-r--r--  1 siepkes siepkes 1968523 2010-01-07 10:46 datanucleus-core-2.0.0-release.jar
1188 -rw-r--r--  1 siepkes siepkes 1210341 2010-01-07 10:46 datanucleus-rdbms-2.0.0-release.jar
 200 -rw-r--r--  1 siepkes siepkes  198552 2010-01-07 10:46 jdo2-api-2.3-ec.jar
  92 -rw-rw-r--  1 siepkes siepkes   87505 2009-10-05 14:44 org.eclipse.core.contenttype_3.4.1.R35x_v20090826-0451.jar
  84 -rw-rw-r--  1 siepkes siepkes   81848 2009-06-12 00:38 org.eclipse.core.jobs_3.4.100.v20090429-1800.jar
  76 -rw-rw-r--  1 siepkes siepkes   69761 2009-06-12 00:38 org.eclipse.core.runtime_3.5.0.v20090525.jar
  84 -rw-rw-r--  1 siepkes siepkes   78162 2009-09-17 20:21 org.eclipse.equinox.app_1.2.0.v20090520-1800.jar
 104 -rw-rw-r--  1 siepkes siepkes   99448 2009-12-31 13:45 org.eclipse.equinox.common_3.5.1.R35x_v20090807-1100.jar
 108 -rw-rw-r--  1 siepkes siepkes  105368 2009-09-17 20:21 org.eclipse.equinox.preferences_3.2.300.v20090520-1800.jar
 176 -rw-rw-r--  1 siepkes siepkes  173316 2009-12-31 13:42 org.eclipse.equinox.registry_3.4.100.v20090520-1800.jar
1104 -rw-rw-r--  1 siepkes siepkes 1125860 2009-09-27 23:56 org.eclipse.osgi-3.5.1.R35x_v20090827.jar
  72 -rw-rw-r--  1 siepkes siepkes   66065 2009-12-31 14:58 org.eclipse.osgi.services_3.2.0.v20090520-1800.jar
 324 -rw-r--r--  1 siepkes siepkes  324242 2009-12-16 16:00 org.springframework.aop-3.0.0.RELEASE.jar
  56 -rw-rw-r--  1 siepkes siepkes   53081 2009-12-31 12:49 org.springframework.asm-3.0.0.RELEASE.jar
 544 -rw-r--r--  1 siepkes siepkes  551967 2009-12-16 16:00 org.springframework.beans-3.0.0.RELEASE.jar
 648 -rw-r--r--  1 siepkes siepkes  657492 2009-12-16 16:00 org.springframework.context-3.0.0.RELEASE.jar
 104 -rw-r--r--  1 siepkes siepkes  101176 2009-12-16 16:00 org.springframework.context.support-3.0.0.RELEASE.jar
 356 -rw-r--r--  1 siepkes siepkes  359338 2009-12-16 16:00 org.springframework.core-3.0.0.RELEASE.jar
 156 -rw-r--r--  1 siepkes siepkes  154200 2009-12-16 16:00 org.springframework.expression-3.0.0.RELEASE.jar
 380 -rw-r--r--  1 siepkes siepkes  381893 2009-12-16 16:00 org.springframework.jdbc-3.0.0.RELEASE.jar
 328 -rw-r--r--  1 siepkes siepkes  330198 2009-12-16 16:00 org.springframework.orm-3.0.0.RELEASE.jar
 232 -rw-r--r--  1 siepkes siepkes  231660 2009-12-16 16:00 org.springframework.transaction-3.0.0.RELEASE.jar
  24 -rw-r--r--  1 siepkes siepkes   24204 2009-09-28 02:29 spring-osgi-annotation-2.0.0.M1.jar
 560 -rw-r--r--  1 siepkes siepkes  565742 2009-09-28 02:25 spring-osgi-core-2.0.0.M1.jar
 184 -rw-r--r--  1 siepkes siepkes  180718 2009-09-28 02:26 spring-osgi-extender-2.0.0.M1.jar
  36 -rw-r--r--  1 siepkes siepkes   35871 2009-09-28 02:24 spring-osgi-io-2.0.0.M1.jar

Creating our application base

Here I will show how one can create a base for an application with our newly created target platform.

Setup Eclipse

Create a Target Platform in Eclipse by going to 'Window' -> 'Preferences' -> 'Plugin Development' -> 'Target Platform' and press the 'Add' button. Select 'Nothing: Start with an empty target platform', give the platform a name and point it to the directory we put all the jars/bundles in. When you are done press the 'Finish' button. Indicate to Eclipse we want to use this new platform by ticking the checkbox in front of our newly created platform in the 'Target Platform' window of the 'Preferences' screen.

Create a new project in Eclipse by going to 'File' -> 'New...' -> 'Project' and Select 'Plug-in Project' under the 'Plugin development' leaf. Give the project a name (I'm going to call it 'nl.siepkes.test.project.a' in this example). In the radiobox options 'This plugin is targetted to run with:' select 'An OSGi framework' -> 'standard'. Click 'Next'. Untick the 'Generate an activator, a Java class that....' and press 'Finish'.

Obviously Eclipse is not the mandatory IDE for the steps described above. Other technologies can be used instead. For this guide I used Eclipse because it is easy to explain, but for most of my projects I use Maven.

If you have the Spring IDE plugin installed (which is advisable if you use Spring) you can add a Spring Nature to your project by right clicking your project and then clicking 'Spring Tools' -> 'Add Spring Nature'. This will enable error detection in your Spring bean configuration file.

Create a directory called 'spring' in your 'META-INF' directory. In this directory create a Spring bean configuration file by right clicking the directory and click 'New...' -> 'Other...'. A menu called 'New' will popup, select 'Spring Bean Configuration File'. Call the file beans.xml.

Declare a Persistence Manager Factory Bean inside the beans.xml:

<bean id="pmf" class="nl.siepkes.util.DatanucleusOSGiLocalPersistenceManagerFactoryBean">
	<property name="jdoProperties">
		<props>
			<prop key="javax.jdo.PersistenceManagerFactoryClass">org.datanucleus.jdo.JDOPersistenceManagerFactory</prop>
			<!-- PostgreSQL DB connection settings -->
			<!-- Add '?loglevel=2' to Connection URL for JDBC Connection debugging. -->
			<prop key="javax.jdo.option.ConnectionURL">jdbc:postgresql://localhost/testdb</prop>
			<prop key="javax.jdo.option.ConnectionDriverName">org.postgresql.Driver</prop>
			<prop key="datanucleus.validateColumns">true</prop>
			<prop key="javax.jdo.option.ConnectionUserName">foo</prop>
			<prop key="javax.jdo.option.ConnectionPassword">bar</prop>
			<prop key="datanucleus.storeManagerType">rdbms</prop>
			<prop key="datanucleus.transactionIsolation">repeatable-read</prop>

			<prop key="datanucleus.autoCreateSchema">true</prop>
			<prop key="datanucleus.validateTables">true</prop>
			<prop key="datanucleus.validateConstraints">true</prop>
			<prop key="datanucleus.rdbms.CheckExistTablesOrViews">true</prop>
		</props>
	</property>
</bean>

<osgi:service ref="pmf" interface="javax.jdo.PersistenceManagerFactory" />

You can specify all the JDO/DataNucleus options you need with '<prop key="foo_key">bar_value</prop>'.

Notice the '<osgi:service ref="pmf" interface="javax.jdo.PersistenceManagerFactory" />' line. This exports our persistence manager as an OSGi sevice and makes it possible for other bundles to access it.

Also notice that the Persistence Manager Factory is not the normal LocalPersistenceManagerFactoryBean class, but instead the OSGiLocalPersistenceManagerFactoryBean class. The OSGiLocalPersistenceManagerFactoryBean is NOT part of the default DataNucleus distribution. So why do we need to use the OSGiLocalPersistenceManagerFactoryBean instead of the default LocalPersistenceManagerFactoryBean ? The default LocalPersistenceManagerFactoryBean is not aware of the OSGi environment and expects all classes to be loaded by one single classloader (this is the case in a normal Java environment without OSGi). This makes the LocalPersistenceManagerFactoryBean unable to locate its plugins.

The OSGiLocalPersistenceManagerFactoryBean is a subclass of the LocalPersistenceManagerFactoryBean and is aware of the OSGi environment:

public class OSGiLocalPersistenceManagerFactoryBean extends LocalPersistenceManagerFactoryBean implements BundleContextAware {

	private BundleContext bundleContext;
	private DataSource dataSource;

	public DatanucleusOSGiLocalPersistenceManagerFactoryBean() {

	}

	@Override
	protected PersistenceManagerFactory newPersistenceManagerFactory(String name) {
		return JDOHelper.getPersistenceManagerFactory(name, getClassLoader());
	}

	@Override
	protected PersistenceManagerFactory newPersistenceManagerFactory(Map props) {
		ClassLoader classLoader = getClassLoader();

		props.put("datanucleus.primaryClassLoader", classLoader);

		PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory(props, classLoader);

		return pmf;
	}

	private ClassLoader getClassLoader() {
		ClassLoader classloader = null;
		Bundle[] bundles = bundleContext.getBundles();

		for (int x = 0; x < bundles.length; x++) {

			if ("org.datanucleus.store.rdbms".equals(bundles[x].getSymbolicName())) {
				try {
					classloader = bundles[x].loadClass("org.datanucleus.JDOClassLoaderResolver").getClassLoader();
				} catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				break;
			}
		}

		return classloader;
	}

	@Override
	public void setBundleContext(BundleContext bundleContext) {
		this.bundleContext = bundleContext;

	}
}

If we create an new, similear (Plug-in) project, for example 'nl.siepkes.test.project.b' we can import/use our Persistance Manager Factory service by specifying the following in its beans.xml:

<osgi:reference id="pmf" interface="javax.jdo.PersistenceManagerFactory" />

The Persistance Manager Factory (pmf) bean can then be injected into other beans as you normally would do when using Spring and JDO/DataNucleus together.

Labels

 
(None)
  1. Jan 11

    Andy Jefferson says:

    Thanks very much for this clear guide. Since we are few who are actually develop...

    Thanks very much for this clear guide. Since we are few who are actually developing DataNucleus, having people sharing their experiences in this way is essential

    1. Jan 13

      Jasper Siepkes says:

      And thank you guys for DataNucleus! :) I expect to have this guide finished so...

      And thank you guys for DataNucleus!

      I expect to have this guide finished somewhere next week.