java.lang.LinkageError: ClassCastException

rit picture rit · Jan 11, 2012 · Viewed 13.2k times · Source

I do experience a really annoying problem with TestNG and RESTeasy.

I do have a class that runs several tests against an API class which uses the RESTeasy framework to expose itself.

However if I let the test run with maven (mvn test), then I get the following exception:

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)

The test does nothing more than calling a method of the API obejct which returns a Response object (from RESTeasy). As testing framework I do use TestNG.

Test Method

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

Issue Description

It looks like the RESTeasy framework loads the RuntimeDelegate in a different class loader. If I take a look at the source code, then there is the following method at the RuntimeDelegate (which covers line 126): RuntimeDelegate.java.

So the main statement that is related to the error is the instanceof check:

if (!(delegate instanceof RuntimeDelegate))

If I check the classloader of the delegate instance vs the classloader of the RuntimeDelegate, then I get the following output:

delegate.getClass().getClassLoader() -> org.powermock.core.classloader.MockClassLoader@31e46a68

RuntimeDelegate.class.getClassLoader() -> sun.misc.Launcher$AppClassLoader@3c0fabe9

I am aware of that this of course doesn't work but I wonder why the RESTeasy stuff is loaded in the MockClassLoader and not in the other one. Especially as I don't mock the TokenAPI which gets tested.

Strange Facts

The strange thing is, that when I run the tests out of IntelliJ (I choose only to run all tests from the given class which contains the method that produces the error), then it runs through. It looks like it is somehow related to the fact that mvn test runs all tests from the maven project (or at least that's what I guess).

Answer

rit picture rit · Jan 30, 2012

Unfortunately I can't tell you why this happened, but I can tell you how to get around this issue.

The problem was, that PowerMockito scanned tha class path and also added the RESTeasy classes (which are located within the package 'javax.ws.*'. Therefor the above mentioned RuntimeDelegate was loaded by the PowerMockito classloader and caused later the issue, that the class was compared against one from a different classloader.

To get around this issue, tell PowerMockito to ignore the javax.ws package when scanning for classes:

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