升级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); } } } }
问题分析
- NullPointerException原因:Axiom 1.4.0对MTOM序列化的校验更严格,仅调用
omt.setOptimize(true)但未关联有效DataHandler的节点,在序列化时会触发NPE——OMMultipartWriter尝试写入part时无法获取有效的数据源。 - 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
相关产品推荐
相关产品推荐

