Datanucleus、JDO 和可执行 jar - 怎么做?
Posted
技术标签:
【中文标题】Datanucleus、JDO 和可执行 jar - 怎么做?【英文标题】:Datanucleus, JDO and executable jar - how to do it? 【发布时间】:2012-04-23 05:12:25 【问题描述】:我正在使用 Datanucleus 和 JDO 为嵌入式 H2 数据库开发桌面应用程序。当我从 Eclipse 运行它时一切正常,但是当我尝试从中制作可执行 jar 时它停止工作。我收到以下错误:
org.datanucleus.exceptions.NucleusUserException:已指定持久化进程使用名称为“jdo”的 ClassLoaderResolver,但 DataNucleus 插件机制尚未发现此问题。请检查您的 CLASSPATH 和插件规范。
当然这表明我没有正确配置一些东西——我错过了什么?如果我遗漏了一些大的东西,它根本就不起作用,所以我假设它是一个有缺陷的可执行 jar。我在其他应用程序(例如 JPOX)中看到了该错误,该错误已得到修复,但没有给出任何解决方案。
整个错误堆栈跟踪:
Exception in thread "main" javax.jdo.JDOFatalInternalException: Unexpected exception caught.
at javax.jdo.JDOHelper.invokeGetPersistenceManagerFactoryOnImplementation(JDOHelper.java:1193)
at javax.jdo.JDOHelper.getPersistenceManagerFactory(JDOHelper.java:808)
at javax.jdo.JDOHelper.getPersistenceManagerFactory(JDOHelper.java:701)
at db.PersistenceManagerFilter.init(PersistenceManagerFilter.java:44)
at Main.main(Main.java:26)
NestedThrowablesStackTrace:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at javax.jdo.JDOHelper$16.run(JDOHelper.java:1965)
at java.security.AccessController.doPrivileged(Native Method)
at javax.jdo.JDOHelper.invoke(JDOHelper.java:1960)
at javax.jdo.JDOHelper.invokeGetPersistenceManagerFactoryOnImplementation(JDOHelper.java:1166)
at javax.jdo.JDOHelper.getPersistenceManagerFactory(JDOHelper.java:808)
at javax.jdo.JDOHelper.getPersistenceManagerFactory(JDOHelper.java:701)
at db.PersistenceManagerFilter.init(PersistenceManagerFilter.java:44)
at Main.main(Main.java:26)
Caused by: org.datanucleus.exceptions.NucleusUserException: Persistence process has been specified to use a ClassLoaderResolver of name "jdo" yet this has not been found by the DataNucleus plugin mechanism. Please check your CLASSPATH and plugin specification.
at org.datanucleus.NucleusContext.<init>(NucleusContext.java:233)
at org.datanucleus.NucleusContext.<init>(NucleusContext.java:196)
at org.datanucleus.NucleusContext.<init>(NucleusContext.java:174)
at org.datanucleus.api.jdo.JDOPersistenceManagerFactory.<init>(JDOPersistenceManagerFactory.java:364)
at org.datanucleus.api.jdo.JDOPersistenceManagerFactory.createPersistenceManagerFactory(JDOPersistenceManagerFactory.java:294)
at org.datanucleus.api.jdo.JDOPersistenceManagerFactory.getPersistenceManagerFactory(JDOPersistenceManagerFactory.java:195)
... 12 more
它指向的行是PersistenceManagerFilter init方法:
pmf = JDOHelper.getPersistenceManagerFactory(getProperties());
属性文件如下所示:
javax.jdo.PersistenceManagerFactoryClass=org.datanucleus.api.jdo.JDOPersistenceManagerFactory
datanucleus.ConnectionDriverName=org.h2.Driver
datanucleus.ConnectionURL=jdbc:h2:datanucleus
datanucleus.ConnectionUserName=sa
datanucleus.ConnectionPassword=
我拥有来自 maven 的所有依赖项,目标是使用依赖项进行部署。依赖关系如datanucleus页面http://www.datanucleus.org/products/datanucleus/jdo/maven.html
所述有什么想法吗?
【问题讨论】:
什么“可执行jar”?由什么组成? 具有 maven 依赖项的整个项目部署为可执行 jar 文件。 你的意思是你解开了 DN 罐子,把所有东西都放在一个罐子里? 我已经把所有东西都打包到了大 jar 中,所有的依赖 jar 都打包到了可执行的 jar 中。在 jar 中使用 maven-repo 样式结构用于依赖项。捆绑所有内容的正确方法是什么,这样我就可以拥有工作 jar,并且所有依赖项都正常工作? 不,我的清单文件中没有。我不是在创建一个插件,只是一个独立的 java 应用程序,所以我没有看那部分文档。我真的不知道该放什么,捆绑信息,还是只定义导入? 【参考方案1】:DataNucleus jars 都支持 OSGi 并使用插件机制来识别功能,因此包含 plugin.xml 和 META-INF/MANIFEST.MF 文件。这些需要位于与原始 DN jar 相同的位置(从 jar 的根目录)。如果您解压缩然后重新打开它们,您将需要从 DN jar 中合并任何 plugin.xml 和 META-INF/MANIFEST.MF ...所有信息,而不仅仅是其中的一些信息。
【讨论】:
好的,我现在很确定我的 jar 是正确的并且包含所有必需的信息。我想我在属性中遗漏了一些东西,它会是什么?或者他们是正确的?我已经编辑了我的帖子以包含所有属性。可能是关于缺少 datanucleus.ConnectionFactoryName 吗?它应该有什么价值? 您能否更准确地了解合并这些文件的方法?不明显。 @DataNucleus 是的,解释合并清单文件的最佳/正确方法会很棒。我在一个大项目中编译了一个带阴影的 JAR,需要手动合并的东西太多了 @jtravaglini。 MANIFEST 信息是 OSGi,因此任何 OSGi 资源都应该在这方面为您提供帮助(DataNucleus 不会明确使用它,并且当您在 AFAIK 中出现一些错误时不会停止工作)。 plugin.xml 应该很明显如何合并(作为一个 XML 文件)......将所有扩展点放在顶部(从所有被合并的 plugin.xml 文件中),然后将所有扩展放在下面(以及每个扩展的所有条目一起扩展)。【参考方案2】:添加到 DataNucleus 答案。 为了达到你所需要的,你应该使用 maven-dependency-plugin 并将以下内容添加到您的 pom.xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>$project.build.directory/jars</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>false</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
然后依赖项将在 target/jars 目录中。 要执行您的应用,请使用命令: Windows:java -cp "yourFile.jar;jars/*" package.className Linux:java -cp "yourFile.jar:jars/*" package.className 注意:不要使用 jars/*.jar,这不起作用
【讨论】:
【参考方案3】:为了在需要单个 jar 的 Apache Storm 拓扑中使用 DataNucleus 4.x,我必须进行两次 hack 以保持他们的 PluginRegistry 工作正常。问题是 DataNucleus 核心尝试将模块加载为 OSGi 包,即使它没有在 OSGi 容器中运行。只要 jar 没有被合并,它就可以正常工作(我不想合并我的依赖项,但这不是我的选择)。
首先,我将所有 plugin.xml 文件合并到 datanucleus-core plugin.xml 中。诀窍是扩展点 id 是相对于其父插件的 id 的。因此,如果您正在使用的任何模块定义新的扩展点,例如datanucleus-rdbms,您必须重写 id,以便它们与新的父插件相关。
其次,我在 jar 的 MANIFEST.MF 中添加了以下条目:
Premain-Class: org.datanucleus.enhancer.DataNucleusClassFileTransformer
Bundle-SymbolicName: org.datanucleus;singleton:=true
这个解决方案并不理想,因为我们的应用程序本质上是在伪装成 DataNucleus 核心 OSGi 包。然而,这就是我在休息几天后把头砸在桌子上的结果。
也许可以提供不同的 PluginRegistry 实现,但我没有对此进行研究。
【讨论】:
你能解释一下哪些ID吗?【参考方案4】:对于其他努力合并 datanucleus plugin.xml 文件的人,我使用以下代码提供帮助。使用此命令从 3 个单独的 datanucleus plugin.xml 文件中传输内容,这将告诉您哪里有明确需要合并的扩展:
cat plugin_core.xml plugin_rdbms.xml plugin_api.xml | grep -h "extension point" | tr -d "[:blank:]"| sort | uniq -d
更多详情请见separate post。
【讨论】:
以上是关于Datanucleus、JDO 和可执行 jar - 怎么做?的主要内容,如果未能解决你的问题,请参考以下文章
Class org.datanucleus.api.jdo.JDOPersistenceManagerFactory was not found
未找到类 org.datanucleus.api.jdo.PersistenceManagerFactoryClass
Datanucleus/JDO,持久化和检索 java.util.Set 属性