Tomcat 运行时异常帮助:...JsonFactory'(当前帧,堆栈[0])无法分配给'com/fasterxml/jackson/core/TokenStreamFactory'

问题描述 投票:0回答:1

背景:

我正在开发一段java代码,该代码应该在tomcat上运行的供应商应用程序服务器中运行(带有startup.xml)。我无法触及供应商代码,但他们公开了一个框架,以便我们可以将插件放入他们的平台中,以创建他们公开的接口的自定义实现。作为版本升级的一部分,他们从 java 11 迁移到 java 17,我们正在经历回归(下面的错误)。

我们确保我们的 maven pom.xml 面向 java 17 并且 fastxml 库都是一致的...

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.13.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.13.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.13.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-jaxb-annotations</artifactId>
            <version>2.13.2</version>
        </dependency>

我注意到在供应商的 tomcat lib 文件夹中,他们确实有 2.9.8 的非常旧的 Fasterxml 库,并且我注意到在该版本的 javadoc 中没有 TokenStreamFactory。不幸的是,我无法改变它,因为它是供应商代码/黑匣子。我只能在我创建的插件的范围内工作并放入其指定的文件夹中。我可以更改startup.xml,但这有点“我自己承担风险”。

ERROR: RuntimeException encountered attempting to send message.
java.lang.RuntimeException: org.apache.kafka.common.KafkaException: Failed to construct kafka producerException in thread "ordermessageadapter" java.lang.RuntimeException: org.apache.kafka.common.KafkaException: Failed to construct kafka producer
        at com.vendor.xyz.conn.kafka.KafkaTopicSenderFactory.createAdapter(KafkaTopicSenderFactory.java:39)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at com.vendor.plugin.APIInvocationHandler.invoke(APIInvocationHandler.java:62)
        at jdk.proxy4/jdk.proxy4.$Proxy166.createAdapter(Unknown Source)
        at com.vendor.messageadapter.EndPoint$MySenderBufferThread.run(EndPoint.java:134)
Caused by: org.apache.kafka.common.KafkaException: Failed to construct kafka producer
        at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:442)
        at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:292)
        at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:319)
        at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:304)
        at com.xyz.connection.manager.KafkaBuilder.producerCreation(KafkaBuilder.java:56)
        at com.xyz.connection.manager.KafkaBuilder.connect(KafkaBuilder.java:52)
        at com.vendor.xyz.conn.kafka.KafkaTopicSenderAdapter.<init>(KafkaTopicSenderAdapter.java:49)
        at com.vendor.xyz.conn.kafka.KafkaTopicSenderFactory.createAdapter(KafkaTopicSenderFactory.java:36)
        ... 7 more
Caused by: java.lang.NoClassDefFoundError: Could not initialize class io.confluent.kafka.schemaregistry.client.rest.RestService
        at io.confluent.kafka.schemaregistry.client.CachedSchemaRegistryClient.<init>(CachedSchemaRegistryClient.java:157)
        at io.confluent.kafka.serializers.AbstractKafkaSchemaSerDe.configureClientProperties(AbstractKafkaSchemaSerDe.java:83)
        at io.confluent.kafka.serializers.AbstractKafkaAvroSerializer.configure(AbstractKafkaAvroSerializer.java:56)
        at io.confluent.kafka.serializers.KafkaAvroSerializer.configure(KafkaAvroSerializer.java:50)
        at org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:378)
        ... 14 more
Caused by: java.lang.ExceptionInInitializerError: Exception java.lang.VerifyError: Bad return type
Exception Details:
  Location:
    com/fasterxml/jackson/databind/cfg/MapperBuilder.streamFactory()Lcom/fasterxml/jackson/core/TokenStreamFactory; @7: areturn
  Reason:
    Type 'com/fasterxml/jackson/core/JsonFactory' (current frame, stack[0]) is not assignable to 'com/fasterxml/jackson/core/TokenStreamFactory' (from method signature)
  Current Frame:
    bci: @7
    flags: { }
    locals: { 'com/fasterxml/jackson/databind/cfg/MapperBuilder' }
    stack: { 'com/fasterxml/jackson/core/JsonFactory' }
  Bytecode:
    0000000: 2ab4 0002 b600 08b0
 [in thread "MyCode"]
        at com.fasterxml.jackson.databind.json.JsonMapper.builder(JsonMapper.java:114)
        at io.confluent.kafka.schemaregistry.utils.JacksonMapper.<clinit>(JacksonMapper.java:27)
        at io.confluent.kafka.schemaregistry.client.rest.RestService.<clinit>(RestService.java:155)
        ... 19 more     

我已经仔细检查过我的java插件版本除了2.13.2之外没有其他版本的fasterxml。关于如何解决或解决此问题的任何想法?我强烈地感觉到这与 tomcat 应用程序从 java 11 到 17 的变化有关......所以我希望也许我可以在startup.xml 中以某种方式调整它。我猜测当应用程序启动时,旧的 FasterXML 版本(2.9.8)会以某种方式产生干扰。

java tomcat java-17
1个回答
0
投票

这看起来像是 “jar hell” 的经典案例,其中应用程序的不同部分及其依赖项需要同一库的不同版本。
在您的情况下,您的插件需要

fasterxml.jackson
版本
2.13.2
,但与 Tomcat 捆绑的供应商应用程序使用旧版本 (
2.9.8
),导致兼容性问题和您遇到的运行时异常。类路径优先级决定加载旧版本的库而不是您的插件所需的版本。

Tomcat
└───lib
    └───jackson-*-2.9.8 (Vendor libs, older version)
    └───other vendor libs
Plugins
└───your-plugin.jar
    └───jackson-*-2.13.2 (Your libs, required version)

如果可能,修改您的插件以使用自定义类加载器,该加载器优先考虑您的捆绑库而不是供应商应用程序提供的库。但这可能不受供应商框架的直接支持。

既然您提到您可以自行承担修改

startup.xml
的风险,您可以尝试调整类路径,以便在供应商的库之前更喜欢从插件的 jar 文件加载类。这可能涉及指定类加载器属性,例如插件上下文的
parentFirst="false"
。但是,这可能不可行或不可靠,具体取决于供应商应用程序和 Tomcat 如何管理类加载。

相反,您可以使用 Maven 的 Shade Plugin 创建插件的阴影(或超级)jar,它将特定版本的 Jackson 库重新定位到不同的包命名空间。这样,您的插件将使用其 Jackson 版本,而不会与供应商的版本发生冲突。然而,这需要仔细处理,以确保对重定位的类的all引用得到相应更新。

举个例子,您的

pom.xml
是:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.4</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <relocations>
                            <relocation>
                                <pattern>com.fasterxml.jackson</pattern>
                                <shadedPattern>your.custom.package.jackson</shadedPattern>
                            </relocation>
                        </relocations>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

该设置会将所有

com.fasterxml.jackson
类重新定位到
your.custom.package.jackson
,允许您的插件使用其 Jackson 版本,而不会与供应商的版本冲突。您将更新代码以相应地使用新的包名称。

假设您已在

pom.xml
中配置了 Maven Shade 插件以将
com.fasterxml.jackson
重新定位到
com.mycompany.shaded.jackson

遮光前

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;

public class Example {
    public static void main(String[] args) {
        ObjectMapper mapper = new ObjectMapper();
        MyObject myObject = new MyObject();
        try {
            String json = mapper.writeValueAsString(myObject);
            System.out.println(json);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

调整后

import com.mycompany.shaded.jackson.databind.ObjectMapper;
import com.mycompany.shaded.jackson.core.JsonProcessingException;

public class Example {
    public static void main(String[] args) {
        // Notice the import statements have changed to the shaded package
        com.mycompany.shaded.jackson.databind.ObjectMapper mapper = new com.mycompany.shaded.jackson.databind.ObjectMapper();
        MyObject myObject = new MyObject();
        try {
            String json = mapper.writeValueAsString(myObject);
            System.out.println(json);
        } catch (com.mycompany.shaded.jackson.core.JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.