You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebLogic 12c中获取SoapFaultClientException详情时遇流关闭错误

WebLogic下SOAP Fault解析报“Stream closed”问题解决

问题场景

解析SOAP Fault的detail节点内容时,代码在Tomcat环境运行正常,但部署到WebLogic环境时抛出com.ctc.wstx.exc.WstxIOException: Stream closed错误。

服务器返回的SOAP Fault响应

<S:Fault>
     <faultcode>S:Server</faultcode>
     <faultstring>AuthException</faultstring>
     <detail>
        <ns2:AuthException xmlns:ns2="url...">
           <code>0</code>
           <failCause>INVALID_AMOUNT</failCause>
           <message>AuthException</message>
        </ns2:AuthException>
     </detail>
  </S:Fault>

报错代码

catch (SoapFaultClientException e) {
    SoapFaultDetail soapFaultDetail = e.getSoapFault().getFaultDetail(); 
    if (soapFaultDetail == null) {
        throw e;
    }
    SoapFaultDetailElement detailElementChild = soapFaultDetail.getDetailEntries().next();
    Source detailSource = detailElementChild.getSource();
    Object detail = webServiceTemplate.getUnmarshaller().unmarshal(detailSource);
    JAXBElement<serviceException> source = (JAXBElement<serviceException>)detail;
    System.out.println("failCause:"+source.getFailCause());  
}

问题原因与解决方案

WebLogic对SOAP消息的处理机制和Tomcat存在差异:当调用detailElementChild.getSource()后,对应的输入流可能被WebLogic提前关闭或已被内部读取过,导致后续反序列化时流无法访问。以下是两种可行的解决方法:

方案一:转换为DOMSource解析

将Source转换为DOM节点,再基于DOMSource进行反序列化,避免流被提前关闭:

catch (SoapFaultClientException e) {
    SoapFaultDetail soapFaultDetail = e.getSoapFault().getFaultDetail(); 
    if (soapFaultDetail == null) {
        throw e;
    }
    SoapFaultDetailElement detailElementChild = soapFaultDetail.getDetailEntries().next();
    // 提取DOM节点并构建新的DOMSource
    Node node = ((DOMSource) detailElementChild.getSource()).getNode();
    DOMSource domSource = new DOMSource(node);
    
    Object detail = webServiceTemplate.getUnmarshaller().unmarshal(domSource);
    JAXBElement<serviceException> source = (JAXBElement<serviceException>)detail;
    // 注意:需通过getValue()获取实体对象后调用方法
    System.out.println("failCause:" + source.getValue().getFailCause());  
}

方案二:缓存流内容为字符串

如果Source不是DOMSource,先将流内容读取为字符串,再用StringSource进行解析:

catch (SoapFaultClientException e) {
    SoapFaultDetail soapFaultDetail = e.getSoapFault().getFaultDetail(); 
    if (soapFaultDetail == null) {
        throw e;
    }
    SoapFaultDetailElement detailElementChild = soapFaultDetail.getDetailEntries().next();
    Source detailSource = detailElementChild.getSource();
    
    // 将Source内容转换为字符串
    StringWriter writer = new StringWriter();
    try {
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(detailSource, new StreamResult(writer));
    } catch (TransformerException ex) {
        throw new RuntimeException("Failed to transform SOAP fault detail", ex);
    }
    String detailXml = writer.toString();
    
    // 使用StringSource反序列化
    StringSource stringSource = new StringSource(detailXml);
    Object detail = webServiceTemplate.getUnmarshaller().unmarshal(stringSource);
    JAXBElement<serviceException> source = (JAXBElement<serviceException>)detail;
    System.out.println("failCause:" + source.getValue().getFailCause());  
}

额外注意事项

  1. 确保JAXB实体类serviceException与SOAP响应中的ns2:AuthException节点正确映射,包括命名空间的注解配置。
  2. 原代码中直接调用source.getFailCause()是错误的,JAXBElement本身没有该方法,必须通过source.getValue()获取实际的实体对象后再调用对应方法。

内容的提问来源于stack exchange,提问作者Behrouz Takhti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:36:31