JBoss和Resteasy:反序列化Java序列化对象时出现ClassNotFoundException异常

4

背景:我们正在使用Activiti作为流程引擎,使用Activiti-Rest作为接口与我们的应用程序进行交互。由于问题涉及到REST服务返回由Java序列化的对象,所以我没有将其添加到标题中。

场景:我们有一个包含模块(我们称之为X)的JBoss Wildfly实例,该模块包含一个名为“ProcessContext”的类。Activiti在此EAR内运行,并且ServiceTasks(从流程中调用的Java代码段来执行一些任务)依赖于该类。它们使用此类来实例化流程变量并向其中添加一些数据。

我们有第二个部署(一个WAR,当前在同一台Wildfly实例上,但稍后移到远程服务器上),通过其REST API访问Activiti,并且现在我们需要访问“ProcessContext”数据。这个WAR也依赖于X,并且其类加载器可以轻松解析“ProcessContext”。

好的,很好。这似乎很简单。调用:

GET history/historic-process-instances/{processInstanceId}/variables/{variableName}/data

这将返回一个媒体类型为"application/x-java-serialized-object"的响应,使用调试器检查似乎没有问题。但是当我试图反序列化对象时,却遇到了以下错误:
Caused by: java.lang.ClassNotFoundException: xxx.commons.metadata.ProcessMetadata from [Module "org.jboss.resteasy.resteasy-jaxrs:main" from local module loader @103f852 (finder: local module finder @587c290d (roots: /opt/wildfly/modules,/opt/wildfly/modules/system/layers/base))]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:213) [jboss-modules.jar:1.3.3.Final]
at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:459) [jboss-modules.jar:1.3.3.Final]
at org.jboss.modules.ConcurrentClassLoader.performLoadClassChecked(ConcurrentClassLoader.java:408) [jboss-modules.jar:1.3.3.Final]
at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:389) [jboss-modules.jar:1.3.3.Final]
at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:134) [jboss-modules.jar:1.3.3.Final]
at java.lang.Class.forName0(Native Method) [rt.jar:1.8.0_20]
at java.lang.Class.forName(Class.java:340) [rt.jar:1.8.0_20]
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:626) [rt.jar:1.8.0_20]
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1613) [rt.jar:1.8.0_20]
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518) [rt.jar:1.8.0_20]
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774) [rt.jar:1.8.0_20]
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351) [rt.jar:1.8.0_20]
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371) [rt.jar:1.8.0_20]
at org.jboss.resteasy.plugins.providers.SerializableProvider.readFrom(SerializableProvider.java:76) [resteasy-jaxrs-3.0.10.Final.jar:]
... 131 more

我发现,如果使用的是 Resteasy 模块的 Module-Classloader 而不是本地(模块)类加载器,则用于反序列化对象的 Classloader 会出现问题。
可能的解决方案之一是编写一个包含“ProcessContext”的模块,并使其在 JBoss 中全局可用,但该项目的负责人否决了这个基础设施决策。
难道 Resteasy 不应该使用调用者的类加载器而不是其模块类加载器吗?调用者知道其所需的类,如果我能够获取响应的内部输入流,我就可以自己进行反序列化而不会出现任何问题。我真的很想知道这是一个错误还是一个特性。
有什么解决这个问题的想法吗?
2个回答

2

好的,最终我通过编写自己的提供程序并通过Resteasy客户端程序注册它来找到了解决方法。 我也尝试过通过web.xml实现此目的,但失败了,稍后我会解释。

对于其他遇到类似问题的人,这是我的解决方案。消息正文提供程序:

@Provider
@Consumes("application/x-java-serialized-object")
public class ActivitiObjectMessageBodyReader implements MessageBodyReader<ProcessMetadata> {

@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return (type == ProcessMetadata.class && "application/x-java-serialized-object".equals(mediaType.toString()));
}

@Override
public ProcessMetadata readFrom(Class<ProcessMetadata> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {

    BufferedInputStream bis = new BufferedInputStream(entityStream);
    ObjectInputStream ois = new ObjectInputStream(bis);
    try {
        return ProcessMetadata.class.cast(ois.readObject());
    } catch (ClassNotFoundException e) {
        throw new WebApplicationException(e);
    }
}

我知道,在isReadable中可能由于注释而不需要整个表达式,但是......

编程注册看起来像这样:

ResteasyProviderFactory factory = new ResteasyProviderFactory();
factory.register(new ActivitiObjectMessageBodyReader());
Configuration configuration = new ClientConfiguration(factory);

现在我可以像这样使用配置:
Client client = ClientBuilder.newClient(configuration);

哇,Java对象变量可以通过REST获取。

----- 如果您不想知道我认为问题的原因,请停止阅读 -----

问题与在resteasy中加载提供程序工厂有关。我进行了调试,并看到它像这样交换提供程序和配置的提供程序:

Providers current = ResteasyProviderFactory.getContextData(Providers.class);
ResteasyProviderFactory.pushContext(Providers.class, configuration);

使用web.xml或RegisterBuiltin.register(factory)全局注册提供程序时,上述调用后可以在current中找到该提供程序,但是current不用于反序列化。相反,将使用配置,并且此配置具有一系列父工厂,这些工厂会导致在Resteasy模块中初始化的工厂,其中不包含我的提供程序,因此无法找到我的类,原因是由于其自己的类加载器。
是的,例如我已经在web.xml中启用了resteasy.scan,但这没有帮助。
当向客户端提供工厂时,它将其注入响应构造函数中,现在提供程序可用于序列化时间,使用我自己模块的类加载器(因为它被注册为bean),这会产生所需的反序列化对象。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接