2012-01-11 22 views
9

Experimento un problema realmente molesto con TestNG y RESTeasy.java.lang.LinkageError: ClassCastException

Tengo una clase que ejecuta varias pruebas contra una clase API que utiliza el marco RESTeasy para exponerse.

Sin embargo si dejo que la prueba de funcionamiento con Maven (mvn test), entonces consigo la siguiente excepción:

java.lang.LinkageError: ClassCastException: attempting to castjar:file:/C:/Users/rit/.m2/repository/org/jboss/resteasy/jaxrs-api/2.3.0.GA/jaxrs-api-2.3.0.GA.jar!/javax/ws/rs/ext/RuntimeDelegate.classtojar:file:/C:/Users/rit/.m2/repository/org/jboss/resteasy/jaxrs-api/2.3.0.GA/jaxrs-api-2.3.0.GA.jar!/javax/ws/rs/ext/RuntimeDelegate.class 
at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:126) 
at javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:96) 
at javax.ws.rs.core.Response$ResponseBuilder.newInstance(Response.java:394) 
at javax.ws.rs.core.Response.status(Response.java:116) 
at javax.ws.rs.core.Response.status(Response.java:130) 
at com.pd.api.TokenAPI_V1.validateAccessToken(TokenAPI_V1.java:141) 
at com.test.pd.api.TokenAPI_V1Test.testIfValidAccessTokenReturnsCorrectHTTPHeadersWhenTokenIsNotFound(TokenAPI_V1Test.java:359) 

La prueba no hace más que llamar a un método de la obejct API que devuelve un objeto Respuesta (de RESTeasy). Como marco de prueba, sí uso TestNG.

Método de prueba

@Test 
public void testIfValidAccessTokenReturnsCorrectHTTPHeadersWhenTokenIsNotFound() throws InvalidAccessTokenException { 
    Mockito.when(tokenService.validateAccessToken(TestConstants.ACCESS_TOKEN)).thenThrow(new InvalidAccessTokenException()); 

    Response response = tokenAPI_v1.validateAccessToken(TestConstants.ACCESS_TOKEN, TestConstants.USER_AGENT); 
    assert "no-store".equals(response.getMetadata().getFirst("Cache-Control")); 
    assert "no-cache".equals(response.getMetadata().getFirst("Pragma")); 
} 

Descripción del problema

Parece que el marco RESTeasy carga el RuntimeDelegate en un cargador de clases diferentes. Si eché un vistazo al código fuente, existe el siguiente método en RuntimeDelegate (que cubre la línea 126): RuntimeDelegate.java.

Así que la declaración principal que se relaciona con el error es el cheque instanceof:

if (!(delegate instanceof RuntimeDelegate)) 

Si puedo comprobar el cargador de clase de la instancia de delegado vs el cargador de clase de la RuntimeDelegate, entonces me sale el siguiente resultado:

delegate.getClass().getClassLoader() -> [email protected] 

RuntimeDelegate.class.getClassLoader() -> [email protected] 

Soy consciente de que esto, por supuesto, no funciona, pero me pregunto por qué las cosas RESTeasy se cargan en el MockClassLoader y no en el otro. Especialmente porque no me burlo del TokenAPI que se prueba.

hechos extraños

Lo extraño es que cuando ejecuto las pruebas de IntelliJ (I sólo podrán optar a ejecutar todas las pruebas de la clase dada que contiene el método que produce el error), entonces Corre a través de. Parece que de alguna manera está relacionado con el hecho de que mvn test ejecuta todas las pruebas del proyecto maven (o al menos eso es lo que supongo).

+0

Al menos me di cuenta ahora cómo hacer que las pruebas se ejecuten nuevamente: Una de las pruebas utiliza PowerMockito y como se indica en el cargador de clases anterior, de alguna manera PowerMockito cargó las clases debajo del paquete jagax.ws. Para desactivar PowerMockito cargar las clases, he añadido el paquete javax.ws a la anotación PowerMockitoIgnore: ({ "javax.ws *"}) @PowerMockIgnore Todavía no entiendo por qué el la prueba que falla utiliza el cargador de clases provisto por PowerMockito. ¿Esto está relacionado con TestNG? – rit

+1

Hola, realmente depende de si el cargador de clases del sistema cargó primero esas clases antes o no. Su código sin duda hace referencia a algunas clases de JAX-WS, que podrían hacer referencia a otras classe en JAX-WS. La JVM realiza el enlace más tarde para evitar una carga innecesaria durante el arranque. Entonces, cuando estas clases se usan por primera vez, las carga el cargador de clases PowerMock, pero cuando se inicia RESTeasy se carga usando el cargador de clases del sistema, luego se comparan los tipos (los de instancia) son diferentes, porque se cargaron en diferentes cargador de clases Su solución es la adecuada. – Brice

+1

Si su solución funciona y es la correcta, puede responder su propia pregunta. – Luciano

Respuesta

6

Lamentablemente no puedo decirle por qué sucedió esto, pero puedo decirle cómo solucionar este problema.

El problema era que PowerMockito escaneaba la ruta de clase y también agregaba las clases RESTeasy (que se encuentran dentro del paquete 'javax.ws. *'. Por lo tanto, el cargador de clases PowerMockito cargó el RuntimeDelegate antes mencionado y causó la . tema, que la clase se comparó con una de un cargador de clases diferentes

para solucionar este problema, informe PowerMockito ignorar el paquete javax.ws al escanear para las clases:

@PowerMockIgnore({"javax.ws.*"}) 
1

que enfrentan el mismo problema al implementar mi aplicación en JBoss v6.1, de alguna manera entendí que el problema debido a la misma estructura de paquete de RuntimeDelegate.class existente dentro de jesrey-core y jboss 'jaxrs-api jar

Below is my pom.xml

<!-- For Restful Client --> 
    <dependency> 
     <groupId>com.sun.jersey</groupId> 
     <artifactId>jersey-client</artifactId> 
     <version>1.8</version> 
     <exclusions> 
      <exclusion> 
       <groupId>com.sun.jersey</groupId> 
       <artifactId>resteasy-jaxrs</artifactId> 
      </exclusion> 
     </exclusions> 
    </dependency> 

    <!-- For Jackson (JSON Utility) --> 
    <dependency> 
     <groupId>org.codehaus.jackson</groupId> 
     <artifactId>jackson-mapper-asl</artifactId> 
     <version>1.8.5</version> 
    </dependency> 

    <dependency> 
     <groupId>com.sun.jersey</groupId> 
     <artifactId>jersey-json</artifactId> 
     <version>1.9</version> 
     <exclusions> 
      <exclusion> 
       <groupId>com.sun.jersey</groupId> 
       <artifactId>jersey-core</artifactId> 
      </exclusion> 
     </exclusions> 
    </dependency> 

Después de mi guerra es desplegada a JBoss, acaba de quitar la carpeta resteasy.deployer de JBoss carpeta servidor \ default \ implementadores. Ejecuté mi aplicación y funcionó.

Me gustaría que alguien explique por qué este error se resolvió de esta manera? (Creo que JBoss 'se envió innecesariamente con resteasy.deployers jars cuando los desarrolladores podían incluir los jars por sí mismos en sus aplicaciones) - No ocultar que traté de implementar mi guerra en Tomcat y funcionó sin problemas.

Cuestiones relacionadas