【问题标题】:java.lang.LinkageError: ClassCastExceptionjava.lang.LinkageError: ClassCastException
【发布时间】:2012-02-08 01:53:32
【问题描述】:

我确实遇到了一个非常烦人的 TestNG 和 RESTeasy 问题。

我确实有一个类针对使用 RESTeasy 框架公开自身的 API 类运行多个测试。

但是,如果我让测试使用 maven (mvn test) 运行,则会出现以下异常:

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)

测试只是调用返回响应对象(来自 RESTeasy)的 API 对象的方法。作为测试框架,我确实使用了 TestNG。

测试方法

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

问题描述

看起来 RESTeasy 框架在不同的类加载器中加载了 RuntimeDelegate。如果我看一下源代码,那么 RuntimeDelegate 有以下方法(涵盖第 126 行):RuntimeDelegate.java

所以与错误相关的主要语句是instanceof check:

if (!(delegate instanceof RuntimeDelegate))

如果我检查委托实例的类加载器与 RuntimeDelegate 的类加载器,则会得到以下输出:

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

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

我知道这当然行不通,但我想知道为什么 RESTeasy 的东西被加载到 MockClassLoader 而不是另一个。特别是因为我不模拟经过测试的 TokenAPI。

奇怪的事实

奇怪的是,当我从 IntelliJ 运行测试时(我选择只运行包含产生错误的方法的给定类中的所有测试),然后它就会运行。看起来它与 mvn test 运行来自 maven 项目的所有测试这一事实有某种关系(或者至少我猜是这样)。

【问题讨论】:

  • 至少我现在想出了如何让测试再次运行:其中一个测试使用 PowerMockito 并且如上面的类加载器中所述,PowerMockito 以某种方式加载了包 jagax.ws 下的类。为了禁用 PowerMockito 加载类,我将 javax.ws 包添加到 PowerMockitoIgnore 注释:@PowerMockIgnore({"javax.ws.*"}) 我仍然不明白为什么失败的测试使用提供的类加载器通过 PowerMockito。这与TestNG有关吗?
  • 嗨,它实际上取决于系统类加载器之前是否首先加载了这些类。您的代码肯定会引用一些 JAX-WS 类,这些类可能会引用 JAX-WS 中的其他类。 JVM 稍后会进行链接以避免在引导时出现不必要的负载。因此,当这些类第一次使用时,它们是由 PowerMock 类加载器加载的,但是当 RESTeasy 启动时,它是使用系统类加载器加载的,然后比较之后的类型(instanceof)它们是不同的,因为它们是在不同的类加载器。您的解决方案是合适的。
  • 如果您的解决方案有效并且是正确的,您可以回答自己的问题。
  • mvn test 顺便可以被指示“fork”测试。

标签: java classloader testng mockito powermock


【解决方案1】:

很遗憾,我无法告诉你为什么会这样,但我可以告诉你如何解决这个问题。

问题是,PowerMockito 扫描了类路径并添加了 RESTeasy 类(位于包 'javax.ws.*' 中。因此,上面提到的 RuntimeDelegate 由 PowerMockito 类加载器加载并导致后来的问题,该类与来自不同类加载器的类进行了比较。

要解决此问题,请告诉 PowerMockito 在扫描类时忽略 javax.ws 包:

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

【讨论】:

    【解决方案2】:

    在将我的应用程序部署到 JBoss v6.1 时,我遇到了同样的问题 - 不知何故,我明白问题是由于 jesrey-core 和 jboss 的 jaxrs-api jar 中存在相同的 RuntimeDelegate.class 包结构

    下面是我的 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>
    

    在我的战争被部署到 JBoss 之后,刚刚从 jboss 的 server\default\deployers 文件夹中删除了 resteasy.deployer 文件夹。运行我的应用程序,它成功了。

    如果有人解释为什么以这种方式解决此错误,我会很高兴? (当开发人员可以在他们的应用程序中包含自己的 jar 时,我认为 JBoss 不必要地附带了 resteasy.deployers jar) - 不要隐藏我尝试在 Tomcat 上部署我的战争并且它运行顺利。

    【讨论】:

      【解决方案3】:

      我通过更改pom.xml 中的org.glassfish.jersey 版本解决了这个问题

      下面是我的pom.xml

       <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>javax.ws.rs-api</artifactId>
            <version>2.1.1</version>
       </dependency>
       <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>2.31</version>
       </dependency>
      

      【讨论】:

        猜你喜欢
        • 2019-02-16
        • 2020-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-06
        • 1970-01-01
        • 1970-01-01
        • 2017-06-19
        相关资源
        最近更新 更多