在OpenLiberty上使用Hibernate Envers设置自定义修订信息

时间:2019-06-13 14:45:22

标签: java hibernate jpa hibernate-envers open-liberty

我们正在将应用程序从JEE7迁移到JEE8。 该应用程序依赖于Hibernate-ORM和Hibernate-Envers。在JEE7上,它是5.2.17版,在JEE8上,两个库现在都是5.4.3。 我们有一个自定义的修订版实体,它通过RevisionListener的实现添加用户信息来扩展DefaultRevisionEntity。 在JEE7上,它在OpenLiberty 19.0.0.5上运行良好,对于JEE8和Hibernate 5.4.3,我们会遇到错误。在JEE8和Hibernate 5.4.3上,现在可以在修订侦听器上使用CDI功能。 (Setting the revision date manually with Hibernate Envers)。 在OpenLiberty上,当没有准备好使用的bean管理器时,修订侦听器的初始化就以某种方式完成,如以下堆栈跟踪所示:

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

我发现这个https://discourse.hibernate.org/t/beanmanager-createinstance-being-called-before-afterbeandiscovery-event-fired/2239对话可能指向同一方向。

实际上,这是我们在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>

如果您需要更多信息,我会提供。 这是一个已知问题吗?如果您能提供此问题的提示或解决方案,我们将不胜感激。

非常感谢。

1 个答案:

答案 0 :(得分:1)

是的,从我从Hibernate和IBM收到的答复来看,这确实是一个已知问题,而且正如我在论坛中提到的那样,在非常有用的Hibernate团队的指导下,我能够针对此问题制定解决方法。

解决方法是实施org.hibernate.search.hcore.spi.EnvironmentSynchronizer以控制JPA初始化何时可以安全地继续,以及实现javax.enterprise.inject.spi.Extension以检测CDI何时准备好BeanManager

我用org.hibernate.service.spi.ServiceContributor接口注册了我的EnvironmentSynchronizer实现。在EnvironmentSynchronizer内部,whenEnvironmentReady事件方法传入Runnable,它表示负责JPA初始化的任务。需要推迟到CDI完成bean发现并且BeanManager准备就绪为止。

我的CDI扩展实现等待AfterBeanDiscovery事件方法,这是允许之前推迟的JPA初始化任务成功继续的正确时间。

确定适当的解决方法之后,我开始与IBM支持人员进行交流,以提高对兼容性问题的认识,并查看在将来的发行版中是否可以将这种对Hibernate初始化的细粒度控制内置于WebSphere Liberty中。 IBM表示他们希望Hibernate与WebSphere一起开箱即用,这太棒了,所以我希望我们能在不久的将来看到对WebSphere Liberty中最新版本的Hibernate的全面支持。

更新:我刚刚确认,不幸的是,我们的EnvironmentSynchronizer实现并没有延迟Envers初始化。启用我们的解决方法后,Envers初始化仍然会在WebSphere中启动时崩溃。

我将通过IBM更新我们的案例,以包括Envers,并在Hibernate论坛的线程中添加注释,以查看是否有任何立即可用的选项。

更新:这是我已使用Hibernate Envers和自定义的RevisionListener测试的解决方案。将其添加到persistence.xml将使WebSphere成功启动:

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

史蒂夫·埃伯索尔的评论:

  

此选项(2)本质上告诉Hibernate延迟访问BeanManager,直到它首先需要在运行时访问。换句话说,第一次执行需要特定CDI bean的操作时,Hibernate会向BeanManager询问。这有一个严重的缺点,就是如果不存在该bean,您可能要等到运行时(大概在部署数月后)才能知道。

我们可能会在短期内使用此选项,以便在IBM WebSphere完全支持最新版本的Hibernate后立即删除它。