JBoss和Resteasy:反序列化java序列化对象时的ClassNotFoundException

上下文:我们使用Activiti作为流程引擎,Activiti-Rest作为其应用程序的接口.由于问题与返回由Java序列化的对象的REST服务有关,我没有将其添加到标题中.

场景:我们有一个JBoss Wildfly实例,它包含一个带有模块的EAR(让我们称之为X作为参考),它包含一个类“ProcessContext”. Activiti在这个EAR中运行,而ServiceTasks(从进程中调用Java-snippets来做一些工作)依赖于该类.他们使用此类来实例化流程变量并向其添加一些数据.

我们有第二个部署(一个WAR,当前在同一个Wildfly实例上但稍后在远程服务器上),它通过其REST api访问Activiti,现在我们需要访问“ProcessContext”数据.此WAR还依赖于X,其类加载器可以毫无问题地解析“ProcessContext”.

好的.这样做似乎很简单.呼叫:

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

这将返回MediaType“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而不是我的本地(模块)类加载器.

一种解决方案可能是编写一个包含“ProcessContext”的模块,并使其在JBoss中全局知晓,但这是项目负责人拒绝的一些基础设施决策.

难道Resteasy不应该使用调用者的类加载器而不是模块类加载器吗?调用者知道它所需的类,如果我可以得到响应内部输入流,我可以自己反序列化它没有任何问题.我真的很想知道这是一个错误还是一个功能.

任何想法如何解决这个问题?

解决方法:

好的,最后我通过编写自己的提供程序并以编程方式将其注册到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);

哇,通过REST检索Java对象变量.

—–你可以在这里停止阅读,除非你想知道我认为是什么问题的原因—–

问题与重新安装内的提供商工厂的加载有关.我调试了它,并看到它交换提供程序对配置的提供程序,如下所示:

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

使用web.xml或RegisterBuiltin.register(factory)全局注册我的提供程序时,可以在上述调用之后找到当前提供程序,但当前不用于反序列化.相反,使用了配置,并且它有一系列父工厂,导致工厂在Resteasy模块中初始化,由于它自己的类加载器,它不包含我的提供者并且找不到我的类.
是的,我有例如在web.xml中启用了resteasy.scan,但它没有帮助.

当将工厂提供给客户端时,它会将其注入响应构造函数,现在提供程序在序列化时可用,使用我自己的模块的类加载器(因为它被注册为bean),这会产生所需的反序列化对象.

上一篇:2021-10-01


下一篇:Activiti工作流