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

在Axis2中获取SOAPBody字符串时遇NodeUnavailableException求助

解决Axis2中获取SOAPBody字符串时的NodeUnavailableException问题

问题场景

在使用Axis2和Tomcat9开发Web服务时,尝试通过以下代码获取SOAPBody的字符串形式:

public static String getXMLSoapBody()
{
   log.getLogger().debug("TEST - getXmlSoapBody - START");
   String soapBody;
   SOAPBody body;
   MessageContext messageContext = MessageContext.getCurrentMessageContext();
   try {
      SOAPEnvelope env  = messageContext.getEnvelope();
      body = env.getBody();
   }
   catch(Exception e)
   {
      log.getLogger().error("SOAP Exception :" + e.toString());
      e.printStackTrace();

      return null;
   }

   soapBody = body.toString();

return soapBody;
}

执行后抛出NodeUnavailableException,完整栈信息如下:

org.apache.axiom.om.NodeUnavailableException
    at org.apache.axiom.om.impl.common.AxiomExceptionTranslator.translate(AxiomExceptionTranslator.java:35)
    at org.apache.axiom.om.impl.llom.AxiomContainerImpl.serialize(Unknown Source)
    at org.apache.axiom.om.impl.llom.AxiomContainerImpl.serializeAndSurfaceIOException(Unknown Source)
    at org.apache.axiom.om.impl.llom.AxiomContainerImpl.serialize(Unknown Source)
    at org.apache.axiom.om.impl.llom.AxiomContainerImpl.serialize(Unknown Source)
    at org.apache.axiom.om.impl.llom.AxiomContainerImpl.serialize(Unknown Source)
    at org.apache.axiom.om.impl.llom.AxiomElementImpl.toString(Unknown Source)
    at uk.co.celesio.orders.server.TransUtils.getXMLSoapBody(TransUtils.java:304)

同时伴随Tomcat日志报错:

10:41:26.935 [http-nio-8080-exec-2] ERROR org.apache.axis2.transport.http.AxisServlet - processAxisFault() found a null HTTP status from the MessageContext instance, setting HttpServletResponse status to: axis2.http.response.state

测试请求示例:

<?xml version='1.0' encoding='windows-1252'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soapenv:Body>
    <MyTestRequest xmlns="...">
      ...
    </MyTestRequest>
  </soapenv:Body>
</soapenv:Envelope>

原因分析

NodeUnavailableException本质是Axis2默认采用的Axiom流式XML处理模型导致的:

  • Axis2为优化性能,默认以流方式解析SOAP消息,解析完成后底层XML输入流会被关闭或释放
  • 当业务代码中延迟调用body.toString()时,SOAPBody对应的OM节点已无法访问底层流数据,触发该异常

解决方案

方案1:使用Axiom序列化API直接生成字符串

避免直接调用toString(),改用Axiom提供的序列化工具类,确保在节点可用时完成序列化:

import org.apache.axiom.om.OMElement;
import org.apache.axis2.context.MessageContext;

import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public static String getXMLSoapBody() {
    log.getLogger().debug("TEST - getXmlSoapBody - START");
    MessageContext messageContext = MessageContext.getCurrentMessageContext();
    try {
        SOAPBody body = messageContext.getEnvelope().getBody();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        // 使用Axiom序列化API输出SOAPBody内容
        body.serialize(baos);
        return baos.toString();
    } catch (XMLStreamException | IOException e) {
        log.getLogger().error("获取SOAPBody失败: " + e.getMessage(), e);
        return null;
    }
}

方案2:提前缓存SOAP消息(推荐大消息场景)

在消息处理的早期阶段(如In拦截器)缓存完整SOAP消息,后续直接从缓存读取,避免重复访问流数据:

  1. 自定义缓存拦截器:
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.handlers.AbstractHandler;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class SoapBodyCacheInterceptor extends AbstractHandler {
    @Override
    public InvocationResponse invoke(MessageContext msgContext) throws AxisFault {
        try {
            InputStream in = msgContext.getEnvelope().getXMLStreamReader().getInputSource().getByteStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            while ((len = in.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            // 将缓存的SOAP消息存入MessageContext
            msgContext.setProperty("CACHED_SOAP_BODY", baos.toString());
            // 重置输入流,确保后续处理能正常读取
            msgContext.getEnvelope().getXMLStreamReader().getInputSource().setByteStream(new ByteArrayInputStream(baos.toByteArray()));
        } catch (IOException e) {
            throw new AxisFault("缓存SOAP消息失败", e);
        }
        return InvocationResponse.CONTINUE;
    }
}
  1. 在axis2.xml中配置拦截器(放入PreDispatch阶段):
<phase name="PreDispatch" phaseOrder="First">
    <handler name="SoapBodyCacheInterceptor" class="your.package.SoapBodyCacheInterceptor"/>
</phase>
  1. 业务代码中直接读取缓存:
public static String getXMLSoapBody() {
    log.getLogger().debug("TEST - getXmlSoapBody - START");
    MessageContext messageContext = MessageContext.getCurrentMessageContext();
    return (String) messageContext.getProperty("CACHED_SOAP_BODY");
}

方案3:修改Axis2消息处理模式(不推荐大消息场景)

通过配置强制Axiom使用缓存模式,关闭流式处理:
在axis2.xml中添加参数:

<parameter name="enableOMBuilderCache">true</parameter>

该方式会增加内存消耗,仅适合小消息场景。

内容的提问来源于stack exchange,提问作者Bohdan Hrytsai

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 12:55:25