Wednesday, June 11, 2008

Spring Dynamic Modules and Hibernate

I'm currently working on a Sourceforge project.
On the server side I want to use Spring DM and Hibernate.
Eclipse RCP and Spring DM builds the client.

I spent the last few evenings trying and searching for a working Spring DM/Hibernate example. I had a lot of classloading issues.
Yesterday I got it to work, you can download the plug-in here.

It's a simple application handling a User object and using HSQL as database.

I added all the dependent jars in the /lib directory. You can create separated plug-ins for them if you want.
Here a list of all the jar files, because thats where I had my problems:
  • cglib-nodep-2.1_3.jar
  • commons-collections.jar
  • commons-dbcp.jar
  • commons-pool.jar
  • dom4j-1.6.1.jar
  • hibernate3.jar
  • hsqldb.jar
  • jta.jar
  • spring-core.jar
  • spring-jdbc.jar
  • spring-orm.jar
  • spring-tx.jar // why is the org.springframework.dao package in this jar?

You can access the service in the Test class:

package com.blogspot.swissdev.springservice;

import org.osgi.framework.BundleContext;

/**
*
* @author Flavio Donze
*/
public class Test {

private BundleContext context = Activator.getDefault().getContext();

public void start() {
System.out.println("starting the test...");

UserService service = (UserService) context.getService(context.getServiceReference(UserService.class.getName()));

User user = (User) context.getService(context.getServiceReference(User.class.getName()));
user.setPassword("pass");
user.setUsername("user");
service.store(user);

for (User u : service.findAll())
{
System.out.println("User: "+u.getId() + ", " + u.getUsername() + ", " + u.getPassword());
}
}
}


And here is my Spring configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="META-INF/spring/database.properties"/>
</bean>

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

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
</props>
</property>
</bean>

<bean id="test" init-method="start" class="com.blogspot.swissdev.springservice.Test"/>

<!-- USER beans, POJO, DAO, Service -->
<bean id="user" class="com.blogspot.swissdev.springservice.UserImpl" scope="prototype">
</bean>

<bean id="userDao" class="com.blogspot.swissdev.springservice.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="userService" class="com.blogspot.swissdev.springservice.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>

</beans>
Since I just wanted to get Spring DM and Hibernate to work, I didn't really test the rest, just in case you encounter some bugs.

To setup your workspace with Spring DM read the first part of my previous post.

For this example I used:
Eclipse 3.4 RC3
Spring Dynamic Modules for OSGi(tm) 1.0.2

11 comments:

Loedolff said...

For another solution with everything encapsulated in OSGi bundles, and which also supports a dynamic Hibernate configuration, please see http://code.google.com/p/voluble/wiki/OsgiHibernateSpringSpringDMSample

manfrhahn said...

I've tried your example, but I could not give the test class a run. Everything else seems to start off, but I can not find the user service in the published services (in the osgi console). How do you start the test class? Can I get an application context and start it with BeanFactory.getBean()?

Flavio Donzé said...

How do you try to access the service?
Using OSGi you don't use the BeanFactory, but the BundleContext.

Here is the Line that reaches the service.
UserService service = (UserService) context.getService(context.getServiceReference(UserService.class.getName()));

Does that answer you question?

Anders Levin said...

Hello.

A very good example for me. However, I am having some problems getting it to work. I have not used your full example, rather I have taken the jar-files and placed them inside my bundle, and also placed them on the Bundle-classpath.

And then I try to wire up my sessionFactory:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

And I get a ClassNotFoundException for this class, even though I can see the class is inside the jar spring-orm.jar, and that jar-file is inside my bundle-jar, and on my classpath. Is that something you had a problem with?

Flavio Donzé said...

hello Andreas

I had a lot of class loading issues! That was probably one of them :-).

Maybe the following helps:

Right Click your project and select "Properties". Select "Java Build Path" and switch to the "Order and Export" tab.
Now make sure the jars are selected.

Anonymous said...

Thank you so much also for this article. I finally managed the thing - to invoke a spring dm backend from an eclipse rcp frontend. I did not run your test example, but the explanations were good enough for me to fix my milion classpath issues. Unbelievable, but in the end it worked!

Anonymous said...
This comment has been removed by a blog administrator.
hire php programmers said...

It was very good formula, this is helpful for my site so, thanks allot for shearing this blog.

dududzerah said...

I tried to use spring dm with hibernate, but something wrong is happening:

WARNING: Unable to find file (ignored): bundle://18.0:1/
java.lang.NullPointerException: in is null
at java.util.zip.ZipInputStream.(ZipInputStream.java:64)
at java.util.jar.JarInputStream.(JarInputStream.java:57)
at java.util.jar.JarInputStream.(JarInputStream.java:43)
at org.hibernate.ejb.packaging.InputStreamZippedJarVisitor.doProcessElements(InputStreamZippedJarVisitor.java:37)
at org.hibernate.ejb.packaging.AbstractJarVisitor.getMatchingEntries(AbstractJarVisitor.java:139)
at org.hibernate.ejb.Ejb3Configuration.addScannedEntries(Ejb3Configuration.java:287)
at org.hibernate.ejb.Ejb3Configuration.scanForClasses(Ejb3Configuration.java:614)
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:360)



It's strange because spring dm should manage the classpath problems.

Did you found this kind of problem in your solution?

Flavio Donzé said...

Hey dududzerah.

Put a breakpoint at:
org.hibernate.ejb.packaging.InputStreamZippedJarVisitor.doProcessElements Line 37.
Check what "jarUrl" is, maybe this will give you a hint on what is missing.

nils said...

hello, this is a very nice example but i can not get the test to run:
do you have any ideas why this could be null?
the service is somehow not injected with spring i think...

starting the test...
!SESSION 2010-11-30 23:55:29.013 -----------------------------------------------
eclipse.buildId=M20100909-0800
java.version=1.6.0_22
java.vendor=Apple Inc.
BootLoader constants: OS=macosx, ARCH=x86_64, WS=cocoa, NL=en_US
Framework arguments: -product org.eclipse.platform.ide
Command-line arguments: -product org.eclipse.platform.ide -data /Users/nils/Documents/fhb/workspaceFHB/../runtime-EclipseApplication -dev file:/Users/nils/Documents/fhb/workspaceFHB/.metadata/.plugins/org.eclipse.pde.core/Eclipse Application/dev.properties -os macosx -ws cocoa -arch x86_64 -consoleLog

!ENTRY com.blogspot.swissdev.springservice 4 0 2010-11-30 23:55:40.035
!MESSAGE
!STACK 0
org.osgi.framework.BundleException: Exception in com.blogspot.swissdev.springservice.Activator.start() of bundle com.blogspot.swissdev.springservice.
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:806)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:755)
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:370)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.resume(AbstractBundle.java:374)
at org.eclipse.osgi.framework.internal.core.Framework.resumeBundle(Framework.java:1067)
at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:561)
at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:546)
at org.eclipse.osgi.framework.internal.core.StartLevelManager.incFWSL(StartLevelManager.java:459)
at org.eclipse.osgi.framework.internal.core.StartLevelManager.doSetStartLevel(StartLevelManager.java:243)
at org.eclipse.osgi.framework.internal.core.StartLevelManager.dispatchEvent(StartLevelManager.java:440)
at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:227)
at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:337)
Caused by: java.lang.NullPointerException: A null service reference is not allowed.
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.getService(BundleContextImpl.java:660)
at com.blogspot.swissdev.springservice.HibernateTest.start(HibernateTest.java:16)
at com.blogspot.swissdev.springservice.Activator.start(Activator.java:27)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:783)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:774)
... 11 more
Root exception:
java.lang.NullPointerException: A null service reference is not allowed.