如何在JBoss 5.x上使用JPA2? (或如何消除类加载隔离问题?) [英] How to use JPA2 on JBoss 5.x ? (or How to eliminate class loading isolation issue?)
问题描述
我希望JBoss只使用我的war文件中的依赖项。
每次我部署这个war文件时,JBoss仍然使用自己的jar。
I would like JBoss to use only the dependencies located in my war file. Each time I deploy this war file, JBoss still uses its own jars.
这是 jboss-web.xml
我使用:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<class-loading java2ClassLoadingCompliance="false">
<loader-repository>
my.package:loader=my-app.war
<loader-repository-config>
java2ParentDelegation=false
</loader-repository-config>
</loader-repository>
</class-loading>
</jboss-web>
和 jboss-classloading.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<classloading
xmlns="urn:jboss:classloading:1.0"
export-all="NON_EMPTY"
import-all="true"
parent-first="false"/>
JBoss 5.1.0.GA
推荐答案
1>摘要
最初,我试过这个类加载用于加载带有JBoss 5.1.0.GA的Hibernate 3.6.4 jar的隔离。
Initially, I have tried this class loading isolation for loading Hibernate 3.6.4 jars with JBoss 5.1.0.GA.
这绝对不可能。引擎盖下有一些魔力这会阻止你使用任何支持JPA2的Hibernate版本。
It's definitively NOT possible. There is some magic under the hood that prevents you from using any Hibernate version with JPA2 support.
我真的失望 JBoss项目没有提供某种补丁或用于在5.1.0.GA上支持JPA2的服务包。
I'm really disappointed that JBoss project didn't provide some kind of patch or service pack for supporting JPA2 on 5.1.0.GA.
2>替代方法: 内核解决方案
我已经设法使用JPA2和JBoss 5.1.0.GA
我在这里描述我的食谱。它可以用来制作自己的解决方案。
2> WORKAROUND : "The Kernel solution"
I have managed to use JPA2 with JBoss 5.1.0.GA
I describe here my recipe. It's more a proof of concept you can use to make your own solution.
成分:
- 1 WAR存档
- 1 servlet
- 1个独立的Java应用程序(J2SE)
食谱:
第1步:构建独立应用程序(APP)
Step 1: Build the standalone application (APP)
此应用程序将从servlet接收使用Hibernate的指令。
This application will receive instructions from the servlet for using Hibernate.
我给你留下通讯方式的选择。
由于APP使用JPA2,它需要一个位于 META-INF
中的 persistence.xml
文件夹。
从JBoss 5.x开始,当您部署WAR时,JBoss将扫描WAR及其所有子部署,以便盲目地查找和部署 persistence.xml
文件。例如,将 persistence.xml
文件重命名为 my-persistence.xml
。在构建 EntityManagerFactory
时使用以下代码(防止JBoss部署persistence.xml)。
I leave you the choice of the communication method.
As the APP uses JPA2, it will need a persistence.xml
file located in a META-INF
folder.
Since JBoss 5.x, when you deploy a WAR, JBoss will scan the WAR and all its sub-deployments for finding and deploying blindly persistence.xml
files. Rename your persistence.xml
file into my-persistence.xml
for example. Use the code below when you build your EntityManagerFactory
(Prevent JBoss from deploying persistence.xml).
更新:
这个方法确实有效,但Hibernate引发了一些奇怪的警告。为了阻止这些警告,我决定将 META-INF
文件夹和持久性文件(重命名为 persistence.xml $)在战争之外的c $ c> now。在我的例子中,我在硬盘驱动器上选择了一个特殊的配置文件夹,并将其添加到类路径中。没有更奇怪的警告,也没有加载持久性文件所需的自定义类加载器。
UPDATE:
This method does work but some strange warnings are raised by Hibernate. In order to stop those warnings, I have decided to put the META-INF
folder and the persistence file (renamed back to persistence.xml
now) outside of the WAR. In my case, I choosed a special config folder on the hard drive and added it to the classpath. No more strange warnings and no custom classloader required for loading the persistence file.
我让你在使用自定义类加载器或更改持久性文件位置之间做出选择。在这两种情况下,JBoss都找不到持久性文件。
第2步:构建servlet
I leave it up to you to choose between using a custom class loader or changing the persistence file location. In both cases, JBoss won't find the persistence file.
Step 2: Build the servlet
当servlet需要时访问数据库,它启动APP并告诉它该做什么。
When the servlet needs to access the database, it launches the APP and tells it what to do.
为了推动APP,servlet负责产生一个新的JVM并构建它的类路径。 APP。阅读下面的代码(生成JVM)。类路径很容易可构建,因为所有必需的jar都位于WAR存档的 / lib
目录中...
第3步:构建WAR存档
For lauching the APP, the servlet is responsible of spawning a new JVM and build the classpath of the APP. Read the code below for (Spawning a JVM). The classpath is easily buildable since all the required jars will be in the /lib
directory of the WAR archive...
Step 3: Build the WAR archive
构建一个WAR存档,放置servlet和独立应用程序打包为JAR。 APP将是WAR的依赖。
Build a WAR archive where you put the servlet and the standalone application packaged as a JAR. The APP will be a dependency of the 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<URL> getResources(String name) throws IOException {
ArrayList<URL> resources = new ArrayList<URL>();
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<String> command = new ArrayList<String>();
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 5.x上使用JPA2? (或如何消除类加载隔离问题?)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!