Search code examples
javahibernatejpahibernate-enversopen-liberty

Setting custom revision information with Hibernate Envers on OpenLiberty


We're migrating an application from JEE7 to JEE8. This application relies on Hibernate-ORM and Hibernate-Envers. On JEE7 it was version 5.2.17, on JEE8 it is 5.4.3 now for both liberaries. We have a custom revision entity which extends the DefaultRevisionEntity by adding userinformations through an implementation of RevisionListener. On JEE7 it works great on OpenLiberty 19.0.0.5, for JEE8 and Hibernate 5.4.3 we're getting errors. On JEE8 and Hibernate 5.4.3 it is now possible to use CDI functionality at revisionlisteners. (Setting the revision date manually with Hibernate Envers). On OpenLiberty the revisionlistener initialization is somehow done, when no bean manager is ready to use, like this stack trace is showing it:

Caused by: java.lang.IllegalStateException: org.hibernate.resource.beans.container.internal.NotYetReadyException: CDI BeanManager not (yet) ready to use
[INFO]  at org.hibernate.resource.beans.container.internal.JpaCompliantLifecycleStrategy$BeanImpl.initialize(JpaCompliantLifecycleStrategy.java:112)
[INFO]  at org.hibernate.resource.beans.container.internal.CdiBeanContainerExtendedAccessImpl$BeanImpl.initialize(CdiBeanContainerExtendedAccessImpl.java:113)
[INFO]  at org.hibernate.resource.beans.container.internal.CdiBeanContainerExtendedAccessImpl$BeanImpl.getBeanInstance(CdiBeanContainerExtendedAccessImpl.java:119)
[INFO]  at org.hibernate.resource.beans.internal.ManagedBeanRegistryImpl$ContainedBeanManagedBeanAdapter.getBeanInstance(ManagedBeanRegistryImpl.java:139)
[INFO]  at org.hibernate.envers.internal.revisioninfo.DefaultRevisionInfoGenerator.generate(DefaultRevisionInfoGenerator.java:77)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcess.getCurrentRevisionData(AuditProcess.java:133)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcess.executeInSession(AuditProcess.java:115)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcess.doBeforeTransactionCompletion(AuditProcess.java:174)
[INFO]  at org.hibernate.envers.internal.synchronization.AuditProcessManager$1.doBeforeTransactionCompletion(AuditProcessManager.java:47)
[INFO]  at org.hibernate.engine.spi.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:954)
[INFO]  ... 20 more
[INFO] Caused by: org.hibernate.resource.beans.container.internal.NotYetReadyException: CDI BeanManager not (yet) ready to use
[INFO]  ... 31 more
[INFO] Caused by: java.lang.NullPointerException
[INFO]  at org.hibernate.resource.beans.container.internal.JpaCompliantLifecycleStrategy$BeanImpl.initialize(JpaCompliantLifecycleStrategy.java:109)
[INFO]  ... 29 more

I found this https://discourse.hibernate.org/t/beanmanager-createinstance-being-called-before-afterbeandiscovery-event-fired/2239 conversation which might point in the same direction.

Actually this are our features set in server.xml.

<featureManager>
    <feature>jaxrs-2.1</feature> 
    <feature>jsonp-1.1</feature>
    <feature>cdi-2.0</feature>
    <feature>jpaContainer-2.2</feature>
    <feature>ejbLite-3.2</feature>
    <feature>mpMetrics-1.1</feature>
    <feature>mpHealth-1.0</feature>
    <feature>mpConfig-1.3</feature>
    <feature>servlet-4.0</feature>
</featureManager>

If you need some more information, i will provide them. Is this a known problem ? I would appreciate if you could provide a hint or solution for this problem.

Thank you, very much.


Solution

  • Yes, from the responses I received from Hibernate and IBM, it does seem to be a known issue, and as I mentioned in the forum, I was able to develop a workaround for the issue with guidance from the very helpful Hibernate team.

    The workaround was to implement org.hibernate.search.hcore.spi.EnvironmentSynchronizer to control when JPA initialization can safely continue, and javax.enterprise.inject.spi.Extension in order to detect when CDI has the BeanManager ready.

    I register my implementation of EnvironmentSynchronizer with the org.hibernate.service.spi.ServiceContributor inteface. Inside the EnvironmentSynchronizer, the whenEnvironmentReady event method passes in a Runnable which represents the task taking care of JPA initialization. This needs to be deferred until CDI is finished with bean discovery, and BeanManager is ready.

    My CDI Extension implementation waits for the AfterBeanDiscovery event method, which is the right time to allow the JPA initialization task deferred earlier to continue successfully.

    After I had my workaround in place, I began communicating with IBM Support to raise awareness of the compatibility issue, and to see if this fine grained control of Hibernate initialization can be built in to WebSphere Liberty in a future release. IBM has indicated that they would like Hibernate to work out of the box with WebSphere which is fantastic, so I am hopeful we will see full support for the latest version of Hibernate in WebSphere Liberty in the near future.

    Update: I have just confirmed that unfortunately, our implementation of EnvironmentSynchronizer does not defer Envers initialization. Envers initialization still crashes starting up in WebSphere with our workaround in place.

    I will update our case with IBM to include Envers, and add a note to the thread in the Hibernate forum to see if there are any immediate options available.

    Update: Here is a solution I have tested with Hibernate Envers and a custom RevisionListener. Adding this to persistence.xml will allow WebSphere to start successfully:

    <property name="hibernate.delay_cdi_access" value="true"/>
    

    Comments from Steve Ebersole:

    This option (2) essentially tells Hibernate to delay accessing the BeanManager until it first needs to which is during runtime. In other words, the first time you perform an operation that needs a particular CDI bean Hibernate will ask the BeanManager for it. This has a serious downside in that if the bean does not exist you will not know about that until runtime, conceivably months after a deployment.

    We might use this option short term, with the intention of removing it once IBM WebSphere fully supports the latest version of Hibernate out of the box.