如何在JBoss 5.x上使用JPA2? (或如何消除类加载隔离问题?)

我希望JBoss只使用我的war文件中的依赖项。 每次我部署这个war文件时,JBoss仍然使用自己的jar。

这是我使用的jboss-web.xml

     my.package:loader=my-app.war  java2ParentDelegation=false     

jboss-classloading.xml

   

JBoss 5.1.0.GA

1>总结

最初,我尝试过这个类加载隔离,用于加载带有JBoss 5.1.0.GA的Hibernate 3.6.4 jar。

这绝对不可能。 引擎盖下有一些魔法阻止你使用任何支持JPA2的Hibernate版本。

我真的很失望 JBoss项目没有提供某种补丁或服务包来支持5.1.0.GA上的JPA2。

2>替代方法“内核解决方案”
我已经设法使用JPA2和JBoss 5.1.0.GA我在这里描述了我的食谱。 它更像是您可以用来制作自己的解决方案的概念certificate。

配料 :

  • 1 WAR档案
  • 1个servlet
  • 1个独立的java应用程序(J2SE)

食谱 :

第1步 :构建独立应用程序(APP)

此应用程序将从servlet接收有关使用Hibernate的指令。

我给你留下了沟通方法的选择。 由于APP使用JPA2,因此需要一个位于META-INF文件夹中的persistence.xml文件。 从JBoss 5.x开始,当您部署WAR时,JBoss将扫描WAR及其所有子部署,以便盲目查找和部署persistence.xml文件。 例如,将persistence.xml文件重命名为my-persistence.xml 。 在构建EntityManagerFactory时使用以下代码(防止JBoss部署persistence.xml)。

更新:此方法确实有效,但Hibernate引发了一些奇怪的警告。 为了阻止这些警告,我决定将META-INF文件夹和持久性文件(现在重命名为persistence.xml )放在WAR之外。 在我的例子中,我在硬盘驱动器上选择了一个特殊的配置文件夹,并将其添加到类路径中。 没有更奇怪的警告,也没有加载持久性文件所需的自定义类加载器。

我让您可以选择使用自定义类加载器还是更改持久性文件位置。 在这两种情况下,JBoss都找不到持久性文件。

第2步 :构建servlet

当servlet需要访问数据库时,它会启动APP并告诉它该做什么。

对于推送APP,servlet负责生成新的JVM并构建APP的类路径。 阅读下面的代码(生成JVM)。 类路径很容易构建,因为所有必需的jar都将在WAR存档的/lib目录中…

第3步 :构建WAR归档文件

构建一个WAR归档文件,将servlet和独立应用程序打包为JAR。 APP将是WAR的依赖。


阻止JBoss部署persistence.xml

 // Install a proxy class loader for adding renamed persistence.xml file Thread t = Thread.currentThread(); ClassLoader clOriginal = t.getContextClassLoader(); t.setContextClassLoader(new SpecialClassLoader(clOriginal, "META-INF/my-persistence.xml")); // Build EntityManagerFactory EntityManagerFactory emf = Persistence.createEntityManagerFactory(persistenceUnitName); // Restore original class loader t.setContextClassLoader(clOriginal); //... private class ProxyClassLoader extends ClassLoader { private ClassLoader realClassLoader; private String hiddenFromJBossPersistenceFile; public ProxyClassLoader(ClassLoader realClassLoader, String hiddenFromJBossPersistenceFile) { this.realClassLoader = realClassLoader; this.hiddenFromJBossPersistenceFile = hiddenFromJBossPersistenceFile; } public void clearAssertionStatus() { realClassLoader.clearAssertionStatus(); } public boolean equals(Object obj) { return realClassLoader.equals(obj); } public URL getResource(String name) { return realClassLoader.getResource(name); } public InputStream getResourceAsStream(String name) { return realClassLoader.getResourceAsStream(name); } public Enumeration getResources(String name) throws IOException { ArrayList resources = new ArrayList(); if (name.equalsIgnoreCase("META-INF/persistence.xml")) { resources.add(getResource(this.hiddenFromJBossPersistenceFile)); } resources.addAll(Collections.list(realClassLoader.getResources(name))); return Collections.enumeration(resources); } public int hashCode() { return realClassLoader.hashCode(); } public Class loadClass(String name) throws ClassNotFoundException { return realClassLoader.loadClass(name); } public void setClassAssertionStatus(String className, boolean enabled) { realClassLoader.setClassAssertionStatus(className, enabled); } public void setDefaultAssertionStatus(boolean enabled) { realClassLoader.setDefaultAssertionStatus(enabled); } public void setPackageAssertionStatus(String packageName, boolean enabled) { realClassLoader.setPackageAssertionStatus(packageName, enabled); } public String toString() { return realClassLoader.toString(); } } 

产生JVM

 public static Process createProcess(final String optionsAsString, final String workingDir, final String mainClass, final String[] arguments) throws IOException { String jvm = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; String[] options = optionsAsString.split(" "); List command = new ArrayList(); command.add(jvm); command.addAll(Arrays.asList(options)); command.add(mainClass); command.addAll(Arrays.asList(arguments)); //System.out.println(command); ProcessBuilder processBuilder = new ProcessBuilder(command); processBuilder.directory(new File(workingDir)); return processBuilder.start(); } public static void makeItRun() { try { // Start JVM String classPath = buildClassPath(); String workingDir = getSuitableWorkingDir();//or just "." Process java = createProcess("-cp \"" + classPath + "\"", workingDir, my.package.APP.class.getCanonicalName(), "-the -options -of -my -APP"); // Communicate with your APP here ... // Stop JVM java.destroy(); } catch(Throwable t) { t.printStackTrace(); } } 

您发布的原始配置(jboss-web.xml和jboss-classloading.xml)适用于EAP-5.12。

显然是jboss-web.xml的

 java2ClassLoadingCompliance="false" 

和/或

   java2ParentDelegation=false  

被Jboss忽略了。 请参阅http://docs.jboss.org/jbossesb/docs/4.10/manuals/html/Getting_Started_Guide/index.html#sect-scoped_deploy ,其中提到了这一点(社区版,但显然也适用于jboss的祝福版)

很长一段时间我一直在努力奋斗。 然后放弃了。 然后最近重新访问它并偶然发现了jboss-classloading.xml解决方案。

没有它,我会得到ClassCastException:

java.lang.ClassCastException:org.hibernate.ejb.HibernatePersistence不能强制转换为javax.persistence.spi.PersistenceProvider

我在战争中拥有hibernate-entitymanager的更高版本(3.6.0.Final),而不是Jboss使用的版本。

   org.hibernate hibernate-entitymanager 3.6.0.Final jar compile  

有什么好处,现在它可以工作,我可以在tomcat和jboss上部署相同的战争。 (tomcat在云端用于备份解决方案,jboss在“地面”。)

同意。 我们采取了以下步骤:

需要从jboss_home / common / lib目录中删除以下JAR文件:

  • ejb3-persistence.jar(我们将使用Hibernate的JPA 2.0实现)
  • hibernate-annotations.jar(现在这是hibernate-core的一部分)
  • hibernate-c3p0.jar hibernate-commons-annotations.jar
  • hibernate-core.jar hibernate-entitymanager.jar
  • 冬眠,validator.jar

将以下升级的JAR添加到jboss_home / common / lib目录:

  • 冬眠,c3p0-3.6.10.Final.jar
  • hibernate公地的注解,3.2.0.Final.jar
  • hibernate核心,3.6.10.Final.jar
  • hibernate-的EntityManager,3.6.10.Final.jar
  • 冬眠-JPA-2.0-API-1.0.1.Final.jar
  • hibernate – validation – 4.2.0.Final.jar
  • validation-api-1.0.0.GA.jar(hibernate-validator的依赖)

添加文件(例如) my-war\src\main\webapp\WEB-INF\jboss-classloading.xml

覆盖默认的类加载策略,以确保使用war中的hibernate类代替任何JBoss客户端库。 这可以通过将jboss-classloading.xml文件添加到’my-war / src / main / webapp / WEB-INF’目录来完成。 要注意的导入行是将parent-first属性设置为false。

    

重命名(例如)’/ src /main/resources/META-INF/persistence.xml’

JBoss将尝试盲目加载类路径上META-INF目录中存在的任何persistence.xml文件。 由于JBoss附带的持久性单元元数据部署器仅支持JPA 1.0,因此我们需要禁用此行为并将其委托给Spring。 这可以通过将persistence.xml重命名为(例如)/src/main/resources/META-INF/my-persistence.xml来轻松完成。

更新’/src/main/resources/spring-jpaaccess.xml’

更新Spring应用程序上下文配置(例如)’/src/main/resources/spring-jpaaccess.xml’并添加persistenceXmlLocation,如下所示。

    /bean>