views:

1754

answers:

2

I am trying to use Hibernate/Spring in an OSGi (Equinox) environment. It works great if I explicitly point it to the Entity classes in the Persistence.xml:

 <class>com.es.t.eee.domain.StuffSource</class>
 <class>com.es.t.eee.domain.PostalAddress</class>

What I want is for Hibernate to "find" all of the Entity classes just as it does outside of the OSGi environment.

Hibernate is looking in the correct bundle for the @Entities:

Searching mapped entities in jar/par: bundleresource://34/
WARN  27-07 15:30:24,235 (InputStreamZippedJarVisitor.java:doProcessElements:41):
Unable to find file (ignored): bundleresource://34/

It looks like it should work, but when it gets to the point of looking in the Bundle Jar for @Entities, an exception occurs, and I am not sure why. I have included the important parts of the log that Hibernate is spitting out.

Does anyone have any ideas what I am doing wrong or what the issue here is?

I am using:

  • Hibernate Core 3.3.0.SP1
  • Hibernate Annotations 3.4.0.GA
  • Hibernate Commons Annotations 3.1.0.GA
  • Hibernate EntityManager 3.4.0.GA
  • Equinox 3.4
  • Spring Dynamic Modules 1.2.0

Here is where Hibernate parses the Persistence.xml

INFO  27-07 15:30:24,110 (Version.java:<clinit>:15):
Hibernate Annotations 3.4.0.GA
INFO  27-07 15:30:24,110 (Environment.java:<clinit>:543):
Hibernate 3.3.0.SP1
INFO  27-07 15:30:24,110 (Environment.java:<clinit>:576):
hibernate.properties not found
INFO  27-07 15:30:24,126 (Environment.java:buildBytecodeProvider:709):
Bytecode provider name : javassist
INFO  27-07 15:30:24,126 (Environment.java:<clinit>:627):
using JDK 1.4 java.sql.Timestamp handling
INFO  27-07 15:30:24,157 (Version.java:<clinit>:14):
Hibernate Commons Annotations 3.1.0.GA
INFO  27-07 15:30:24,157 (Version.java:<clinit>:16):
Hibernate EntityManager 3.4.0.GA
DEBUG 27-07 15:30:24,219 (Ejb3Configuration.java:configure:312):
Processing PersistenceUnitInfo [
    name: unit.postgresql
    persistence provider classname: null
    classloader: BundleDelegatingClassLoader for [DatabaseObjects (DatabaseObjects)]
    Temporary classloader: org.springframework.instrument.classloading.SimpleThrowawayClassLoader@11b50a1
    excludeUnlistedClasses: true
    JTA datasource: null
    Non JTA datasource: com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 1000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 1012f6d821goxcok12zcw86|174f02c, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> org.postgresql.Driver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 1012f6d821goxcok12zcw86|174f02c, idleConnectionTestPeriod -> 0, initialPoolSize -> 5, jdbcUrl -> jdbc:postgresql://localhost:5432/test2, lastAcquisitionFailureDefaultUser -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 100, maxStatements -> 100, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]
    Transaction type: RESOURCE_LOCAL
    PU root URL: bundleresource://34/
    Jar files URLs []
    Managed classes names []
    Mapping files names []
    Properties [
     hibernate.default_batch_fetch_size: 500
     hibernate.cache.provider_class: net.sf.ehcache.hibernate.EhCacheProvider
     hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
     hibernate.max_fetch_depth: 5
     hibernate.query.factory_class: org.hibernate.hql.ast.ASTQueryTranslatorFactory
     hibernate.format_sql: false
     hibernate.jdbc.batch_size: 1000
     hibernate.use_outer_join: true
     hibernate.archive.autodetection: class
     hibernate.show_sql: false
     hibernate.bytecode.provider: cglib]

Here is where the error occurs, as it tries to find the entities:

DEBUG 27-07 15:30:24,235 (Ejb3Configuration.java:getDetectedArtifacts:562):
Detect class: true; detect hbm: false
DEBUG 27-07 15:30:24,235 (Ejb3Configuration.java:getDetectedArtifacts:562):
Detect class: true; detect hbm: false
DEBUG 27-07 15:30:24,235 (AbstractJarVisitor.java:unqualify:116):
Searching mapped entities in jar/par: bundleresource://34/
WARN  27-07 15:30:24,235 (InputStreamZippedJarVisitor.java:doProcessElements:41):
Unable to find file (ignored): bundleresource://34/
java.lang.NullPointerException: in is null
    at java.util.zip.ZipInputStream.<init>(Unknown Source)
    at java.util.jar.JarInputStream.<init>(Unknown Source)
    at java.util.jar.JarInputStream.<init>(Unknown Source)
    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)
    at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFactory(HibernatePersistence.java:131)
    at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:224)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:291)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.beans.factory.support.AbstractBeanFactory.isSingleton(AbstractBeanFactory.java:366)
    at org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean.afterPropertiesSet(OsgiServiceFactoryBean.java:235)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    at java.security.AccessController.doPrivileged(Native Method)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:423)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
    at org.springframework.osgi.context.support.AbstractDelegatedExecutionApplicationContext.completeRefresh(AbstractDelegatedExecutionApplicationContext.java:288)
    at org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor$CompleteRefreshTask.run(DependencyWaiterApplicationContextExecutor.java:145)
    at java.lang.Thread.run(Unknown Source)
+2  A: 

This blog shows how it can be handled with Spring's Dynamic Modules. Based on that blog, you'd create bean definitions for DAO and Service beans in your application context. The DAO has a reference to the hibernate sessionFactory bean:

<bean id="stuffDao" class="com.es.t.eee.domain.dao.StuffSourceDaoImpl">
  <property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="stuffService" 
  class="com.es.t.eee.domain.dao.StuffServiceImpl">
  <property name="stuffDao" ref="stuffDao"/>
</bean>

<bean id="stuffSource" 
  class="com.es.t.eee.domain.StuffSource" scope="prototype"/>

The StuffSourceDaoImpl would implement the store() method to persist the StuffSource, it passes the StuffSource to the HibernateTemplate to actually do the persistence.

To avoid having to define persistence.xml files or specifying every Entity, you can use the AnnotationSessionFactoryBean and a wildcard to include all the types in the package(s) containing your entities (you may instead want to set the packagesToScan property, I've not tried this and it might take you back to your original problem):

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="hibernateProperties">
    <props>
      <!--etc-->
    </props>
  </property>
  <!-- set either one of these properties -->
  <property name="annotatedClasses">
    <list>
      <value>com.es.t.eee.domain.StuffSource</value>
      <value>com.es.t.eee.domain.PostalAddress</value>
    </list>
  </property>
  <property name="packagesToScan">
    <list>
      <value>com.es.t.eee.domain</value>
    </list>
  </property>
</bean>

To use this you'd get the BundleContext, get the service and bean from the context, then carry on as normal:

String serviceName = StuffService .class.getName();
StuffService service = 
  (StuffService )context.getService(context.getServiceReference(serviceName));

String stuffName = StuffSource.class.getName();
StuffSource stuffSource = 
  (StuffSource) context.getService(context.getServiceReference(stuffName ));

//do whatever with StuffSource
...

service.store(stuffSource );


You may also want to look at this OSGi Alliance blog that discusses some of the issues involved. From the OSGi Alliance blog:

Hibernate manipulates the classpath, and programs like that usually do not work well together with OSGi based systems. The reason is that in many systems the class visibility between modules is more or less unrestricted. In OSGi frameworks, the classpath is well defined and restricted. This gives us a lot of good features but it also gives us pain when we want to use a library that has aspirations to become a classloader when it grows up. ...

To work with Hibernate, you need a Session object. You can get a Session object from a SessionFactory. To get the the SessionFactory, you need to create it with a Configuration object. The Configuration object is created from a configuration XML file. By default, Hibernate loads this from the root of your JAR file, however, you can add classes manually to the configuration if so desired.

Rich Seller
+1  A: 

The previous reply mentioning the OSGi blog entry on the problems of Hibernate and OSGi is an informative read.

I suggest stepping back and thinking about the assumptions that are true in a traditional Java application and how they are no longer valid inside OSGi:

OSGi is dynamic

How will your application work if the Hibernate bundle is started before any bundles that provide the entity classes? Even if the scanning for entity classes worked, it wouldn't find any.

In traditional Java all the classes are available at start-up because of the flat class path, but in OSGi classes are only available if the contributing bundle is started and you import the packages.

Explicit Dependencies

OSGi requires explicit wiring of dependencies; if you don't import a bundle/package, then you can't see the classes in those bundles/packages.

Since you say that explicitly configuring the entity classes work, I assume you are using one of the following to do the dependency wiring:

  • Import-Package
  • Require-Bundle
  • Dynamic-Import
  • Equinox's buddy policy

Rich Seller's suggestion should work (I haven't tested it), but you are still forced to import the packages containing the entity classes, though I see nothing wrong with this and the proposed solution gives the option for package scanning rather than explicitly specifying every single entity class.

Suggestions

  1. Use Rich Seller's solution, but be aware that the solution is not dynamic (this might not matter for your application).
  2. Implement the extender pattern for entity classes so that you can re-create the Hibernate session factory when entity-contributing bundles start and stop.

Now option 1 is easy and option 2 involves a whole lot of work. It all depends on your requirement for automatic adding/removing of entity classes from Hibernate without needing to explicitly specify packages/classes.

hbunny