java.lang.LinkageError: ClassCastException

2019-01-27 18:14发布

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).

2条回答
做个烂人
2楼-- · 2019-01-27 18:19

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.*"})
查看更多
疯言疯语
3楼-- · 2019-01-27 18:19

I faced the same issue when deploying my application to JBoss v6.1 - somehow understood that the problem was due to the same package structure of RuntimeDelegate.class existing within jesrey-core and 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>

After my war is deployed to JBoss, just removed resteasy.deployer folder from jboss' server\default\deployers folder. Ran my application and it worked.

I would be happy if someone explain why this error got resolved this way? (I think JBoss' has unnecessarily shipped with resteasy.deployers jars when developers could include the jars on their own in their apps) - Not to hide that I tried deploying my war on Tomcat and it worked smoothly.

查看更多
登录 后发表回答