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

升级axiom-impl至1.4.0.jar后SOAP MTOM功能抛出空指针异常

Axiom 1.4.0升级后SOAP-MTOM抛出NullPointerException的解决方法

我将axiom-impl-1.2.20.jar及相关Axiom系列jar包升级至axiom-impl-1.4.0.jar后,现有SOAP-MTOM功能代码抛出NullPointerException异常,堆栈信息如下:

Caused by: java.lang.NullPointerException
    at org.apache.axiom.om.impl.OMMultipartWriter.writePart(OMMultipartWriter.java:165)
    at org.apache.axiom.om.impl.OMMultipartWriter.writePart(OMMultipartWriter.java:183)
    at org.apache.axiom.om.impl.llom.AxiomContainerImpl.serialize(Unknown Source)
    at org.apache.axiom.om.impl.llom.AxiomContainerImpl.serializeAndConsume(Unknown Source)

我尝试通过注入DataHandler修复(见enableMTOM4SpecifiedNodes中的注释代码),但修改后MTOM功能无法正常工作:

主处理代码

String soapVersion = config.soapVersion;
String soapAction = config.soapAction;
soap.getSOAPMessage().saveChanges();//save current changes
MimeHeaders mhs = soap.getMimeHeaders();
String[] contentTypes = mhs.getHeader("Content-Type");
ContentType cType = new ContentType(contentTypes[0]);
String boundary = cType.getParameter("boundary");
if (boundary == null) {
    boundary = DCMimeMessage.getRFC2822MsgID().replace(DCMimeMessage.CH_AT, BCMimeMessage.CH_DOT);
}
String start = cType.getParameter("start");
if (start == null) {
    start = DCMimeMessage.getRFC2822MsgID();//need to be fixed using standard
} else {
    if (start.startsWith("<")) {
        start = start.substring(1);
        if (start.endsWith(">")) {
            start = start.substring(0, start.length()-1);
        }
    }
}

String primaryType = cType.getPrimaryType();
String subType = cType.getSubType();
String startinfo = null;
if (StringUtils.equalsIgnoreCase(primaryType, "multipart")) {
    startinfo = cType.getParameter("type");
} else if (StringUtils.equalsIgnoreCase(primaryType, "application")
    && StringUtils.equalsIgnoreCase(subType, "soap+xml")) {
    startinfo = DCSoapConstants.BCSOAP_12_CONTENTTYPE;
} else if (StringUtils.equalsIgnoreCase(primaryType, "text")
        && StringUtils.equalsIgnoreCase(subType, "xml")) {
    startinfo = DCSoapConstants.BCSOAP_11_CONTENTTYPE;
}
if (startinfo == null) {
    if (StringUtils.equalsIgnoreCase(soapVersion, SOAPConstants.SOAP_1_1_PROTOCOL)) {
        startinfo = DCSoapConstants.BCSOAP_11_CONTENTTYPE;
    } else {
        startinfo = DCSoapConstants.BCSOAP_12_CONTENTTYPE;
    }
}
startinfo = "start-info=\"" + startinfo + "\"";
if(StringUtils.equalsIgnoreCase(soapVersion, SOAPConstants.SOAP_1_2_PROTOCOL)
        && soapAction != null) {
    startinfo = startinfo + "; action=\"" + soapAction + "\"";
}

mmh.addHeader("Content-Type", "Multipart/Related;boundary=\"" 
        + boundary + "\";type=\"application/xop+xml\";start=\"<" 
        + start + ">\";" + startinfo);
SOAPEnvelope env = soap.getSOAPEnvelope();
org.apache.axiom.soap.SOAPEnvelope omEnv = null;
if (env instanceof org.apache.axis2.saaj.SOAPEnvelopeImpl) {
    omEnv = saaj.toOM(env);
} else if (env instanceof org.apache.axiom.soap.SOAPEnvelope) {
    omEnv = (org.apache.axiom.soap.SOAPEnvelope)env;
} else {
    return null;
}

if (omEnv == null) {
    return null;
}
omEnv.declareNamespace("http://www.w3.org/2004/08/xop/include", "xop");
Iterator children = omEnv.getBody().getChildElements();
if (children != null && children.hasNext()) {
    HashMap<String, Boolean> tagNamesUsedMap = new HashMap<String, Boolean>();
    for (int i=0, s=tagNames.size(); i<s; i++) {
        tagNamesUsedMap.put(tagNames.get(i), false);
    }
    enableMTOM4SpecifiedNodes(children, tagNamesUsedMap);
    List<String> notUsedTagNames = getNotUsedTagNames(tagNamesUsedMap);
    if (!notUsedTagNames.isEmpty()) {
        throw new D2CException("tagNames is given, but some of the items is not used : " + notUsedTagNames);
    }
} else {
    if (tagNames != null) {
        throw new D2CException("tagNames is given, but there isn't any payload found.");
    }
}
OMOutputFormat format = new OMOutputFormat();
format.setDoOptimize(true);
format.setSOAP11(StringUtils.equalsIgnoreCase(soapVersion, SOAPConstants.SOAP_1_1_PROTOCOL));
format.setMimeBoundary(boundary);
if (start != null) {
    format.setRootContentId(start);
}

ByteArrayOutputStream baos = new ByteArrayOutputStream();
omEnv.serializeAndConsume(baos, format);

enableMTOM4SpecifiedNodes方法

public static void enableMTOM4SpecifiedNodes(Iterator iter, HashMap<String, Boolean> tagNames) {
    while (iter.hasNext()) {
        Object obj = iter.next();
        if (obj instanceof OMElement) {
            OMElement ome = (OMElement) obj;
            OMFactory fac = OMAbstractFactory.getOMFactory();
            Iterator children = ome.getChildElements();
            if (children == null || !children.hasNext()) {
                String nodeName = ome.getLocalName();
                if (tagNames.containsKey(nodeName)) {
                    tagNames.put(nodeName, true);
                    Iterator niter = ome.getChildren();
                    while (niter.hasNext()) {
                        Object nobj = niter.next();
                        if (nobj instanceof OMText) {
                            OMText omt = (OMText)nobj;
//                          byte[] bytes = new String(omt.getTextCharacters()).getBytes(StandardCharsets.UTF_8);
//                          ByteArrayDataSource rawData = new ByteArrayDataSource(bytes);
//                          DataHandler datahandler = new DataHandler(rawData);
//                          omt = fac.createOMText(datahandler, true);
                            omt.setOptimize(true);
                        }
                    }
                }
            } else {
                enableMTOM4SpecifiedNodes(children, tagNames);
            }
        }
    }
}

问题分析

  1. NullPointerException原因:Axiom 1.4.0对MTOM序列化的校验更严格,仅调用omt.setOptimize(true)但未关联有效DataHandler的节点,在序列化时会触发NPE——OMMultipartWriter尝试写入part时无法获取有效的数据源。
  2. MTOM失效原因:注释代码中创建新的OMText节点后,仅赋值给局部变量omt,并未替换DOM树中的原节点,因此修改无法生效。

修复方案

修改enableMTOM4SpecifiedNodes方法,确保每个要优化的节点都关联DataHandler,并替换原节点:

public static void enableMTOM4SpecifiedNodes(Iterator iter, HashMap<String, Boolean> tagNames) {
    while (iter.hasNext()) {
        Object obj = iter.next();
        if (obj instanceof OMElement) {
            OMElement ome = (OMElement) obj;
            OMFactory fac = OMAbstractFactory.getOMFactory();
            Iterator children = ome.getChildElements();
            if (children == null || !children.hasNext()) {
                String nodeName = ome.getLocalName();
                if (tagNames.containsKey(nodeName)) {
                    tagNames.put(nodeName, true);
                    Iterator niter = ome.getChildren();
                    // 先收集所有需要替换的节点,避免迭代时修改结构引发异常
                    List<OMNode> nodesToReplace = new ArrayList<>();
                    while (niter.hasNext()) {
                        Object nobj = niter.next();
                        if (nobj instanceof OMText) {
                            nodesToReplace.add((OMNode) nobj);
                        }
                    }
                    // 替换每个符合条件的OMText节点
                    for (OMNode oldNode : nodesToReplace) {
                        OMText oldOmt = (OMText) oldNode;
                        // 将文本内容转换为带MIME类型的DataSource
                        byte[] bytes = new String(oldOmt.getTextCharacters()).getBytes(StandardCharsets.UTF_8);
                        ByteArrayDataSource rawData = new ByteArrayDataSource(bytes, "text/plain");
                        DataHandler datahandler = new DataHandler(rawData);
                        // 创建优化的OMText节点
                        OMText newOmt = fac.createOMText(datahandler, true);
                        // 替换原节点
                        oldNode.replace(newOmt);
                    }
                }
            } else {
                enableMTOM4SpecifiedNodes(children, tagNames);
            }
        }
    }
}

关键修改点

  • 先收集所有需要替换的OMText节点,避免迭代时直接修改元素子节点导致的并发修改异常
  • 创建ByteArrayDataSource时指定MIME类型(示例用text/plain,可根据实际内容调整为对应类型)
  • 使用replace方法将新节点替换原节点,确保修改生效
  • 每个要优化的节点都关联了有效的DataHandler,从根源解决NPE问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:42:00