OSGi 中 Envers 的类加载器问题

Classloader Issues with Envers in OSGi

我已经在 OSGi 上下文中成功启动了 Hibernate,现在我想添加 Envers。

The documentation 声称这是可能的。我不相信了。这个主题没有任何类型的文档,似乎也没有人真正做过。此外,即使使用蓝图实现,我也不得不破解类加载器以使 Hibernate 甚至找到 Envers:

osgiClassLoader = new org.hibernate.osgi.OsgiClassLoader();
osgiClassLoader.addBundle(requestingBundle);
osgiClassLoader.addBundle(FrameworkUtil.getBundle(SessionFactory.class));
osgiClassLoader.addBundle(FrameworkUtil.getBundle(HibernateEntityManagerFactory.class));
osgiClassLoader.addBundle(FrameworkUtil.getBundle(EnversService.class));

Thread.currentThread().setContextClassLoader(osgiClassLoader);

(我觉得我应该问:"Is Envers in OSGi even possible?" 所以如果你对这个问题有明确的答案,请告诉我。我花了太多时间在这些问题。)

然而,实际问题与 Hibernate/Envers 无关,而与 OSGi 无关。两者都想访问所使用的实体和枚举。尽管如此,还是要反思。他们当然不能。当然,我不能添加 Import-Packages。

相关的堆栈跟踪看起来像这样:

Caused by: java.lang.ClassNotFoundException: org.acme.project.MyEnum cannot be found by org.hibernate.core_5.1.0.Final
    at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:461)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:372)
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:364)
    at org.eclipse.osgi.internal.loader.ModuleClassLoader.loadClass(ModuleClassLoader.java:161)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at org.hibernate.internal.util.ReflectHelper.classForName(ReflectHelper.java:151)
    at org.hibernate.type.EnumType.setParameterValues(EnumType.java:105)
    ... 62 more

通常使用与 OSGi 无关的框架,我只是将类似的内容添加到 MANIFEST.MF:

// to org.hibernate.core
Eclipse-BuddyPolicy: registered
// to org.acme.project
Eclipse-RegisterBuddy: org.hibernate.core

但是我无法向 Hibernate 的清单中添加任何内容。我尝试通过片段贡献该行,但这也不起作用。

我什至尝试将整个 Hibernate 依赖项作为 JAR 添加到插件中以添加上述内容。不行。

如何解决这些类加载器问题?

如果您的@Audited class 引用了枚举,org.hibernate.core 需要导入 classes 包。最好的方法是注册一个片段包。

此外,确保休眠包的起始级别低于实体包的起始级别是明智的。

这是示例 Gradle 脚本中的一个片段,用于创建片段包。确保将 org.hibernate.core 包版本更改为您正在使用的版本,并将 'your.package' 更新为包含您的枚举的版本。

/*
When using Envers, entities that reference Enum classes must be imported (Import-Package) by the org.hibernate.core bundle.
 */

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'biz.aQute.bnd:biz.aQute.bnd.gradle:3.2.0'
    }
}

apply plugin: 'biz.aQute.bnd.builder'

jar {
    manifest {
        attributes(
                'Fragment-Host': 'org.hibernate.core;' + 'bundle-version=' + '5.2.9.Final',
                'Import-Package': 'your.package'
        )
    }
}

聚会迟到了,但把它扔在这里以防其他人需要它:

Hibernate 引导在 OSGi 中看起来与在 SE/EE 领域中完全不同。我们在 JIRA 跟踪增强方面有很多票,特别是为了使事情更加动态并减少启动顺序的脆弱性(Steven 绝对正确,hibernate-core、hibernate-envers 等。必须目前在您的捆绑包之前首先启动)。

我强烈建议不要使用 bundle fragment 方法,但我会在 Steven 的回答中评论原因。

我们的 hibernate-demos 项目有一些 OSGi 快速入门,所有这些都包括 Envers 设置。这一个可能是您正在寻找的更多内容:https://github.com/hibernate/hibernate-demos/tree/master/hibernate-orm/osgi/unmanaged-native