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

JBoss和Resteasy:反序列化java序列化对象时ClassNotFoundException,java,serialization,jboss,resteasy,activiti,Java,Serialization,Jboss,Resteasy,Activiti,上下文:我们使用Activiti作为流程引擎,使用ActivitiREST作为应用程序的接口。因为这个问题与REST服务返回Java序列化的对象有关,所以我没有将此添加到标题中 场景:我们有一个JBoss Wildfly实例,它包含一个EAR和一个包含类“ProcessContext”的模块(我们称之为X以供参考)。Activiti在这个EAR中运行,ServiceTasks(从进程调用Java代码片段来完成一些工作)依赖于这个类。他们使用这个类来实例化一个流程变量并向其中添加一些数据 我们有第

上下文:我们使用Activiti作为流程引擎,使用ActivitiREST作为应用程序的接口。因为这个问题与REST服务返回Java序列化的对象有关,所以我没有将此添加到标题中

场景:我们有一个JBoss Wildfly实例,它包含一个EAR和一个包含类“ProcessContext”的模块(我们称之为X以供参考)。Activiti在这个EAR中运行,ServiceTasks(从进程调用Java代码片段来完成一些工作)依赖于这个类。他们使用这个类来实例化一个流程变量并向其中添加一些数据

我们有第二个部署(WAR,目前在同一个Wildfly实例上,但稍后在远程服务器上),它通过RESTAPI访问Activiti,现在我们需要访问“ProcessContext”数据。这场战争还依赖于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模块的模块类加载器,而不是我的本地(模块)类加载器

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

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


有没有办法解决这个问题?

好的,最后我找到了一个解决方案,编写了一个自己的提供者,并以编程方式将其注册到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);
    }
}
现在我可以使用如下配置:

ResteasyProviderFactory factory = new ResteasyProviderFactory();
factory.register(new ActivitiObjectMessageBodyReader());
Configuration configuration = new ClientConfiguration(factory);
Client client = ClientBuilder.newClient(configuration);
Providers current = ResteasyProviderFactory.getContextData(Providers.class);
ResteasyProviderFactory.pushContext(Providers.class, configuration);
哇,Java对象变量是通过REST检索的

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

问题与resteasy中的提供者工厂的加载有关。我对其进行了调试,看到它将提供程序与配置的提供程序进行了交换,如下所示:

ResteasyProviderFactory factory = new ResteasyProviderFactory();
factory.register(new ActivitiObjectMessageBodyReader());
Configuration configuration = new ClientConfiguration(factory);
Client client = ClientBuilder.newClient(configuration);
Providers current = ResteasyProviderFactory.getContextData(Providers.class);
ResteasyProviderFactory.pushContext(Providers.class, configuration);
当使用web.xml或RegisterBuiltin.register(factory)全局注册我的提供程序时,在上述调用之后可以在current中找到该提供程序,但current不用于反序列化。取而代之的是使用配置,这有一个父工厂链,导致工厂在Resteasy模块中初始化,该模块不包含我的提供程序,并且由于它自己的类加载器而找不到我的类。 是的,我在web.xml中启用了例如resteasy.scan,但它没有帮助


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

将原始Jboss SerializableProvider复制并粘贴到应用程序中,并在web.xml中声明它


resteasy.scan.providers
真的