2011-12-12 6 views
7

Me gustaría que JBoss use solo las dependencias ubicadas en mi archivo war. Cada vez que despliegue este archivo de guerra, JBoss todavía usa sus propias jarras.¿Cómo usar JPA2 en JBoss 5.x? (o ¿Cómo eliminar el problema de aislamiento de carga de clase?)

Aquí es el jboss-web.xml que utilizo:

<?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> 

y la 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

+0

¿Cómo sabes que está utilizando sus propias jarras? – gkamal

+0

@gkamal Cuando mi aplicación se ejecuta, recibí una excepción 'NoSuchMethod'. – Stephan

+0

¿Se despliega el WAR por sí solo o es parte de un EAR? – CoolBeans

Respuesta

7

1> Resumen

Inicialmente, probé esta clase cargando aislamiento para cargar tarros de Hibernate 3.6.4 con JBoss 5.1.0.GA.

Definitivamente NO es posible. Hay algo mágico debajo del capó que le impide usar cualquier versión de Hibernate con soporte JPA2.

Estoy realmente decepcionado que el proyecto JBoss no proporcionó algún tipo de parche o service pack para admitir JPA2 en 5.1.0.GA.

2> Solución: "El Kernel solución"
que han logrado utilizar JPA2 con JBoss 5.1.0.GA describo aquí mi receta. Es más una prueba de concepto que puedes usar para crear tu propia solución.

Ingredientes:

  • 1 Archivo de la Guerra
  • 1 servlet
  • 1 aplicación independiente de Java (J2SE)

Receta:

Paso 1: cree la aplicación independiente (APP)

Esta aplicación recibirá instrucciones del servlet para usar Hibernate.

Te dejo la elección del método de comunicación. Como la aplicación utiliza JPA2, necesitará un archivo persistence.xml ubicado en una carpeta META-INF. Desde JBoss 5.x, cuando implementa un WAR, JBoss escaneará el WAR y todas sus sub-implementaciones para encontrar y desplegar ciegamente persistence.xml archivos. Cambie el nombre de su archivo persistence.xml en my-persistence.xml por ejemplo. Use el siguiente código cuando construya su EntityManagerFactory (evite que JBoss implemente persistence.xml).

ACTUALIZACIÓN: Este método funciona, pero Hibernate genera algunas advertencias extrañas. Para detener esas advertencias, he decidido colocar la carpeta META-INF y el archivo de persistencia (renombrado de nuevo a persistence.xml) fuera del WAR. En mi caso, elegí una carpeta de configuración especial en el disco duro y la agregué al classpath. No más advertencias extrañas y no se requiere un cargador de clases personalizado para cargar el archivo de persistencia.

Dejo que usted elija entre usar un cargador de clases personalizado o cambiar la ubicación del archivo de persistencia. En ambos casos, JBoss no encontrará el archivo de persistencia.


Paso 2: Construir el servlet

Cuando el servlet necesita tener acceso a la base de datos, se lanza la aplicación y le dice qué hacer.

Para lanzar la aplicación, el servlet es responsable de generar una nueva JVM y construir el classpath de la aplicación. Lea el siguiente código para (Engendrar una JVM). La ruta de clases es fácilmente edificable ya que todos los frascos necesarios estarán en el directorio /lib del Archivo de la Guerra ...


Paso 3: Construir el archivo WAR

construir un archivo WAR donde pones el servlet y la aplicación independiente empaquetada como un JAR. La aplicación será una dependencia de la GUERRA.


Prevenir JBoss con la implementación de 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(); 
    } 
} 

desove una 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

La configuración original que había publicado (jboss-web.xml y jboss- classloading.xml) funciona para mí en EAP-5.12.

Al parecer, el

java2ClassLoadingCompliance="false" 

y/o

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

de jboss-web.xml están siendo ignorados por Jboss. Ver http://docs.jboss.org/jbossesb/docs/4.10/manuals/html/Getting_Started_Guide/index.html#sect-scoped_deploy donde se menciona esto (edición de la comunidad, pero al parecer también es válido para la bendita edición de jboss)

He estado luchando con esto durante mucho tiempo. Luego se dio por vencido. Luego lo revisé recientemente y tropecé con la solución jboss-classloading.xml.

Sin ella, me gustaría tener ClassCastException:

java.lang.ClassCastException: org.hibernate.ejb.HibernatePersistence no se puede echar a javax.persistence.spi.PersistenceProvider

tengo una versión posterior (3.6.0.Final) de hibernate-entitymanager en la guerra de lo que es utilizado por Jboss.

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

Lo bueno de esto, ahora que funciona, es que puedo implementar la misma guerra en tomcat y jboss. (tomcat en la nube para una solución de copia de seguridad, y jboss en el "terreno").

+0

Excelente si funciona para usted en JBoss EAP-5.1.2 pero inicialmente no tuve otra opción para trabajar con JBoss 5.1.0.GA;) – Stephan

1

De acuerdo. Tomamos los siguientes pasos:

La siguiente JAR archivos deben ser retirados de la jboss_home// lib común:

  • ejb3-persistence.jar (vamos a utilizar la aplicación de Hibernate para JPA 2.0)
  • hibernación-annotations.jar (esto es ahora parte de hibernación-core)
  • hibernar-c3p0.jar hibernate-commons-annotations.jar
  • hibernar-core.jar hibernación-entitymanager.jar
  • hibernate-validator.jar

Añadir los siguientes archivos JAR actualizado a JBOSS_HOME/common/lib:

  • hibernación-c3p0-3.6.10.Final.jar
  • de hibernación-commons-annotations- 3.2.0.Final.jar
  • hibernación-core-3.6.10.Final.jar
  • hibernar-EntityManager-3.6.10.Final.jar
  • hibernar-JPA-2,0-api-1.0.1. Final.jar
  • hibernación-validador-4.2.0.Final.jar
  • validación-api-1.0.0.GA.jar (dependencia de hibernación-validador)

Añadir archivo (por ejemplo) my-war\src\main\webapp\WEB-INF\jboss-classloading.xml

Reemplace la política de carga de clases predeterminada para garantizar que las clases de hibernación en su guerra se usen en lugar de cualquier biblioteca de cliente de JBoss. Esto se puede hacer agregando un archivo jboss-classloading.xml al directorio 'my-war/src/main/webapp/WEB-INF'. La línea de importación que se debe tener en cuenta es establecer el primer atributo padre en falso.

<?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> 

Cambiar nombre (por ejemplo) '/src/main/resources/META-INF/persistence.xml'

JBoss intentará cargar ciegamente cualquier archivo persistence.xml que existe en un directorio META-INF en el classpath. Dado que el despliegue de metadatos de la unidad de persistencia suministrado con JBoss solo es compatible con JPA 1.0, debemos deshabilitar este comportamiento y delegarlo en Spring. Esto se puede hacer fácilmente al cambiar el nombre de persistence.xml a (por ejemplo) /src/main/resources/META-INF/my-persistence.xml

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

Actualice la configuración del contexto de la aplicación Spring (por ejemplo) '/src/main/resources/spring-jpaaccess.xml' y agregue persistenceXmlLocation de la siguiente manera.

<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> 
Cuestiones relacionadas