Как использовать JPA2 на JBoss 5.x?(или Как устранить проблему изоляции загрузки классов?) - PullRequest
8 голосов
/ 12 декабря 2011

Я бы хотел, чтобы JBoss использовал только зависимости, расположенные в моем файле войны.Каждый раз, когда я развертываю этот файл войны, JBoss по-прежнему использует свои собственные банки.

Вот 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

Ответы [ 3 ]

8 голосов
/ 13 декабря 2011

1> РЕЗЮМЕ

Изначально я пробовал этот класс с загрузочной изоляцией для загрузки Jiber Hibernate 3.6.4 с JBoss 5.1.0.GA.

Это определенно НЕ возможно. Под колпаком скрывается магия, которая не позволяет использовать любую версию Hibernate с поддержкой JPA2.

Я действительно разочарован тем, что проект JBoss не предоставил какой-либо патч или пакет обновления для поддержки JPA2 на 5.1.0.GA.

2> ВРЕМЕННОЕ РЕШЕНИЕ : «Решение ядра»
Мне удалось использовать JPA2 с JBoss 5.1.0.GA Я опишу здесь мой рецепт. Это скорее подтверждение концепции, которую вы можете использовать для создания собственного решения.

Ингредиенты:

  • 1 WAR архив
  • 1 сервлет
  • 1 автономное Java-приложение (J2SE)

Рецепт:

Шаг 1 : создание автономного приложения (APP)

Это приложение будет получать инструкции от сервлета для использования Hibernate.

Я оставляю вам выбор способа общения. Поскольку приложение использует JPA2, ему потребуется файл persistence.xml, расположенный в папке META-INF. Начиная с JBoss 5.x, когда вы развертываете WAR, JBoss будет сканировать WAR и все его вспомогательные развертывания для поиска и развертывания вслепую persistence.xml файлов. Например, переименуйте ваш файл persistence.xml в my-persistence.xml. Используйте приведенный ниже код при сборке EntityManagerFactory (не позволяйте JBoss развертывать файл persistence.xml).

UPDATE: Этот метод работает, но Hibernate выдвигает некоторые странные предупреждения. Чтобы прекратить эти предупреждения, я решил поместить папку META-INF и файл постоянства (теперь переименованный в persistence.xml) за пределы WAR. В моем случае я выбрал специальную папку конфигурации на жестком диске и добавил ее в путь к классам. Больше нет странных предупреждений и не требуется специальный загрузчик классов для загрузки файла персистентности.

Я оставляю на ваше усмотрение выбор между использованием пользовательского загрузчика классов или изменением местоположения файла постоянства. В обоих случаях JBoss не найдет файл персистентности.


Шаг 2 : Сборка сервлета

Когда сервлету требуется доступ к базе данных, он запускает приложение и сообщает ему, что делать.

За запуск APP сервлет отвечает за создание новой JVM и создание пути к классам APP. Прочитайте код ниже для (порождение JVM). Путь к классам легко может быть собран, поскольку все необходимые файлы jar будут в каталоге /lib архива WAR ...


Шаг 3 : создание архива WAR

Создайте WAR-архив, в который вы поместите сервлет и автономное приложение, упакованное в JAR-файл. Приложение будет зависеть от 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();
   }
}
1 голос
/ 02 октября 2014

Согласовано.Мы предприняли следующие шаги:

Следующие JAR-файлы необходимо удалить из каталога jboss_home / common / lib:

  • 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
  • hibernate-validator.jar

Добавьте следующие обновленные JAR-файлы в каталог jboss_home / common / lib:

  • hibernate-c3p0-3.6.10.Final.jar
  • hibernate-commons-annotations-3.2.0.Final.jar
  • hibernate-core-3.6.10.Final.jar
  • hibernate-entitymanager-3.6.10.Final.jar
  • hibernate-jpa-2.0-api-1.0.1.Final.jar
  • hibernate-validator-4.2.0.Final.jar
  • validation-api-1.0.0.GA.jar (зависимость от hibernate-validator)

Добавить файл (например) my-war\src\main\webapp\WEB-INF\jboss-classloading.xml

Переопределить дефолтПолитика загрузки классов, обеспечивающая использование классов гибернации в вашей войне вместо любых клиентских библиотек JBoss.Это можно сделать, добавив файл jboss-classloading.xml в каталог «my-war / src / main / webapp / WEB-INF».Строка импорта, на которую следует обратить внимание, устанавливает для атрибута parent-first значение false.

<?xml version="1.0" encoding="UTF-8"?>
 <classloading xmlns="urn:jboss:classloading:1.0" 
   export-all="NON_EMPTY"  
   import-all="true"  
   parent-first="false">
 </classloading>

Rename (например) '/src/main/resources/META-INF/persistence.xml'

JBoss попытается вслепую загрузить любой файл persistence.xml, который существует в каталоге META-INF на пути к классам.Поскольку средство развертывания метаданных модуля персистентности, поставляемое с JBoss, поддерживает только JPA 1.0, нам необходимо отключить это поведение и передать его Spring.Это можно легко сделать, переименовав файл persistence.xml в (например, /src/main/resources/META-INF/my-persistence.xml

Update '/ src / main / resources / spring-jpaaccess.xml '

Обновите конфигурацию контекста приложения Spring (например,') /src/main/resources/spring-jpaaccess.xml 'и добавьте persistenceXmlLocation следующим образом.

<bean id="entityManagerFactory" 
   class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
   <property name="persistenceUnitName" value="myPU" /> 
   <property name="persistenceXmlLocation"  value="classpath:META-INF/my-persistence.xml" />   
/bean>
1 голос
/ 28 июля 2012

Исходная конфигурация, которую вы опубликовали (jboss-web.xml и jboss-classloading.xml), работает для меня на EAP-5.12.

Очевидно, что jboss-web.xml

java2ClassLoadingCompliance="false"

и / или

    <loader-repository-config> 
      java2ParentDelegation=false 
   </loader-repository-config>

игнорируются 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

У меня есть более поздняя версия (3.6.0.Final) hibernate-entitymanager в войне, чем та, которую использует Jboss.

    <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>3.6.0.Final</version>
   <type>jar</type>
   <scope>compile</scope>
</dependency>

Что в этом хорошего, теперь, когда он работает, я могу развернуть то же самоевойна с котом и jboss.(tomcat в облаке для решения для резервного копирования и jboss на «земле».)

...