2012-01-11 26 views
9

Ho riscontrato un problema davvero fastidioso con TestNG e RESTeasy.java.lang.LinkageError: ClassCastException

Ho una classe che esegue diversi test su una classe API che utilizza il framework RESTeasy per esporlo.

Tuttavia se lascio la prova di funzionamento con Maven (test mvn), allora ottengo la seguente eccezione:

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) 

Il test non fa altro che chiamare un metodo della obejct API che restituisce un oggetto Response (da RESTeasy). Come test framework uso TestNG.

Metodo di prova

@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")); 
} 

Descrizione del problema

Sembra che il quadro RESTEasy carica il RuntimeDelegate in una classe loader diverso. Se guardo il codice sorgente, il metodo seguente è disponibile in RuntimeDelegate (che copre la riga 126): RuntimeDelegate.java.

Quindi la comunicazione generale che è legato al l'errore è il controllo instanceof:

if (!(delegate instanceof RuntimeDelegate)) 

Se posso controllare il programma di caricamento classe dell'istanza delegato contro il programma di caricamento classe del RuntimeDelegate, allora ottengo il seguente output:

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

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

Sono consapevole che questo naturalmente non funziona, ma mi chiedo perché il materiale RESTeasy sia caricato nel MockClassLoader e non nell'altro. Soprattutto perché non sto prendendo in giro il TokenAPI che viene testato.

Fatti Strani

La cosa strana è che quando ho eseguito i test di IntelliJ (scelgo solo per eseguire tutti i test dalla data classe che contiene il metodo che genera l'errore), allora scorre attraverso Sembra che sia in qualche modo correlato al fatto che mvn test esegue tutti i test del progetto maven (o almeno questo è quello che immagino).

+0

Almeno ora ho capito come eseguire nuovamente i test: Uno dei test utilizza PowerMockito e come indicato nel classloader sopra, in qualche modo PowerMockito ha caricato le classi sotto il pacchetto jagax.ws. Per disabilitare PowerMockito per caricare le classi, ho aggiunto il pacchetto javax.ws per l'annotazione PowerMockitoIgnore: ({ "javax.ws *"}) @PowerMockIgnore Ancora non capisco perché il test che fallisce utilizza il classloader fornito da PowerMockito. Questo è legato a TestNG? – rit

+1

Salve, in realtà dipende dal fatto che tali classi siano state caricate prima dal classloader di sistema prima o dopo. Il tuo codice fa certamente riferimento ad alcune classi JAX-WS, che potrebbero fare riferimento ad altre classi in JAX-WS. La JVM esegue il collegamento in un secondo momento per evitare carichi inutili all'avvio. Quindi, quando queste classi vengono utilizzate per la prima volta, vengono caricate dal programma di caricamento PowerMock, ma quando RESTeasy viene avviato viene caricato utilizzando il classloader di sistema, quindi i tipi vengono confrontati (l'instanceof) sono diversi, perché sono stati caricati in diversi classloader. La tua soluzione è quella giusta. – Brice

+1

Se la tua soluzione funziona ed è quella giusta, puoi rispondere alla tua stessa domanda. – Luciano

risposta

6

Purtroppo non posso dirti perché è successo, ma posso dirti come aggirare questo problema.

Il problema era che PowerMockito analizzava il percorso della classe e aggiungeva anche le classi RESTeasy (che si trovano all'interno del pacchetto 'javax.ws. *'. Pertanto il RuntimeDelegate sopra menzionato è stato caricato dal programma di caricamento PowerMockito e ha causato in seguito il . questione, che la classe è stato confrontato con uno da un classloader diversa

per ovviare a questo problema, dire PowerMockito di ignorare il pacchetto javax.ws durante la scansione per le classi:

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

ho affrontato lo stesso problema quando distribuisco la mia applicazione su JBoss v6.1 - in qualche modo ho capito che il problema w come dovuto alla stessa struttura del pacchetto di RuntimeDelegate.class esistente all'interno di jesrey-core e jboss 'jaxrs-api jar

Di seguito è riportato il mio 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> 

Dopo la mia guerra viene distribuito a JBoss, appena rimosso cartella resteasy.deployer da JBoss cartella server \ default \ installatori. Ho eseguito la mia applicazione e ha funzionato.

Sarei felice se qualcuno spiegasse perché questo errore è stato risolto in questo modo? (Penso che JBoss sia stato spedito inutilmente con ghiottoni di reimpeditori quando gli sviluppatori potevano includere i giare da soli nelle loro app) - Non per nascondere che ho provato a distribuire la mia guerra su Tomcat e ha funzionato senza intoppi.

Problemi correlati