Reinstantiate support for Apache Axiom

This commit restores support for Apache Axiom now that it supports
Jakarta. This commit tries to restore the code that was deleted as-is,
without reviewing the deprecations.

See gh-1454
This commit is contained in:
Stéphane Nicoll
2025-03-13 15:34:12 +01:00
parent 3ac5ba0fc8
commit 60942af529
76 changed files with 5224 additions and 28 deletions

View File

@@ -23,6 +23,15 @@ dependencies {
exclude(group: "commons-logging", module: "commons-logging")
}
optional("org.apache.httpcomponents.client5:httpclient5")
optional("org.apache.ws.commons.axiom:axiom-impl") {
exclude(group: "commons-logging", module: "commons-logging")
}
optional("org.apache.ws.commons.axiom:axiom-compat") {
exclude(group: "commons-logging", module: "commons-logging")
}
optional("org.apache.ws.commons.axiom:axiom-legacy-attachments") {
exclude(group: "commons-logging", module: "commons-logging")
}
optional("org.apache.ws.xmlschema:xmlschema-core")
optional("org.dom4j:dom4j")
optional("org.jdom:jdom2")

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.util.Assert;
import org.springframework.util.xml.StaxUtils;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
/**
* Abstract base class for {@link Payload} implementations.
*
* @author Arjen Poutsma
* @since 2.0
*/
abstract class AbstractPayload extends Payload {
private final SOAPBody axiomBody;
private final SOAPFactory axiomFactory;
protected AbstractPayload(SOAPBody axiomBody, SOAPFactory axiomFactory) {
Assert.notNull(axiomBody, "'axiomBody' must not be null");
Assert.notNull(axiomFactory, "'axiomFactory' must not be null");
this.axiomBody = axiomBody;
this.axiomFactory = axiomFactory;
}
@Override
public final Source getSource() {
try {
OMElement payloadElement = getPayloadElement();
if (payloadElement != null) {
XMLStreamReader streamReader = getStreamReader(payloadElement);
return StaxUtils.createCustomStaxSource(streamReader);
}
else {
return null;
}
}
catch (OMException ex) {
throw new AxiomSoapBodyException(ex);
}
}
protected abstract XMLStreamReader getStreamReader(OMElement payloadElement);
@Override
public final Result getResult() {
AxiomUtils.removeContents(getAxiomBody());
return getResultInternal();
}
protected abstract Result getResultInternal();
public SOAPFactory getAxiomFactory() {
return this.axiomFactory;
}
protected SOAPBody getAxiomBody() {
return this.axiomBody;
}
protected OMElement getPayloadElement() throws OMException {
return getAxiomBody().getFirstElement();
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.IOException;
import java.io.InputStream;
import jakarta.activation.DataHandler;
import org.springframework.util.Assert;
import org.springframework.ws.mime.Attachment;
/**
* Axiom-specific implementation of {@link org.springframework.ws.mime.Attachment}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomAttachment implements Attachment {
private final DataHandler dataHandler;
private final String contentId;
AxiomAttachment(String contentId, DataHandler dataHandler) {
Assert.notNull(contentId, "contentId must not be null");
Assert.notNull(dataHandler, "dataHandler must not be null");
this.contentId = contentId;
this.dataHandler = dataHandler;
}
@Override
public String getContentId() {
return this.contentId;
}
@Override
public String getContentType() {
return this.dataHandler.getContentType();
}
@Override
public InputStream getInputStream() throws IOException {
return this.dataHandler.getInputStream();
}
@Override
public long getSize() {
// Axiom does not support getting the size of attachments.
return -1;
}
@Override
public DataHandler getDataHandler() {
return this.dataHandler;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.mime.AttachmentException;
/**
* Exception thrown when a {@link AxiomAttachment} could not be accessed.
*
* @author Arjen Poutsma
*/
@SuppressWarnings("serial")
public class AxiomAttachmentException extends AttachmentException {
public AxiomAttachmentException(String msg) {
super(msg);
}
public AxiomAttachmentException(String msg, Throwable ex) {
super(msg, ex);
}
public AxiomAttachmentException(Throwable ex) {
super(ex);
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.Locale;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultCode;
import org.apache.axiom.soap.SOAPFaultReason;
import org.apache.axiom.soap.SOAPProcessingException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
import org.springframework.ws.soap.soap11.Soap11Body;
import org.springframework.ws.soap.soap11.Soap11Fault;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.Soap11Body}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoap11Body extends AxiomSoapBody implements Soap11Body {
private final boolean langAttributeOnSoap11FaultString;
AxiomSoap11Body(SOAPBody axiomBody, SOAPFactory axiomFactory, boolean payloadCaching,
boolean langAttributeOnSoap11FaultString) {
super(axiomBody, axiomFactory, payloadCaching);
this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
}
@Override
public Soap11Fault addMustUnderstandFault(String faultString, Locale locale) {
SOAPFault fault = addStandardFault(SOAP11Constants.FAULT_CODE_MUST_UNDERSTAND, faultString, locale);
return new AxiomSoap11Fault(fault, getAxiomFactory());
}
@Override
public Soap11Fault addClientOrSenderFault(String faultString, Locale locale) {
SOAPFault fault = addStandardFault(SOAP11Constants.FAULT_CODE_SENDER, faultString, locale);
return new AxiomSoap11Fault(fault, getAxiomFactory());
}
@Override
public Soap11Fault addServerOrReceiverFault(String faultString, Locale locale) {
SOAPFault fault = addStandardFault(SOAP11Constants.FAULT_CODE_RECEIVER, faultString, locale);
return new AxiomSoap11Fault(fault, getAxiomFactory());
}
@Override
public Soap11Fault addVersionMismatchFault(String faultString, Locale locale) {
SOAPFault fault = addStandardFault(SOAP11Constants.FAULT_CODE_VERSION_MISMATCH, faultString, locale);
return new AxiomSoap11Fault(fault, getAxiomFactory());
}
@Override
public Soap11Fault addFault(QName code, String faultString, Locale faultStringLocale) {
Assert.notNull(code, "No faultCode given");
Assert.hasLength(faultString, "faultString cannot be empty");
if (!StringUtils.hasLength(code.getNamespaceURI())) {
throw new IllegalArgumentException(
"A fault code with namespace and local part must be specific for a custom fault code");
}
if (!this.langAttributeOnSoap11FaultString) {
faultStringLocale = null;
}
try {
AxiomUtils.removeContents(getAxiomBody());
SOAPFault fault = getAxiomFactory().createSOAPFault(getAxiomBody());
SOAPFaultCode faultCode = getAxiomFactory().createSOAPFaultCode(fault);
setValueText(code, fault, faultCode);
SOAPFaultReason faultReason = getAxiomFactory().createSOAPFaultReason(fault);
if (faultStringLocale != null) {
addLangAttribute(faultStringLocale, faultReason);
}
faultReason.setText(faultString);
return new AxiomSoap11Fault(fault, getAxiomFactory());
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapFaultException(ex);
}
}
private void setValueText(QName code, SOAPFault fault, SOAPFaultCode faultCode) {
String prefix = code.getPrefix();
if (StringUtils.hasLength(code.getNamespaceURI()) && StringUtils.hasLength(prefix)) {
OMNamespace namespace = fault.findNamespaceURI(prefix);
if (namespace == null) {
fault.declareNamespace(code.getNamespaceURI(), prefix);
}
}
else if (StringUtils.hasLength(code.getNamespaceURI())) {
OMNamespace namespace = fault.findNamespace(code.getNamespaceURI(), null);
if (namespace == null) {
namespace = fault.declareNamespace(code.getNamespaceURI(), "");
}
code = new QName(code.getNamespaceURI(), code.getLocalPart(), namespace.getPrefix());
}
faultCode.setText(code);
}
private SOAPFault addStandardFault(String localName, String faultString, Locale locale) {
Assert.notNull(faultString, "No faultString given");
try {
AxiomUtils.removeContents(getAxiomBody());
SOAPFault fault = getAxiomFactory().createSOAPFault(getAxiomBody());
SOAPFaultCode faultCode = getAxiomFactory().createSOAPFaultCode(fault);
faultCode.setText(
new QName(fault.getNamespace().getNamespaceURI(), localName, fault.getNamespace().getPrefix()));
SOAPFaultReason faultReason = getAxiomFactory().createSOAPFaultReason(fault);
if (locale != null) {
addLangAttribute(locale, faultReason);
}
faultReason.setText(faultString);
return fault;
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapFaultException(ex);
}
}
private void addLangAttribute(Locale locale, SOAPFaultReason faultReason) {
OMNamespace xmlNamespace = getAxiomFactory().createOMNamespace("http://www.w3.org/XML/1998/namespace", "xml");
OMAttribute langAttribute = getAxiomFactory().createOMAttribute("lang", xmlNamespace,
AxiomUtils.toLanguage(locale));
faultReason.addAttribute(langAttribute);
}
@Override
public Soap11Fault getFault() {
SOAPFault axiomFault = getAxiomBody().getFault();
return (axiomFault != null) ? new AxiomSoap11Fault(axiomFault, getAxiomFactory()) : null;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.Locale;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFault;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
import org.springframework.ws.soap.soap11.Soap11Fault;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.Soap11Fault}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoap11Fault extends AxiomSoapFault implements Soap11Fault {
AxiomSoap11Fault(SOAPFault axiomFault, SOAPFactory axiomFactory) {
super(axiomFault, axiomFactory);
}
@Override
public QName getFaultCode() {
return getAxiomFault().getCode().getTextAsQName();
}
@Override
public String getFaultStringOrReason() {
if (getAxiomFault().getReason() != null) {
return getAxiomFault().getReason().getText();
}
return null;
}
@Override
public Locale getFaultStringLocale() {
if (getAxiomFault().getReason() != null) {
OMAttribute langAttribute = getAxiomFault().getReason()
.getAttribute(new QName("http://www.w3.org/XML/1998/namespace", "lang"));
if (langAttribute != null) {
String xmlLangString = langAttribute.getAttributeValue();
if (xmlLangString != null) {
return AxiomUtils.toLocale(xmlLangString);
}
}
}
return null;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.apache.axiom.soap.RolePlayer;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.springframework.util.ObjectUtils;
import org.springframework.ws.soap.SoapHeaderElement;
import org.springframework.ws.soap.soap11.Soap11Header;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.Soap11Header}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoap11Header extends AxiomSoapHeader implements Soap11Header {
AxiomSoap11Header(SOAPHeader axiomHeader, SOAPFactory axiomFactory) {
super(axiomHeader, axiomFactory);
}
@Override
@SuppressWarnings("unchecked")
public Iterator<SoapHeaderElement> examineHeaderElementsToProcess(final String[] actors) {
RolePlayer rolePlayer = null;
if (!ObjectUtils.isEmpty(actors)) {
rolePlayer = new RolePlayer() {
public List<String> getRoles() {
return Arrays.asList(actors);
}
public boolean isUltimateDestination() {
return false;
}
};
}
Iterator<SOAPHeaderBlock> result = (Iterator<SOAPHeaderBlock>) getAxiomHeader().getHeadersToProcess(rolePlayer);
return new AxiomSoapHeaderElementIterator(result);
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.Locale;
import javax.xml.namespace.QName;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultCode;
import org.apache.axiom.soap.SOAPFaultReason;
import org.apache.axiom.soap.SOAPFaultText;
import org.apache.axiom.soap.SOAPFaultValue;
import org.apache.axiom.soap.SOAPProcessingException;
import org.springframework.util.Assert;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
import org.springframework.ws.soap.soap12.Soap12Body;
import org.springframework.ws.soap.soap12.Soap12Fault;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.Soap12Body}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoap12Body extends AxiomSoapBody implements Soap12Body {
AxiomSoap12Body(SOAPBody axiomBody, SOAPFactory axiomFactory, boolean payloadCaching) {
super(axiomBody, axiomFactory, payloadCaching);
}
@Override
public Soap12Fault addMustUnderstandFault(String reason, Locale locale) {
Assert.notNull(locale, "No locale given");
SOAPFault fault = addStandardFault(SOAP12Constants.FAULT_CODE_MUST_UNDERSTAND, reason, locale);
return new AxiomSoap12Fault(fault, getAxiomFactory());
}
@Override
public Soap12Fault addClientOrSenderFault(String reason, Locale locale) {
Assert.notNull(locale, "No locale given");
SOAPFault fault = addStandardFault(SOAP12Constants.FAULT_CODE_SENDER, reason, locale);
return new AxiomSoap12Fault(fault, getAxiomFactory());
}
@Override
public Soap12Fault addServerOrReceiverFault(String reason, Locale locale) {
Assert.notNull(locale, "No locale given");
SOAPFault fault = addStandardFault(SOAP12Constants.FAULT_CODE_RECEIVER, reason, locale);
return new AxiomSoap12Fault(fault, getAxiomFactory());
}
@Override
public Soap12Fault addVersionMismatchFault(String reason, Locale locale) {
Assert.notNull(locale, "No locale given");
SOAPFault fault = addStandardFault(SOAP12Constants.FAULT_CODE_VERSION_MISMATCH, reason, locale);
return new AxiomSoap12Fault(fault, getAxiomFactory());
}
@Override
public Soap12Fault addDataEncodingUnknownFault(QName[] subcodes, String reason, Locale locale) {
Assert.notNull(locale, "No locale given");
SOAPFault fault = addStandardFault(SOAP12Constants.FAULT_CODE_DATA_ENCODING_UNKNOWN, reason, locale);
return new AxiomSoap12Fault(fault, getAxiomFactory());
}
private SOAPFault addStandardFault(String localName, String faultStringOrReason, Locale locale) {
Assert.notNull(faultStringOrReason, "No faultStringOrReason given");
try {
AxiomUtils.removeContents(getAxiomBody());
SOAPFault fault = getAxiomFactory().createSOAPFault(getAxiomBody());
SOAPFaultCode code = getAxiomFactory().createSOAPFaultCode(fault);
SOAPFaultValue value = getAxiomFactory().createSOAPFaultValue(code);
value.setText(fault.getNamespace().getPrefix() + ":" + localName);
SOAPFaultReason reason = getAxiomFactory().createSOAPFaultReason(fault);
SOAPFaultText text = getAxiomFactory().createSOAPFaultText(reason);
if (locale != null) {
text.setLang(AxiomUtils.toLanguage(locale));
}
text.setText(faultStringOrReason);
return fault;
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapFaultException(ex);
}
}
@Override
public Soap12Fault getFault() {
SOAPFault axiomFault = getAxiomBody().getFault();
return (axiomFault != null) ? new AxiomSoap12Fault(axiomFault, getAxiomFactory()) : null;
}
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultCode;
import org.apache.axiom.soap.SOAPFaultNode;
import org.apache.axiom.soap.SOAPFaultReason;
import org.apache.axiom.soap.SOAPFaultSubCode;
import org.apache.axiom.soap.SOAPFaultText;
import org.apache.axiom.soap.SOAPFaultValue;
import org.apache.axiom.soap.SOAPProcessingException;
import org.springframework.util.StringUtils;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
import org.springframework.ws.soap.soap12.Soap12Fault;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.Soap12Fault}.
*
* @author Arjen Poutsma
*/
class AxiomSoap12Fault extends AxiomSoapFault implements Soap12Fault {
AxiomSoap12Fault(SOAPFault axiomFault, SOAPFactory axiomFactory) {
super(axiomFault, axiomFactory);
}
@Override
public QName getFaultCode() {
return getAxiomFault().getCode().getValue().getTextAsQName();
}
@Override
public Iterator<QName> getFaultSubcodes() {
List<QName> subcodes = new ArrayList<QName>();
SOAPFaultSubCode subcode = getAxiomFault().getCode().getSubCode();
while (subcode != null) {
subcodes.add(subcode.getValue().getTextAsQName());
subcode = subcode.getSubCode();
}
return subcodes.iterator();
}
@Override
public void addFaultSubcode(QName subcode) {
SOAPFaultCode faultCode = getAxiomFault().getCode();
SOAPFaultSubCode faultSubCode = null;
if (faultCode.getSubCode() == null) {
faultSubCode = getAxiomFactory().createSOAPFaultSubCode(faultCode);
}
else {
faultSubCode = faultCode.getSubCode();
while (true) {
if (faultSubCode.getSubCode() != null) {
faultSubCode = faultSubCode.getSubCode();
}
else {
faultSubCode = getAxiomFactory().createSOAPFaultSubCode(faultSubCode);
break;
}
}
}
SOAPFaultValue faultValue = getAxiomFactory().createSOAPFaultValue(faultSubCode);
setValueText(subcode, faultValue);
}
private void setValueText(QName code, SOAPFaultValue faultValue) {
String prefix = code.getPrefix();
if (StringUtils.hasLength(code.getNamespaceURI()) && StringUtils.hasLength(prefix)) {
OMNamespace namespace = getAxiomFault().findNamespaceURI(prefix);
if (namespace == null) {
getAxiomFault().declareNamespace(code.getNamespaceURI(), prefix);
}
}
else if (StringUtils.hasLength(code.getNamespaceURI())) {
OMNamespace namespace = getAxiomFault().findNamespace(code.getNamespaceURI(), null);
if (namespace == null) {
throw new IllegalArgumentException("Could not resolve namespace of code [" + code + "]");
}
code = new QName(code.getNamespaceURI(), code.getLocalPart(), namespace.getPrefix());
}
faultValue.setText(prefix + ":" + code.getLocalPart());
}
@Override
public String getFaultNode() {
SOAPFaultNode faultNode = getAxiomFault().getNode();
if (faultNode == null) {
return null;
}
else {
return faultNode.getFaultNodeValue();
}
}
@Override
public void setFaultNode(String uri) {
try {
SOAPFaultNode faultNode = getAxiomFactory().createSOAPFaultNode(getAxiomFault());
faultNode.setFaultNodeValue(uri);
getAxiomFault().setNode(faultNode);
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapFaultException(ex);
}
}
@Override
public String getFaultStringOrReason() {
return getFaultReasonText(Locale.getDefault());
}
@Override
public String getFaultReasonText(Locale locale) {
SOAPFaultReason faultReason = getAxiomFault().getReason();
String language = AxiomUtils.toLanguage(locale);
SOAPFaultText faultText = faultReason.getSOAPFaultText(language);
return (faultText != null) ? faultText.getText() : null;
}
@Override
public void setFaultReasonText(Locale locale, String text) {
SOAPFaultReason faultReason = getAxiomFault().getReason();
String language = AxiomUtils.toLanguage(locale);
try {
SOAPFaultText faultText = getAxiomFactory().createSOAPFaultText(faultReason);
faultText.setLang(language);
faultText.setText(text);
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapFaultException(ex);
}
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.RolePlayer;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.apache.axiom.soap.SOAPProcessingException;
import org.springframework.util.ObjectUtils;
import org.springframework.ws.soap.SoapHeaderElement;
import org.springframework.ws.soap.SoapHeaderException;
import org.springframework.ws.soap.soap12.Soap12Header;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.Soap12Header}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoap12Header extends AxiomSoapHeader implements Soap12Header {
AxiomSoap12Header(SOAPHeader axiomHeader, SOAPFactory axiomFactory) {
super(axiomHeader, axiomFactory);
}
@Override
public SoapHeaderElement addNotUnderstoodHeaderElement(QName headerName) {
try {
SOAPHeaderBlock notUnderstood = getAxiomHeader().addHeaderBlock("NotUnderstood",
getAxiomHeader().getNamespace());
OMNamespace headerNamespace = notUnderstood.declareNamespace(headerName.getNamespaceURI(),
headerName.getPrefix());
notUnderstood.addAttribute("qname", headerNamespace.getPrefix() + ":" + headerName.getLocalPart(), null);
return new AxiomSoapHeaderElement(notUnderstood, getAxiomFactory());
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
public SoapHeaderElement addUpgradeHeaderElement(String[] supportedSoapUris) {
try {
SOAPHeaderBlock upgrade = getAxiomHeader().addHeaderBlock("Upgrade", getAxiomHeader().getNamespace());
for (String supportedSoapUri : supportedSoapUris) {
OMElement supportedEnvelope = getAxiomFactory().createOMElement("SupportedEnvelope",
getAxiomHeader().getNamespace(), upgrade);
OMNamespace namespace = supportedEnvelope.declareNamespace(supportedSoapUri, "");
supportedEnvelope.addAttribute("qname", namespace.getPrefix() + ":Envelope", null);
}
return new AxiomSoapHeaderElement(upgrade, getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
@SuppressWarnings("unchecked")
public Iterator<SoapHeaderElement> examineHeaderElementsToProcess(final String[] roles,
final boolean isUltimateDestination) throws SoapHeaderException {
RolePlayer rolePlayer = null;
if (!ObjectUtils.isEmpty(roles)) {
rolePlayer = new RolePlayer() {
public List<String> getRoles() {
return Arrays.asList(roles);
}
public boolean isUltimateDestination() {
return isUltimateDestination;
}
};
}
Iterator<SOAPHeaderBlock> result = (Iterator<SOAPHeaderBlock>) getAxiomHeader().getHeadersToProcess(rolePlayer);
return new AxiomSoapHeaderElementIterator(result);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import org.apache.axiom.om.OMDataSource;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.util.Assert;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
import org.springframework.ws.stream.StreamingPayload;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.Soap11Body}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
abstract class AxiomSoapBody extends AxiomSoapElement implements SoapBody {
private final Payload payload;
protected AxiomSoapBody(SOAPBody axiomBody, SOAPFactory axiomFactory, boolean payloadCaching) {
super(axiomBody, axiomFactory);
if (payloadCaching) {
this.payload = new CachingPayload(axiomBody, axiomFactory);
}
else {
this.payload = new NonCachingPayload(axiomBody, axiomFactory);
}
}
@Override
public Source getPayloadSource() {
return this.payload.getSource();
}
@Override
public Result getPayloadResult() {
return this.payload.getResult();
}
@Override
public boolean hasFault() {
return getAxiomBody().hasFault();
}
protected final SOAPBody getAxiomBody() {
return (SOAPBody) getAxiomElement();
}
public void setStreamingPayload(StreamingPayload payload) {
Assert.notNull(payload, "'payload' must not be null");
OMDataSource dataSource = new StreamingOMDataSource(payload);
SOAPFactory factory = getAxiomFactory();
QName name = payload.getName();
// Ignore the prefix; only the namespace URI and local name are significant
OMElement payloadElement = factory.createOMElement(dataSource, name.getLocalPart(),
factory.createOMNamespace(name.getNamespaceURI(), null));
SOAPBody soapBody = getAxiomBody();
AxiomUtils.removeContents(soapBody);
soapBody.addChild(payloadElement);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapEnvelopeException;
/**
* Exception thrown when an Axiom SOAP body could not be accessed.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class AxiomSoapBodyException extends SoapEnvelopeException {
public AxiomSoapBodyException(String msg) {
super(msg);
}
public AxiomSoapBodyException(String msg, Throwable ex) {
super(msg, ex);
}
public AxiomSoapBodyException(Throwable ex) {
super(ex);
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.StaxUtils;
import org.springframework.ws.soap.SoapElement;
/**
* Axiom-specific version of {@link SoapElement}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoapElement implements SoapElement {
private final OMElement axiomElement;
private final SOAPFactory axiomFactory;
protected AxiomSoapElement(OMElement axiomElement, SOAPFactory axiomFactory) {
Assert.notNull(axiomElement, "axiomElement must not be null");
Assert.notNull(axiomFactory, "axiomFactory must not be null");
this.axiomElement = axiomElement;
this.axiomFactory = axiomFactory;
}
@Override
public final QName getName() {
try {
return this.axiomElement.getQName();
}
catch (OMException ex) {
throw new AxiomSoapElementException(ex);
}
}
@Override
public final Source getSource() {
try {
return StaxUtils.createCustomStaxSource(this.axiomElement.getXMLStreamReader());
}
catch (OMException ex) {
throw new AxiomSoapElementException(ex);
}
}
@Override
public final void addAttribute(QName name, String value) {
try {
String namespaceUri = name.getNamespaceURI();
String prefix = name.getPrefix();
if (StringUtils.hasLength(namespaceUri) && !StringUtils.hasLength(prefix)) {
prefix = null;
}
OMNamespace namespace = getAxiomFactory().createOMNamespace(namespaceUri, prefix);
OMAttribute attribute = getAxiomFactory().createOMAttribute(name.getLocalPart(), namespace, value);
getAxiomElement().addAttribute(attribute);
}
catch (OMException ex) {
throw new AxiomSoapElementException(ex);
}
}
@Override
public void removeAttribute(QName name) {
try {
OMAttribute attribute = getAxiomElement().getAttribute(name);
if (attribute != null) {
getAxiomElement().removeAttribute(attribute);
}
}
catch (OMException ex) {
throw new AxiomSoapElementException(ex);
}
}
@Override
public final String getAttributeValue(QName name) {
try {
return getAxiomElement().getAttributeValue(name);
}
catch (OMException ex) {
throw new AxiomSoapElementException(ex);
}
}
@Override
public final Iterator<QName> getAllAttributes() {
try {
List<QName> results = new ArrayList<QName>();
for (Iterator<?> iterator = getAxiomElement().getAllAttributes(); iterator.hasNext();) {
OMAttribute attribute = (OMAttribute) iterator.next();
results.add(attribute.getQName());
}
return results.iterator();
}
catch (OMException ex) {
throw new AxiomSoapElementException(ex);
}
}
@Override
public void addNamespaceDeclaration(String prefix, String namespaceUri) {
try {
if (StringUtils.hasLength(prefix)) {
getAxiomElement().declareNamespace(namespaceUri, prefix);
}
else {
getAxiomElement().declareDefaultNamespace(namespaceUri);
}
}
catch (OMException ex) {
throw new AxiomSoapElementException(ex);
}
}
protected final OMElement getAxiomElement() {
return this.axiomElement;
}
protected final SOAPFactory getAxiomFactory() {
return this.axiomFactory;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapElementException;
/**
* Axiom-specific {@link SoapElementException}.
*
* @author Arjen Poutsma
*/
@SuppressWarnings("serial")
public class AxiomSoapElementException extends SoapElementException {
public AxiomSoapElementException(String msg) {
super(msg);
}
public AxiomSoapElementException(String msg, Throwable ex) {
super(msg, ex);
}
public AxiomSoapElementException(Throwable ex) {
super(ex);
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPHeader;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapEnvelope;
import org.springframework.ws.soap.SoapHeader;
/**
* Axiom-Specific version of {@code org.springframework.ws.soap.SoapEnvelope}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoapEnvelope extends AxiomSoapElement implements SoapEnvelope {
boolean payloadCaching;
private AxiomSoapBody body;
private final boolean langAttributeOnSoap11FaultString;
AxiomSoapEnvelope(SOAPEnvelope axiomEnvelope, SOAPFactory axiomFactory, boolean payloadCaching,
boolean langAttributeOnSoap11FaultString) {
super(axiomEnvelope, axiomFactory);
this.payloadCaching = payloadCaching;
this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
}
@Override
public SoapHeader getHeader() {
try {
if (getAxiomEnvelope().getHeader() == null) {
return null;
}
else {
SOAPHeader axiomHeader = getAxiomEnvelope().getHeader();
String namespaceURI = getAxiomEnvelope().getNamespace().getNamespaceURI();
if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceURI)) {
return new AxiomSoap11Header(axiomHeader, getAxiomFactory());
}
else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceURI)) {
return new AxiomSoap12Header(axiomHeader, getAxiomFactory());
}
else {
throw new AxiomSoapEnvelopeException("Unknown SOAP namespace \"" + namespaceURI + "\"");
}
}
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
public SoapBody getBody() {
if (this.body == null) {
try {
SOAPBody axiomBody = getAxiomEnvelope().getBody();
String namespaceURI = getAxiomEnvelope().getNamespace().getNamespaceURI();
if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceURI)) {
this.body = new AxiomSoap11Body(axiomBody, getAxiomFactory(), this.payloadCaching,
this.langAttributeOnSoap11FaultString);
}
else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(namespaceURI)) {
this.body = new AxiomSoap12Body(axiomBody, getAxiomFactory(), this.payloadCaching);
}
else {
throw new AxiomSoapEnvelopeException("Unknown SOAP namespace \"" + namespaceURI + "\"");
}
}
catch (OMException ex) {
throw new AxiomSoapBodyException(ex);
}
}
return this.body;
}
protected SOAPEnvelope getAxiomEnvelope() {
return (SOAPEnvelope) getAxiomElement();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapEnvelopeException;
/**
* Axiom-specific {@link SoapEnvelopeException}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class AxiomSoapEnvelopeException extends SoapEnvelopeException {
public AxiomSoapEnvelopeException(String msg) {
super(msg);
}
public AxiomSoapEnvelopeException(String msg, Throwable ex) {
super(msg, ex);
}
public AxiomSoapEnvelopeException(Throwable ex) {
super(ex);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFault;
import org.apache.axiom.soap.SOAPFaultDetail;
import org.apache.axiom.soap.SOAPFaultRole;
import org.apache.axiom.soap.SOAPProcessingException;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapFaultDetail;
/**
* Axiom implementation of {@link SoapFault}.
*
* @author Arjen Poutsma
*/
abstract class AxiomSoapFault extends AxiomSoapElement implements SoapFault {
protected AxiomSoapFault(SOAPFault axiomFault, SOAPFactory axiomFactory) {
super(axiomFault, axiomFactory);
}
@Override
public String getFaultActorOrRole() {
SOAPFaultRole faultRole = getAxiomFault().getRole();
return (faultRole != null) ? faultRole.getRoleValue() : null;
}
@Override
public void setFaultActorOrRole(String actor) {
try {
SOAPFaultRole axiomFaultRole = getAxiomFactory().createSOAPFaultRole(getAxiomFault());
axiomFaultRole.setRoleValue(actor);
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapFaultException(ex);
}
}
@Override
public SoapFaultDetail getFaultDetail() {
try {
SOAPFaultDetail axiomFaultDetail = getAxiomFault().getDetail();
return (axiomFaultDetail != null) ? new AxiomSoapFaultDetail(axiomFaultDetail, getAxiomFactory()) : null;
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);
}
}
@Override
public SoapFaultDetail addFaultDetail() {
try {
SOAPFaultDetail axiomFaultDetail = getAxiomFactory().createSOAPFaultDetail(getAxiomFault());
return new AxiomSoapFaultDetail(axiomFaultDetail, getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);
}
}
protected SOAPFault getAxiomFault() {
return (SOAPFault) getAxiomElement();
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.Iterator;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPFaultDetail;
import org.springframework.ws.soap.SoapFaultDetail;
import org.springframework.ws.soap.SoapFaultDetailElement;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.SoapFaultDetail}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoapFaultDetail extends AxiomSoapElement implements SoapFaultDetail {
AxiomSoapFaultDetail(SOAPFaultDetail axiomFaultDetail, SOAPFactory axiomFactory) {
super(axiomFaultDetail, axiomFactory);
}
@Override
public SoapFaultDetailElement addFaultDetailElement(QName name) {
try {
OMElement element = getAxiomFactory().createOMElement(name, getAxiomFaultDetail());
return new AxiomSoapFaultDetailElement(element, getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);
}
}
@Override
@SuppressWarnings("unchecked")
public Iterator<SoapFaultDetailElement> getDetailEntries() {
return new AxiomSoapFaultDetailElementIterator(getAxiomFaultDetail().getChildElements());
}
@Override
public Result getResult() {
return getAxiomFaultDetail().getSAXResult();
}
protected SOAPFaultDetail getAxiomFaultDetail() {
return (SOAPFaultDetail) getAxiomElement();
}
private final class AxiomSoapFaultDetailElementIterator implements Iterator<SoapFaultDetailElement> {
private final Iterator<OMElement> axiomIterator;
private AxiomSoapFaultDetailElementIterator(Iterator<OMElement> axiomIterator) {
this.axiomIterator = axiomIterator;
}
@Override
public boolean hasNext() {
return this.axiomIterator.hasNext();
}
@Override
public SoapFaultDetailElement next() {
try {
OMElement axiomElement = this.axiomIterator.next();
return new AxiomSoapFaultDetailElement(axiomElement, getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);
}
}
@Override
public void remove() {
this.axiomIterator.remove();
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.transform.Result;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.soap.SoapFaultDetailElement;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.SoapFaultDetailElement}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
class AxiomSoapFaultDetailElement extends AxiomSoapElement implements SoapFaultDetailElement {
AxiomSoapFaultDetailElement(OMElement axiomElement, SOAPFactory soapFactory) {
super(axiomElement, soapFactory);
}
@Override
public Result getResult() {
try {
return getAxiomElement().getSAXResult();
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);
}
}
@Override
public void addText(String text) {
try {
getAxiomElement().setText(text);
}
catch (OMException ex) {
throw new AxiomSoapFaultException(ex);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapFaultException;
/**
* Axiom-specific {@link SoapFaultException}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class AxiomSoapFaultException extends SoapFaultException {
public AxiomSoapFaultException(String msg) {
super(msg);
}
public AxiomSoapFaultException(String msg, Throwable ex) {
super(msg, ex);
}
public AxiomSoapFaultException(Throwable ex) {
super(ex);
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.util.Iterator;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPHeader;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.SoapHeaderElement;
import org.springframework.ws.soap.SoapHeaderException;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.SoapHeader}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
abstract class AxiomSoapHeader extends AxiomSoapElement implements SoapHeader {
AxiomSoapHeader(SOAPHeader axiomHeader, SOAPFactory axiomFactory) {
super(axiomHeader, axiomFactory);
}
@Override
public Result getResult() {
return getAxiomHeader().getSAXResult();
}
@Override
public SoapHeaderElement addHeaderElement(QName name) {
try {
OMNamespace namespace = getAxiomFactory().createOMNamespace(name.getNamespaceURI(), name.getPrefix());
SOAPHeaderBlock axiomHeaderBlock = getAxiomHeader().addHeaderBlock(name.getLocalPart(), namespace);
return new AxiomSoapHeaderElement(axiomHeaderBlock, getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
public void removeHeaderElement(QName name) throws SoapHeaderException {
try {
OMElement element = getAxiomHeader().getFirstChildWithName(name);
if (element != null) {
element.detach();
}
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
@SuppressWarnings("unchecked")
public Iterator<SoapHeaderElement> examineMustUnderstandHeaderElements(String role) {
try {
return new AxiomSoapHeaderElementIterator(getAxiomHeader().examineMustUnderstandHeaderBlocks(role));
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
@SuppressWarnings("unchecked")
public Iterator<SoapHeaderElement> examineAllHeaderElements() {
try {
return new AxiomSoapHeaderElementIterator(getAxiomHeader().examineAllHeaderBlocks());
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
@SuppressWarnings("unchecked")
public Iterator<SoapHeaderElement> examineHeaderElements(QName name) throws SoapHeaderException {
try {
return new AxiomSoapHeaderElementIterator(getAxiomHeader().getChildrenWithName(name));
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
protected SOAPHeader getAxiomHeader() {
return (SOAPHeader) getAxiomElement();
}
protected class AxiomSoapHeaderElementIterator implements Iterator<SoapHeaderElement> {
private final Iterator<? extends OMElement> axiomIterator;
protected AxiomSoapHeaderElementIterator(Iterator<? extends OMElement> axiomIterator) {
this.axiomIterator = axiomIterator;
}
@Override
public boolean hasNext() {
return this.axiomIterator.hasNext();
}
@Override
public SoapHeaderElement next() {
try {
OMElement axiomHeaderBlock = this.axiomIterator.next();
return new AxiomSoapHeaderElement(axiomHeaderBlock, getAxiomFactory());
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
public void remove() {
this.axiomIterator.remove();
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.transform.Result;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPHeaderBlock;
import org.springframework.ws.soap.SoapHeaderElement;
/**
* Axiom-specific version of {@code org.springframework.ws.soap.SoapHeaderHeaderElement}.
*
* @author Arjen Poutsma
*/
class AxiomSoapHeaderElement extends AxiomSoapElement implements SoapHeaderElement {
AxiomSoapHeaderElement(OMElement axiomHeaderBlock, SOAPFactory axiomFactory) {
super(axiomHeaderBlock, axiomFactory);
}
@Override
public String getActorOrRole() {
return getAxiomHeaderBlock().getRole();
}
@Override
public void setActorOrRole(String role) {
getAxiomHeaderBlock().setRole(role);
}
@Override
public boolean getMustUnderstand() {
return getAxiomHeaderBlock().getMustUnderstand();
}
@Override
public void setMustUnderstand(boolean mustUnderstand) {
getAxiomHeaderBlock().setMustUnderstand(mustUnderstand);
}
@Override
public Result getResult() {
try {
return getAxiomHeaderBlock().getSAXResult();
}
catch (OMException ex) {
throw new AxiomSoapHeaderException(ex);
}
}
@Override
public String getText() {
return getAxiomHeaderBlock().getText();
}
@Override
public void setText(String content) {
getAxiomHeaderBlock().setText(content);
}
protected SOAPHeaderBlock getAxiomHeaderBlock() {
return (SOAPHeaderBlock) getAxiomElement();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapHeaderException;
/**
* Axiom-specific {@link SoapHeaderException}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class AxiomSoapHeaderException extends SoapHeaderException {
public AxiomSoapHeaderException(String msg) {
super(msg);
}
public AxiomSoapHeaderException(String msg, Throwable ex) {
super(msg, ex);
}
public AxiomSoapHeaderException(Throwable ex) {
super(ex);
}
}

View File

@@ -0,0 +1,407 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Iterator;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.dom.DOMSource;
import jakarta.activation.DataHandler;
import org.apache.axiom.attachments.Attachments;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.OMXMLBuilderFactory;
import org.apache.axiom.om.impl.MTOMConstants;
import org.apache.axiom.om.impl.OMMultipartWriter;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPMessage;
import org.apache.axiom.soap.SOAPProcessingException;
import org.w3c.dom.Document;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.ws.mime.Attachment;
import org.springframework.ws.soap.AbstractSoapMessage;
import org.springframework.ws.soap.SoapEnvelope;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
import org.springframework.ws.soap.support.SoapUtils;
import org.springframework.ws.stream.StreamingPayload;
import org.springframework.ws.stream.StreamingWebServiceMessage;
import org.springframework.ws.transport.TransportConstants;
import org.springframework.ws.transport.TransportOutputStream;
/**
* AXIOM-specific implementation of the {@link SoapMessage} interface. Created via the
* {@link AxiomSoapMessageFactory}, wraps a {@link SOAPMessage}.
*
* @author Arjen Poutsma
* @since 1.0.0
* @see SOAPMessage
*/
public class AxiomSoapMessage extends AbstractSoapMessage implements StreamingWebServiceMessage {
private static final String EMPTY_SOAP_ACTION = "\"\"";
private SOAPMessage axiomMessage;
private final SOAPFactory axiomFactory;
private final Attachments attachments;
private final boolean payloadCaching;
private AxiomSoapEnvelope envelope;
private String soapAction;
private final boolean langAttributeOnSoap11FaultString;
private OMOutputFormat outputFormat;
/**
* Create a new, empty {@code AxiomSoapMessage}.
* @param soapFactory the AXIOM SOAPFactory
*/
public AxiomSoapMessage(SOAPFactory soapFactory) {
this(soapFactory, true, true);
}
/**
* Create a new, empty {@code AxiomSoapMessage}.
* @param soapFactory the AXIOM SOAPFactory
*/
public AxiomSoapMessage(SOAPFactory soapFactory, boolean payloadCaching, boolean langAttributeOnSoap11FaultString) {
SOAPEnvelope soapEnvelope = soapFactory.getDefaultEnvelope();
this.axiomFactory = soapFactory;
this.axiomMessage = this.axiomFactory.createSOAPMessage();
this.axiomMessage.setSOAPEnvelope(soapEnvelope);
this.attachments = new Attachments();
this.payloadCaching = payloadCaching;
this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
this.soapAction = EMPTY_SOAP_ACTION;
}
/**
* Create a new {@code AxiomSoapMessage} based on the given AXIOM {@code SOAPMessage}.
* @param soapMessage the AXIOM SOAPMessage
* @param soapAction the value of the SOAP Action header
* @param payloadCaching whether the contents of the SOAP body should be cached or not
*/
public AxiomSoapMessage(SOAPMessage soapMessage, String soapAction, boolean payloadCaching,
boolean langAttributeOnSoap11FaultString) {
this(soapMessage, new Attachments(), soapAction, payloadCaching, langAttributeOnSoap11FaultString);
}
/**
* Create a new {@code AxiomSoapMessage} based on the given AXIOM {@code SOAPMessage}
* and attachments.
* @param soapMessage the AXIOM SOAPMessage
* @param attachments the attachments
* @param soapAction the value of the SOAP Action header
* @param payloadCaching whether the contents of the SOAP body should be cached or not
*/
public AxiomSoapMessage(SOAPMessage soapMessage, Attachments attachments, String soapAction, boolean payloadCaching,
boolean langAttributeOnSoap11FaultString) {
Assert.notNull(soapMessage, "'soapMessage' must not be null");
Assert.notNull(attachments, "'attachments' must not be null");
this.axiomMessage = soapMessage;
this.axiomFactory = (SOAPFactory) soapMessage.getSOAPEnvelope().getOMFactory();
this.attachments = attachments;
if (!StringUtils.hasLength(soapAction)) {
soapAction = EMPTY_SOAP_ACTION;
}
this.soapAction = soapAction;
this.payloadCaching = payloadCaching;
this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
}
/**
* Return the AXIOM {@code SOAPMessage} that this {@code AxiomSoapMessage} is based
* on.
*/
public final SOAPMessage getAxiomMessage() {
return this.axiomMessage;
}
/**
* Sets the AXIOM {@code SOAPMessage} that this {@code AxiomSoapMessage} is based on.
* <p>
* Calling this method also clears the SOAP Action property.
*/
public final void setAxiomMessage(SOAPMessage axiomMessage) {
Assert.notNull(axiomMessage, "'axiomMessage' must not be null");
this.axiomMessage = axiomMessage;
this.envelope = null;
this.soapAction = EMPTY_SOAP_ACTION;
}
/**
* Sets the {@link OMOutputFormat} to be used when writing the message.
* @see #writeTo(java.io.OutputStream)
*/
public void setOutputFormat(OMOutputFormat outputFormat) {
this.outputFormat = outputFormat;
}
@Override
public void setStreamingPayload(StreamingPayload payload) {
AxiomSoapBody soapBody = (AxiomSoapBody) getSoapBody();
soapBody.setStreamingPayload(payload);
}
@Override
public SoapEnvelope getEnvelope() {
if (this.envelope == null) {
try {
this.envelope = new AxiomSoapEnvelope(this.axiomMessage.getSOAPEnvelope(), this.axiomFactory,
this.payloadCaching, this.langAttributeOnSoap11FaultString);
}
catch (SOAPProcessingException ex) {
throw new AxiomSoapEnvelopeException(ex);
}
}
return this.envelope;
}
@Override
public String getSoapAction() {
return this.soapAction;
}
@Override
public void setSoapAction(String soapAction) {
soapAction = SoapUtils.escapeAction(soapAction);
this.soapAction = soapAction;
}
@Override
public Document getDocument() {
return AxiomUtils.toDocument(this.axiomMessage.getSOAPEnvelope());
}
@Override
public void setDocument(Document document) {
// save the Soap Action
String soapAction = getSoapAction();
// replace the Axiom message
setAxiomMessage(
OMXMLBuilderFactory.createSOAPModelBuilder(this.axiomFactory.getMetaFactory(), new DOMSource(document))
.getSOAPMessage());
// restore the Soap Action
setSoapAction(soapAction);
}
@Override
public boolean isXopPackage() {
try {
return MTOMConstants.MTOM_TYPE.equals(this.attachments.getAttachmentSpecType());
}
catch (OMException ex) {
return false;
}
catch (NullPointerException ex) {
// gotta love Axis2
return false;
}
}
@Override
public boolean convertToXopPackage() {
return false;
}
@Override
public Attachment getAttachment(String contentId) {
Assert.hasLength(contentId, "contentId must not be empty");
if (contentId.startsWith("<") && contentId.endsWith(">")) {
contentId = contentId.substring(1, contentId.length() - 1);
}
DataHandler dataHandler = this.attachments.getDataHandler(contentId);
return (dataHandler != null) ? new AxiomAttachment(contentId, dataHandler) : null;
}
@Override
public Iterator<Attachment> getAttachments() {
return new AxiomAttachmentIterator();
}
@Override
public Attachment addAttachment(String contentId, DataHandler dataHandler) {
Assert.hasLength(contentId, "contentId must not be empty");
Assert.notNull(dataHandler, "dataHandler must not be null");
this.attachments.addDataHandler(contentId, dataHandler);
return new AxiomAttachment(contentId, dataHandler);
}
@Override
public void writeTo(OutputStream outputStream) throws IOException {
try {
OMOutputFormat outputFormat = getOutputFormat();
if (outputStream instanceof TransportOutputStream) {
TransportOutputStream transportOutputStream = (TransportOutputStream) outputStream;
String contentType = outputFormat.getContentType();
if (!(outputFormat.isDoingSWA() || outputFormat.isOptimized())) {
String charsetEncoding = this.axiomMessage.getCharsetEncoding();
contentType += "; charset=" + charsetEncoding;
}
SoapVersion version = getVersion();
if (SoapVersion.SOAP_11 == version) {
transportOutputStream.addHeader(TransportConstants.HEADER_SOAP_ACTION, this.soapAction);
transportOutputStream.addHeader(TransportConstants.HEADER_ACCEPT, version.getContentType());
}
else if (SoapVersion.SOAP_12 == version) {
contentType += "; action=" + this.soapAction;
transportOutputStream.addHeader(TransportConstants.HEADER_ACCEPT, version.getContentType());
}
transportOutputStream.addHeader(TransportConstants.HEADER_CONTENT_TYPE, contentType);
}
if (!(outputFormat.isOptimized()) & outputFormat.isDoingSWA()) {
writeSwAMessage(outputStream, outputFormat);
}
else {
if (this.payloadCaching) {
this.axiomMessage.serialize(outputStream, outputFormat);
}
else {
this.axiomMessage.serializeAndConsume(outputStream, outputFormat);
}
}
outputStream.flush();
}
catch (XMLStreamException ex) {
throw new AxiomSoapMessageException("Could not write message to OutputStream: " + ex.getMessage(), ex);
}
catch (OMException ex) {
throw new AxiomSoapMessageException("Could not write message to OutputStream: " + ex.getMessage(), ex);
}
}
private OMOutputFormat getOutputFormat() {
if (this.outputFormat != null) {
return this.outputFormat;
}
else {
String charsetEncoding = this.axiomMessage.getCharsetEncoding();
OMOutputFormat outputFormat = new OMOutputFormat();
outputFormat.setCharSetEncoding(charsetEncoding);
outputFormat.setSOAP11(getVersion() == SoapVersion.SOAP_11);
if (isXopPackage()) {
outputFormat.setDoOptimize(true);
}
else if (!this.attachments.getContentIDSet().isEmpty()) {
outputFormat.setDoingSWA(true);
}
return outputFormat;
}
}
private void writeSwAMessage(OutputStream outputStream, OMOutputFormat format)
throws XMLStreamException, UnsupportedEncodingException {
StringWriter writer = new StringWriter();
SOAPEnvelope envelope = this.axiomMessage.getSOAPEnvelope();
if (this.payloadCaching) {
envelope.serialize(writer, format);
}
else {
envelope.serializeAndConsume(writer, format);
}
try {
OMMultipartWriter mpw = new OMMultipartWriter(outputStream, format);
Writer rootPartWriter = new OutputStreamWriter(mpw.writeRootPart(), format.getCharSetEncoding());
rootPartWriter.write(writer.toString());
rootPartWriter.close();
// Get the collection of ids associated with the attachments
for (String id : this.attachments.getAllContentIDs()) {
mpw.writePart(this.attachments.getBlob(id), id);
}
mpw.complete();
}
catch (IOException ex) {
throw new OMException("Error writing SwA message", ex);
}
}
public String toString() {
StringBuilder builder = new StringBuilder("AxiomSoapMessage");
if (this.payloadCaching) {
try {
SOAPEnvelope envelope = this.axiomMessage.getSOAPEnvelope();
if (envelope != null) {
SOAPBody body = envelope.getBody();
if (body != null) {
OMElement bodyElement = body.getFirstElement();
if (bodyElement != null) {
builder.append(' ');
builder.append(bodyElement.getQName());
}
}
}
}
catch (OMException ex) {
// ignore
}
}
return builder.toString();
}
private final class AxiomAttachmentIterator implements Iterator<Attachment> {
private final Iterator<String> iterator;
private AxiomAttachmentIterator() {
this.iterator = AxiomSoapMessage.this.attachments.getContentIDSet().iterator();
}
@Override
public boolean hasNext() {
return this.iterator.hasNext();
}
@Override
public Attachment next() {
String contentId = this.iterator.next();
DataHandler dataHandler = AxiomSoapMessage.this.attachments.getDataHandler(contentId);
return new AxiomAttachment(contentId, dataHandler);
}
@Override
public void remove() {
this.iterator.remove();
}
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapMessageCreationException;
/**
* Axiom-specific {@link SoapMessageCreationException}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class AxiomSoapMessageCreationException extends SoapMessageCreationException {
public AxiomSoapMessageCreationException(String msg) {
super(msg);
}
public AxiomSoapMessageCreationException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapMessageException;
/**
* Axiom-specific {@link SoapMessageException}.
*
* @author Arjen Poutsma
* @since 1.0.0
*/
@SuppressWarnings("serial")
public class AxiomSoapMessageException extends SoapMessageException {
public AxiomSoapMessageException(String msg) {
super(msg);
}
public AxiomSoapMessageException(String msg, Throwable ex) {
super(msg, ex);
}
}

View File

@@ -0,0 +1,407 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Locale;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.axiom.attachments.Attachments;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.impl.MTOMConstants;
import org.apache.axiom.soap.SOAP11Constants;
import org.apache.axiom.soap.SOAP11Version;
import org.apache.axiom.soap.SOAP12Constants;
import org.apache.axiom.soap.SOAP12Version;
import org.apache.axiom.soap.SOAPFactory;
import org.apache.axiom.soap.SOAPMessage;
import org.apache.axiom.soap.SOAPModelBuilder;
import org.apache.axiom.soap.impl.builder.MTOMStAXSOAPModelBuilder;
import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
import org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping;
import org.springframework.ws.soap.SoapMessageFactory;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationMethodEndpointMapping;
import org.springframework.ws.soap.support.SoapUtils;
import org.springframework.ws.transport.TransportConstants;
import org.springframework.ws.transport.TransportInputStream;
import org.springframework.xml.XMLInputFactoryUtils;
/**
* Axiom-specific implementation of the
* {@link org.springframework.ws.WebServiceMessageFactory WebServiceMessageFactory}
* interface. Creates {@link org.springframework.ws.soap.axiom.AxiomSoapMessage
* AxiomSoapMessages}.
* <p>
* To increase reading performance on the SOAP request created by this message factory,
* you can set the {@link #setPayloadCaching(boolean) payloadCaching} property to
* {@code false} (default is {@code true}). This this will read the contents of the body
* directly from the stream. However, <strong>when this setting is enabled, the payload
* can only be read once</strong>. This means that any endpoint mappings or interceptors
* which are based on the message payload (such as the
* {@link PayloadRootAnnotationMethodEndpointMapping}, the
* {@link PayloadValidatingInterceptor}, or the {@link PayloadLoggingInterceptor}) cannot
* be used. Instead, use an endpoint mapping that does not consume the payload (i.e. the
* {@link SoapActionAnnotationMethodEndpointMapping}).
* <p>
* Additionally, this message factory can cache large attachments to disk by setting the
* {@link #setAttachmentCaching(boolean) attachmentCaching} property to {@code true}
* (default is {@code false}). Optionally, the location where attachments are stored can
* be defined via the {@link #setAttachmentCacheDir(File) attachmentCacheDir} property
* (defaults to the system temp file path).
* <p>
* Mostly derived from {@code org.apache.axis2.transport.http.HTTPTransportUtils} and
* {@code org.apache.axis2.transport.TransportUtils}, which we cannot use since they are
* not part of the Axiom distribution.
*
* @author Arjen Poutsma
* @author Andreas Veithen
* @since 1.0.0
* @see AxiomSoapMessage
* @see #setPayloadCaching(boolean)
*/
public class AxiomSoapMessageFactory implements SoapMessageFactory, InitializingBean {
private static final String CHARSET_PARAMETER = "charset";
private static final String DEFAULT_CHARSET_ENCODING = "UTF-8";
private static final String MULTI_PART_RELATED_CONTENT_TYPE = "multipart/related";
private static final Log logger = LogFactory.getLog(AxiomSoapMessageFactory.class);
private XMLInputFactory inputFactory;
private boolean payloadCaching = true;
private boolean attachmentCaching = false;
private File attachmentCacheDir;
private int attachmentCacheThreshold = 4096;
// use SOAP 1.1 by default
private SOAPFactory soapFactory = OMAbstractFactory.getSOAP11Factory();
private boolean langAttributeOnSoap11FaultString = true;
private boolean replacingEntityReferences = false;
private boolean supportingExternalEntities = false;
/**
* Indicates whether the SOAP Body payload should be cached or not. Default is
* {@code true}.
* <p>
* Setting this to {@code false} will increase performance, but also result in the
* fact that the message payload can only be read once.
*/
public void setPayloadCaching(boolean payloadCaching) {
this.payloadCaching = payloadCaching;
}
/**
* Indicates whether SOAP attachments should be cached or not. Default is
* {@code false}.
* <p>
* Setting this to {@code true} will cause Axiom to store larger attachments on disk,
* rather than in memory. This decreases memory consumption, but decreases
* performance.
*/
public void setAttachmentCaching(boolean attachmentCaching) {
this.attachmentCaching = attachmentCaching;
}
/**
* Sets the directory where SOAP attachments will be stored. Only used when
* {@link #setAttachmentCaching(boolean) attachmentCaching} is set to {@code true}.
* <p>
* The parameter should be an existing, writable directory. This property defaults to
* the temporary directory of the operating system (i.e. the value of the
* {@code java.io.tmpdir} system property).
*/
public void setAttachmentCacheDir(File attachmentCacheDir) {
Assert.notNull(attachmentCacheDir, "'attachmentCacheDir' must not be null");
Assert.isTrue(attachmentCacheDir.isDirectory(), "'attachmentCacheDir' must be a directory");
Assert.isTrue(attachmentCacheDir.canWrite(), "'attachmentCacheDir' must be writable");
this.attachmentCacheDir = attachmentCacheDir;
}
/**
* Sets the threshold for attachments caching, in bytes. Attachments larger than this
* threshold will be cached in the {@link #setAttachmentCacheDir(File) attachment
* cache directory}. Only used when {@link #setAttachmentCaching(boolean)
* attachmentCaching} is set to {@code true}.
* <p>
* Defaults to 4096 bytes (i.e. 4 kilobytes).
*/
public void setAttachmentCacheThreshold(int attachmentCacheThreshold) {
Assert.isTrue(attachmentCacheThreshold > 0, "'attachmentCacheThreshold' must be larger than 0");
this.attachmentCacheThreshold = attachmentCacheThreshold;
}
@Override
public void setSoapVersion(SoapVersion version) {
if (SoapVersion.SOAP_11 == version) {
this.soapFactory = OMAbstractFactory.getSOAP11Factory();
}
else if (SoapVersion.SOAP_12 == version) {
this.soapFactory = OMAbstractFactory.getSOAP12Factory();
}
else {
throw new IllegalArgumentException(
"Invalid version [" + version + "]. " + "Expected the SOAP_11 or SOAP_12 constant");
}
}
/**
* Defines whether a {@code xml:lang} attribute should be set on SOAP 1.1
* {@code <faultstring>} elements.
* <p>
* The default is {@code true}, to comply with WS-I, but this flag can be set to
* {@code false} to the older W3C SOAP 1.1 specification.
* @see <a href=
* "http://www.ws-i.org/Profiles/BasicProfile-1.1.html#SOAP_Fault_Language">WS-I Basic
* Profile 1.1</a>
*/
public void setLangAttributeOnSoap11FaultString(boolean langAttributeOnSoap11FaultString) {
this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
}
/**
* Sets whether internal entity references should be replaced with their replacement
* text and report them as characters.
* @see XMLInputFactory#IS_REPLACING_ENTITY_REFERENCES
*/
public void setReplacingEntityReferences(boolean replacingEntityReferences) {
this.replacingEntityReferences = replacingEntityReferences;
}
/**
* Sets whether external parsed entities should be resolved.
* @see XMLInputFactory#IS_SUPPORTING_EXTERNAL_ENTITIES
*/
public void setSupportingExternalEntities(boolean supportingExternalEntities) {
this.supportingExternalEntities = supportingExternalEntities;
}
@Override
public void afterPropertiesSet() throws Exception {
if (logger.isInfoEnabled()) {
logger.info(this.payloadCaching ? "Enabled payload caching" : "Disabled payload caching");
}
if (this.attachmentCacheDir == null) {
String tempDir = System.getProperty("java.io.tmpdir");
setAttachmentCacheDir(new File(tempDir));
}
this.inputFactory = createXmlInputFactory();
}
@Override
public AxiomSoapMessage createWebServiceMessage() {
return new AxiomSoapMessage(this.soapFactory, this.payloadCaching, this.langAttributeOnSoap11FaultString);
}
@Override
public AxiomSoapMessage createWebServiceMessage(InputStream inputStream) throws IOException {
Assert.isInstanceOf(TransportInputStream.class, inputStream,
"AxiomSoapMessageFactory requires a TransportInputStream");
if (this.inputFactory == null) {
this.inputFactory = createXmlInputFactory();
}
TransportInputStream transportInputStream = (TransportInputStream) inputStream;
String contentType = getHeaderValue(transportInputStream, TransportConstants.HEADER_CONTENT_TYPE);
if (!StringUtils.hasLength(contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("TransportInputStream has no Content-Type header; defaulting to \""
+ this.soapFactory.getSOAPVersion().getMediaType() + "\"");
}
contentType = this.soapFactory.getSOAPVersion().getMediaType().toString();
}
String soapAction = getHeaderValue(transportInputStream, TransportConstants.HEADER_SOAP_ACTION);
if (!StringUtils.hasLength(soapAction)) {
soapAction = SoapUtils.extractActionFromContentType(contentType);
}
try {
if (isMultiPartRelated(contentType)) {
return createMultiPartAxiomSoapMessage(inputStream, contentType, soapAction);
}
else {
return createAxiomSoapMessage(inputStream, contentType, soapAction);
}
}
catch (XMLStreamException ex) {
throw new AxiomSoapMessageCreationException("Could not parse request: " + ex.getMessage(), ex);
}
catch (OMException ex) {
throw new AxiomSoapMessageCreationException("Could not create message: " + ex.getMessage(), ex);
}
}
private String getHeaderValue(TransportInputStream transportInputStream, String header) throws IOException {
String contentType = null;
Iterator<String> iterator = transportInputStream.getHeaders(header);
if (iterator.hasNext()) {
contentType = iterator.next();
}
return contentType;
}
private boolean isMultiPartRelated(String contentType) {
contentType = contentType.toLowerCase(Locale.ENGLISH);
return contentType.contains(MULTI_PART_RELATED_CONTENT_TYPE);
}
@SuppressWarnings("deprecation")
/** Creates an AxiomSoapMessage without attachments. */
private AxiomSoapMessage createAxiomSoapMessage(InputStream inputStream, String contentType, String soapAction)
throws XMLStreamException {
XMLStreamReader reader = this.inputFactory.createXMLStreamReader(inputStream, getCharSetEncoding(contentType));
String envelopeNamespace = getSoapEnvelopeNamespace(contentType);
SOAPModelBuilder builder = new StAXSOAPModelBuilder(reader, this.soapFactory, envelopeNamespace);
SOAPMessage soapMessage = builder.getSOAPMessage();
return new AxiomSoapMessage(soapMessage, soapAction, this.payloadCaching,
this.langAttributeOnSoap11FaultString);
}
@SuppressWarnings("deprecation")
/** Creates an AxiomSoapMessage with attachments. */
private AxiomSoapMessage createMultiPartAxiomSoapMessage(InputStream inputStream, String contentType,
String soapAction) throws XMLStreamException {
Attachments attachments = new Attachments(inputStream, contentType, this.attachmentCaching,
this.attachmentCacheDir.getAbsolutePath(), Integer.toString(this.attachmentCacheThreshold));
XMLStreamReader reader = this.inputFactory.createXMLStreamReader(attachments.getRootPartInputStream(),
getCharSetEncoding(attachments.getRootPartContentType()));
SOAPModelBuilder builder;
String envelopeNamespace = getSoapEnvelopeNamespace(contentType);
if (MTOMConstants.SWA_TYPE.equals(attachments.getAttachmentSpecType())
|| MTOMConstants.SWA_TYPE_12.equals(attachments.getAttachmentSpecType())) {
builder = new StAXSOAPModelBuilder(reader, this.soapFactory, envelopeNamespace);
}
else if (MTOMConstants.MTOM_TYPE.equals(attachments.getAttachmentSpecType())) {
builder = new MTOMStAXSOAPModelBuilder(reader, attachments, envelopeNamespace);
}
else {
throw new AxiomSoapMessageCreationException(
"Unknown attachment type: [" + attachments.getAttachmentSpecType() + "]");
}
return new AxiomSoapMessage(builder.getSOAPMessage(), attachments, soapAction, this.payloadCaching,
this.langAttributeOnSoap11FaultString);
}
private String getSoapEnvelopeNamespace(String contentType) {
if (contentType.contains(SOAP11Constants.SOAP_11_CONTENT_TYPE)) {
return SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI;
}
else if (contentType.contains(SOAP12Constants.SOAP_12_CONTENT_TYPE)) {
return SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;
}
else {
throw new AxiomSoapMessageCreationException("Unknown content type '" + contentType + "'");
}
}
/**
* Returns the character set from the given content type. Mostly copied
* @return the character set encoding
*/
protected String getCharSetEncoding(String contentType) {
int charSetIdx = contentType.indexOf(CHARSET_PARAMETER);
if (charSetIdx == -1) {
return DEFAULT_CHARSET_ENCODING;
}
int eqIdx = contentType.indexOf("=", charSetIdx);
int indexOfSemiColon = contentType.indexOf(";", eqIdx);
String value;
if (indexOfSemiColon > 0) {
value = contentType.substring(eqIdx + 1, indexOfSemiColon);
}
else {
value = contentType.substring(eqIdx + 1, contentType.length()).trim();
}
if (value.startsWith("\"")) {
value = value.substring(1);
}
if (value.endsWith("\"")) {
return value.substring(0, value.length() - 1);
}
if ("null".equalsIgnoreCase(value)) {
return DEFAULT_CHARSET_ENCODING;
}
else {
return value.trim();
}
}
/**
* Create a {@code XMLInputFactory} that this resolver will use to create
* {@link XMLStreamReader} objects.
* <p>
* Can be overridden in subclasses, adding further initialization of the factory. The
* resulting factory is cached, so this method will only be called once.
* <p>
* By default this method creates a standard {@link XMLInputFactory} and configures it
* based on the {@link #setReplacingEntityReferences(boolean)
* replacingEntityReferences} and {@link #setSupportingExternalEntities(boolean)
* supportingExternalEntities} properties.
* @return the created factory
*/
protected XMLInputFactory createXmlInputFactory() {
XMLInputFactory inputFactory = XMLInputFactoryUtils.newInstance();
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, this.replacingEntityReferences);
inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, this.supportingExternalEntities);
return inputFactory;
}
public String toString() {
StringBuilder builder = new StringBuilder("AxiomSoapMessageFactory[");
if (this.soapFactory.getSOAPVersion() == SOAP11Version.getSingleton()) {
builder.append("SOAP 1.1");
}
else if (this.soapFactory.getSOAPVersion() == SOAP12Version.getSingleton()) {
builder.append("SOAP 1.2");
}
builder.append(',');
if (this.payloadCaching) {
builder.append("PayloadCaching enabled");
}
else {
builder.append("PayloadCaching disabled");
}
builder.append(']');
return builder.toString();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Result;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMXMLStreamReaderConfiguration;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
/**
* Caching payload in Axiom.
*
* @author Arjen Poutsma
* @since 1.5.2
*/
@SuppressWarnings("Since15")
class CachingPayload extends AbstractPayload {
CachingPayload(SOAPBody axiomBody, SOAPFactory axiomFactory) {
super(axiomBody, axiomFactory);
}
@Override
protected XMLStreamReader getStreamReader(OMElement payloadElement) {
OMXMLStreamReaderConfiguration config = new OMXMLStreamReaderConfiguration();
config.setPreserveNamespaceContext(true);
return payloadElement.getXMLStreamReader(true, config);
}
@Override
public Result getResultInternal() {
return getAxiomBody().getSAXResult();
}
}

View File

@@ -0,0 +1,297 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.ByteArrayOutputStream;
import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.Result;
import org.apache.axiom.blob.Blobs;
import org.apache.axiom.om.OMDataSource;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.ds.BlobOMDataSource;
import org.apache.axiom.om.util.StAXUtils;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.util.xml.StaxUtils;
/**
* Non-caching payload in Axiom.
*
* @author Jim Cummings
* @author Arjen Poutsma
* @since 1.5.2
*/
class NonCachingPayload extends AbstractPayload {
private static final int BUF_SIZE = 1024;
NonCachingPayload(SOAPBody axiomBody, SOAPFactory axiomFactory) {
super(axiomBody, axiomFactory);
}
@Override
public Result getResultInternal() {
return StaxUtils.createCustomStaxResult(new DelegatingStreamWriter());
}
@Override
protected XMLStreamReader getStreamReader(OMElement payloadElement) {
return payloadElement.getXMLStreamReaderWithoutCaching();
}
private final class DelegatingStreamWriter implements XMLStreamWriter {
private final ByteArrayOutputStream baos = new ByteArrayOutputStream(BUF_SIZE);
private final XMLStreamWriter delegate;
private QName name;
private String encoding = "UTF-8";
private int elementDepth = 0;
private boolean payloadAdded = false;
private DelegatingStreamWriter() {
try {
this.delegate = StAXUtils.createXMLStreamWriter(this.baos);
}
catch (XMLStreamException ex) {
throw new AxiomSoapBodyException("Could not determine payload root element", ex);
}
}
@Override
public void writeStartDocument() throws XMLStreamException {
// ignored
}
@Override
public void writeStartDocument(String version) throws XMLStreamException {
// ignored
}
@Override
public void writeStartDocument(String encoding, String version) throws XMLStreamException {
this.encoding = encoding;
}
@Override
public void writeStartElement(String localName) throws XMLStreamException {
if (this.name == null) {
this.name = new QName(localName);
}
this.elementDepth++;
this.delegate.writeStartElement(localName);
}
@Override
public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
if (this.name == null) {
this.name = new QName(namespaceURI, localName);
}
this.elementDepth++;
this.delegate.writeStartElement(namespaceURI, localName);
}
@Override
public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
if (this.name == null) {
this.name = new QName(namespaceURI, localName, prefix);
}
this.elementDepth++;
this.delegate.writeStartElement(prefix, localName, namespaceURI);
}
@Override
public void writeEndElement() throws XMLStreamException {
this.elementDepth--;
this.delegate.writeEndElement();
addPayload();
}
private void addPayload() throws XMLStreamException {
if (this.elementDepth <= 0 && !this.payloadAdded) {
this.delegate.flush();
if (this.baos.size() > 0) {
byte[] buf = this.baos.toByteArray();
OMDataSource dataSource = new BlobOMDataSource(Blobs.createBlob(buf), this.encoding);
OMNamespace namespace = getAxiomFactory().createOMNamespace(this.name.getNamespaceURI(),
this.name.getPrefix());
OMElement payloadElement = getAxiomFactory().createOMElement(dataSource, this.name.getLocalPart(),
namespace);
getAxiomBody().addChild(payloadElement);
this.payloadAdded = true;
}
}
}
@Override
public void writeEmptyElement(String localName) throws XMLStreamException {
if (this.name == null) {
this.name = new QName(localName);
}
this.delegate.writeEmptyElement(localName);
addPayload();
}
@Override
public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
if (this.name == null) {
this.name = new QName(namespaceURI, localName);
}
this.delegate.writeEmptyElement(namespaceURI, localName);
addPayload();
}
@Override
public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
if (this.name == null) {
this.name = new QName(namespaceURI, localName, prefix);
}
this.delegate.writeEmptyElement(prefix, localName, namespaceURI);
addPayload();
}
@Override
public void writeEndDocument() throws XMLStreamException {
this.elementDepth = 0;
this.delegate.writeEndDocument();
addPayload();
}
// Delegation
@Override
public void close() throws XMLStreamException {
addPayload();
this.delegate.close();
}
@Override
public void flush() throws XMLStreamException {
this.delegate.flush();
}
@Override
public NamespaceContext getNamespaceContext() {
return this.delegate.getNamespaceContext();
}
@Override
public String getPrefix(String uri) throws XMLStreamException {
return this.delegate.getPrefix(uri);
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
return this.delegate.getProperty(name);
}
@Override
public void setDefaultNamespace(String uri) throws XMLStreamException {
this.delegate.setDefaultNamespace(uri);
}
@Override
public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
this.delegate.setNamespaceContext(context);
}
@Override
public void setPrefix(String prefix, String uri) throws XMLStreamException {
this.delegate.setPrefix(prefix, uri);
}
@Override
public void writeAttribute(String localName, String value) throws XMLStreamException {
this.delegate.writeAttribute(localName, value);
}
@Override
public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException {
this.delegate.writeAttribute(namespaceURI, localName, value);
}
@Override
public void writeAttribute(String prefix, String namespaceURI, String localName, String value)
throws XMLStreamException {
this.delegate.writeAttribute(prefix, namespaceURI, localName, value);
}
@Override
public void writeCData(String data) throws XMLStreamException {
this.delegate.writeCData(data);
}
@Override
public void writeCharacters(char[] text, int start, int len) throws XMLStreamException {
this.delegate.writeCharacters(text, start, len);
}
@Override
public void writeCharacters(String text) throws XMLStreamException {
this.delegate.writeCharacters(text);
}
@Override
public void writeComment(String data) throws XMLStreamException {
this.delegate.writeComment(data);
}
@Override
public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException {
this.delegate.writeDefaultNamespace(namespaceURI);
}
@Override
public void writeDTD(String dtd) throws XMLStreamException {
this.delegate.writeDTD(dtd);
}
@Override
public void writeEntityRef(String name) throws XMLStreamException {
this.delegate.writeEntityRef(name);
}
@Override
public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
this.delegate.writeNamespace(prefix, namespaceURI);
}
@Override
public void writeProcessingInstruction(String target) throws XMLStreamException {
this.delegate.writeProcessingInstruction(target);
}
@Override
public void writeProcessingInstruction(String target, String data) throws XMLStreamException {
this.delegate.writeProcessingInstruction(target, data);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
/**
* Defines the contract for payloads in Axiom.
*
* @author Arjen Poutsma
* @since 1.5.2
*/
abstract class Payload {
/**
* Returns the source of the payload.
* @return the source of the payload
*/
public abstract Source getSource();
/**
* Returns the result of the payload.
* @return the result of the payload
*/
public abstract Result getResult();
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.axiom.om.OMDataSource;
import org.apache.axiom.om.ds.AbstractPushOMDataSource;
import org.springframework.util.Assert;
import org.springframework.ws.stream.StreamingPayload;
/**
* Implementation of {@link OMDataSource} that wraps a {@link StreamingPayload}.
*
* @author Arjen Poutsma
* @since 2.0
*/
class StreamingOMDataSource extends AbstractPushOMDataSource {
private final StreamingPayload payload;
StreamingOMDataSource(StreamingPayload payload) {
Assert.notNull(payload, "'payload' must not be null");
this.payload = payload;
}
@Override
public boolean isDestructiveWrite() {
return false;
}
@Override
public void serialize(XMLStreamWriter xmlWriter) throws XMLStreamException {
this.payload.writeTo(xmlWriter);
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* AXis Object Model (AXIOM) support for Spring-WS soap message infrastructure.
*/
package org.springframework.ws.soap.axiom;

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom.support;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Iterator;
import java.util.Locale;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import org.apache.axiom.om.OMContainer;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMException;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.OMXMLBuilderFactory;
import org.apache.axiom.soap.SOAPEnvelope;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.springframework.util.StringUtils;
import org.springframework.xml.DocumentBuilderFactoryUtils;
/**
* Collection of generic utility methods to work with Axiom. Includes conversion from
* {@code OMNamespace}s to {@code QName}s.
*
* @author Arjen Poutsma
* @author Tareq Abed Rabbo
* @since 1.0.0
* @see org.apache.axiom.om.OMNamespace
* @see javax.xml.namespace.QName
*/
@SuppressWarnings("Since15")
public abstract class AxiomUtils {
/**
* Converts a {@code javax.xml.namespace.QName} to a
* {@code org.apache.axiom.om.OMNamespace}. A {@code OMElement} is used to resolve the
* namespace, or to declare a new one.
* @param qName the {@code QName} to convert
* @param resolveElement the element used to resolve the Q
* @return the converted SAAJ Name
* @throws OMException if conversion is unsuccessful
* @throws IllegalArgumentException if {@code qName} is not fully qualified
*/
public static OMNamespace toNamespace(QName qName, OMElement resolveElement) throws OMException {
String prefix = qName.getPrefix();
if (StringUtils.hasLength(qName.getNamespaceURI()) && StringUtils.hasLength(prefix)) {
return resolveElement.declareNamespace(qName.getNamespaceURI(), prefix);
}
else if (StringUtils.hasLength(qName.getNamespaceURI())) {
// check for existing namespace, and declare if necessary
return resolveElement.declareNamespace(qName.getNamespaceURI(), "");
}
else {
throw new IllegalArgumentException("qName [" + qName + "] does not contain a namespace");
}
}
/**
* Converts the given locale to a {@code xml:lang} string, as used in Axiom Faults.
* @param locale the locale
* @return the language string
*/
public static String toLanguage(Locale locale) {
return locale.toString().replace('_', '-');
}
/**
* Converts the given locale to a {@code xml:lang} string, as used in Axiom Faults.
* @param language the language string
* @return the locale
*/
public static Locale toLocale(String language) {
language = language.replace('-', '_');
return StringUtils.parseLocaleString(language);
}
/** Removes the contents (i.e. children) of the container. */
public static void removeContents(OMContainer container) {
for (Iterator<?> iterator = container.getChildren(); iterator.hasNext();) {
iterator.next();
iterator.remove();
}
}
/**
* Converts a given AXIOM {@link org.apache.axiom.soap.SOAPEnvelope} to a
* {@link Document}.
* @param envelope the SOAP envelope to be converted
* @return the converted document
* @throws IllegalArgumentException in case of errors
*/
public static Document toDocument(SOAPEnvelope envelope) {
try {
if (envelope instanceof Element) {
return ((Element) envelope).getOwnerDocument();
}
else {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
envelope.build();
envelope.serialize(bos);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactoryUtils.newInstance();
documentBuilderFactory.setNamespaceAware(true);
return documentBuilderFactory.newDocumentBuilder().parse(bis);
}
}
catch (Exception ex) {
throw new IllegalArgumentException("Error in converting SOAP Envelope to Document", ex);
}
}
/**
* Converts a given {@link Document} to an AXIOM
* {@link org.apache.axiom.soap.SOAPEnvelope}.
* @param document the document to be converted
* @return the converted envelope
* @throws IllegalArgumentException in case of errors
*/
public static SOAPEnvelope toEnvelope(Document document) {
return OMXMLBuilderFactory.createSOAPModelBuilder(new DOMSource(document)).getSOAPEnvelope();
}
}

View File

@@ -0,0 +1,20 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support classes for working with the AXis Object Model (AXIOM).
*/
package org.springframework.ws.soap.axiom.support;

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.client.core;
import org.springframework.ws.soap.SoapMessageFactory;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
/**
* @author Arjen Poutsma
*/
public class AxiomNonStreamingSoap11WebServiceTemplateIntegrationTest
extends AbstractSoap11WebServiceTemplateIntegrationTest {
@Override
public SoapMessageFactory createMessageFactory() throws Exception {
return new AxiomSoapMessageFactory();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.client.core;
import org.springframework.ws.soap.SoapMessageFactory;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
/**
* @author Arjen Poutsma
*/
public class AxiomNonStreamingSoap12WebServiceTemplateIntegrationTest
extends AbstractSoap12WebServiceTemplateIntegrationTest {
@Override
public SoapMessageFactory createMessageFactory() {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
return messageFactory;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.client.core;
import org.springframework.ws.soap.SoapMessageFactory;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
/**
* @author Arjen Poutsma
*/
public class AxiomStreamingSoap11WebServiceTemplateIntegrationTest
extends AbstractSoap11WebServiceTemplateIntegrationTest {
@Override
public SoapMessageFactory createMessageFactory() throws Exception {
AxiomSoapMessageFactory axiomFactory = new AxiomSoapMessageFactory();
axiomFactory.setPayloadCaching(false);
return axiomFactory;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.client.core;
import org.springframework.ws.soap.SoapMessageFactory;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
/**
* @author Arjen Poutsma
*/
public class AxiomStreamingSoap12WebServiceTemplateIntegrationTest
extends AbstractSoap12WebServiceTemplateIntegrationTest {
@Override
public SoapMessageFactory createMessageFactory() throws Exception {
AxiomSoapMessageFactory axiomFactory = new AxiomSoapMessageFactory();
axiomFactory.setSoapVersion(SoapVersion.SOAP_12);
axiomFactory.setPayloadCaching(false);
return axiomFactory;
}
}

View File

@@ -21,11 +21,15 @@ import javax.xml.transform.TransformerException;
import jakarta.xml.soap.MessageFactory;
import jakarta.xml.soap.SOAPException;
import jakarta.xml.soap.SOAPMessage;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.MockWebServiceMessage;
import org.springframework.ws.MockWebServiceMessageFactory;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.axiom.AxiomSoapMessage;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.xml.transform.StringSource;
@@ -55,22 +59,21 @@ public class AbstractMethodArgumentResolverTest extends TransformerObjectSupport
}
protected MessageContext createCachingAxiomMessageContext() throws Exception {
MessageFactory messageFactory = MessageFactory.newInstance();
SaajSoapMessage request = new SaajSoapMessage(messageFactory.createMessage(), true, messageFactory);
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP11Factory();
AxiomSoapMessage request = new AxiomSoapMessage(axiomFactory, true, false);
transform(new StringSource(XML), request.getPayloadResult());
SaajSoapMessageFactory soapMessageFactory = new SaajSoapMessageFactory();
AxiomSoapMessageFactory soapMessageFactory = new AxiomSoapMessageFactory();
soapMessageFactory.afterPropertiesSet();
return new DefaultMessageContext(request, soapMessageFactory);
}
protected MessageContext createNonCachingAxiomMessageContext() throws Exception {
MessageFactory messageFactory = MessageFactory.newInstance();
SaajSoapMessage request = new SaajSoapMessage(messageFactory.createMessage(), true, messageFactory);
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP11Factory();
AxiomSoapMessage request = new AxiomSoapMessage(axiomFactory, false, false);
transform(new StringSource(XML), request.getPayloadResult());
SaajSoapMessageFactory soapMessageFactory = new SaajSoapMessageFactory();
AxiomSoapMessageFactory soapMessageFactory = new AxiomSoapMessageFactory();
soapMessageFactory.setPayloadCaching(false);
soapMessageFactory.afterPropertiesSet();
return new DefaultMessageContext(request, soapMessageFactory);

View File

@@ -16,7 +16,10 @@
package org.springframework.ws.server.endpoint.adapter.method.jaxb;
import java.io.ByteArrayOutputStream;
import javax.xml.namespace.QName;
import javax.xml.transform.Transformer;
import jakarta.xml.bind.JAXBElement;
import jakarta.xml.bind.JAXBException;
@@ -34,6 +37,10 @@ import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.axiom.AxiomSoapMessage;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.TransformerFactoryUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -133,6 +140,45 @@ public class JaxbElementPayloadMethodProcessorTest {
assertThat(messageContext.hasResponse()).isFalse();
}
@Test
public void handleReturnValueAxiom() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
MessageContext messageContext = new DefaultMessageContext(messageFactory);
MyType type = new MyType();
type.setString("Foo");
JAXBElement<MyType> element = new JAXBElement<>(new QName("http://springframework.org", "type"), MyType.class,
type);
this.processor.handleReturnValue(messageContext, this.supportedReturnType, element);
assertThat(messageContext.hasResponse()).isTrue();
AxiomSoapMessage response = (AxiomSoapMessage) messageContext.getResponse();
Transformer transformer = TransformerFactoryUtils.newInstance().newTransformer();
StringResult payloadResult = new StringResult();
transformer.transform(response.getPayloadSource(), payloadResult);
XmlAssert.assertThat(payloadResult.toString())
.and("<type xmlns='http://springframework.org'><string>Foo</string></type>")
.ignoreWhitespace()
.areIdentical();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
response.writeTo(bos);
String messageResult = bos.toString("UTF-8");
XmlAssert.assertThat(messageResult)
.and("<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'><soapenv:Header/><soapenv:Body>"
+ "<type xmlns='http://springframework.org'><string>Foo</string></type>"
+ "</soapenv:Body></soapenv:Envelope>")
.ignoreWhitespace()
.areIdentical();
}
@ResponsePayload
public JAXBElement<MyType> supported(@RequestPayload JAXBElement<MyType> element) {
return element;

View File

@@ -16,10 +16,12 @@
package org.springframework.ws.server.endpoint.adapter.method.jaxb;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXSource;
import jakarta.xml.bind.JAXBException;
@@ -43,7 +45,11 @@ import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.axiom.AxiomSoapMessage;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
import org.springframework.xml.sax.AbstractXmlReader;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.TransformerFactoryUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -208,6 +214,71 @@ public class XmlRootElementPayloadMethodProcessorTest {
assertThat(messageContext.hasResponse()).isFalse();
}
@Test
public void handleReturnValueAxiom() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
MessageContext messageContext = new DefaultMessageContext(messageFactory);
MyRootElement rootElement = new MyRootElement();
rootElement.setString("Foo");
this.processor.handleReturnValue(messageContext, this.rootElementReturnType, rootElement);
assertThat(messageContext.hasResponse()).isTrue();
AxiomSoapMessage response = (AxiomSoapMessage) messageContext.getResponse();
Transformer transformer = TransformerFactoryUtils.newInstance().newTransformer();
StringResult payloadResult = new StringResult();
transformer.transform(response.getPayloadSource(), payloadResult);
XmlAssert.assertThat(payloadResult.toString())
.and("<root xmlns='http://springframework.org'><string>Foo</string></root>")
.ignoreWhitespace()
.areIdentical();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
response.writeTo(bos);
String messageResult = bos.toString("UTF-8");
XmlAssert.assertThat(messageResult)
.and("<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'><soapenv:Header/><soapenv:Body>"
+ "<root xmlns='http://springframework.org'><string>Foo</string></root>"
+ "</soapenv:Body></soapenv:Envelope>")
.ignoreWhitespace()
.areIdentical();
}
@Test
public void handleReturnValueAxiomNoPayloadCaching() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
MessageContext messageContext = new DefaultMessageContext(messageFactory);
MyRootElement rootElement = new MyRootElement();
rootElement.setString("Foo");
this.processor.handleReturnValue(messageContext, this.rootElementReturnType, rootElement);
assertThat(messageContext.hasResponse()).isTrue();
AxiomSoapMessage response = (AxiomSoapMessage) messageContext.getResponse();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
response.writeTo(bos);
String messageResult = bos.toString("UTF-8");
XmlAssert.assertThat(messageResult)
.and("<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'><soapenv:Header/><soapenv:Body>"
+ "<root xmlns='http://springframework.org'><string>Foo</string></root>"
+ "</soapenv:Body></soapenv:Envelope>")
.ignoreWhitespace()
.areIdentical();
}
@ResponsePayload
public MyRootElement rootElement(@RequestPayload MyRootElement rootElement) {
return rootElement;

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.junit.jupiter.api.Test;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.soap11.AbstractSoap11BodyTest;
import org.springframework.xml.transform.StringSource;
public class AxiomSoap11BodyTest extends AbstractSoap11BodyTest {
@Override
protected SoapBody createSoapBody() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP11Factory();
AxiomSoapMessage axiomSoapMessage = new AxiomSoapMessage(axiomFactory);
return axiomSoapMessage.getSoapBody();
}
@Test
public void testPayloadNoCaching() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.setSoapVersion(SoapVersion.SOAP_11);
AxiomSoapMessage axiomSoapMessage = messageFactory.createWebServiceMessage();
this.soapBody = axiomSoapMessage.getSoapBody();
String payload = "<payload xmlns='http://www.springframework.org' />";
this.transformer.transform(new StringSource(payload), this.soapBody.getPayloadResult());
assertPayloadEqual(payload);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.soap.SoapEnvelope;
import org.springframework.ws.soap.soap11.AbstractSoap11EnvelopeTest;
public class AxiomSoap11EnvelopeTest extends AbstractSoap11EnvelopeTest {
@Override
protected SoapEnvelope createSoapEnvelope() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP11Factory();
AxiomSoapMessage axiomSoapMessage = new AxiomSoapMessage(axiomFactory);
return axiomSoapMessage.getEnvelope();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.soap11.AbstractSoap11HeaderTest;
public class AxiomSoap11HeaderTest extends AbstractSoap11HeaderTest {
@Override
protected SoapHeader createSoapHeader() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP11Factory();
AxiomSoapMessage axiomSoapMessage = new AxiomSoapMessage(axiomFactory);
return axiomSoapMessage.getSoapHeader();
}
@Override
public void testGetResult() {
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import org.junit.jupiter.api.Test;
import org.xmlunit.assertj.XmlAssert;
import org.springframework.ws.InvalidXmlException;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.soap.soap11.AbstractSoap11MessageFactoryTest;
import org.springframework.ws.transport.MockTransportInputStream;
import org.springframework.ws.transport.TransportInputStream;
import org.springframework.xml.transform.StringResult;
import org.springframework.xml.transform.TransformerFactoryUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
public class AxiomSoap11MessageFactoryTest extends AbstractSoap11MessageFactoryTest {
private Transformer transformer;
@Override
protected WebServiceMessageFactory createMessageFactory() throws Exception {
this.transformer = TransformerFactoryUtils.newInstance().newTransformer();
AxiomSoapMessageFactory factory = new AxiomSoapMessageFactory();
factory.afterPropertiesSet();
return factory;
}
@Override
public void doTestCreateSoapMessageIllFormedXml() {
// Axiom parses the contents of XML lazily, so it will not throw an
// InvalidXmlException when a message is parsed
throw new InvalidXmlException(null, null);
}
@Test
public void testGetCharsetEncoding() {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
assertThat(messageFactory.getCharSetEncoding("text/html; charset=utf-8")).isEqualTo("utf-8");
assertThat(messageFactory.getCharSetEncoding("application/xop+xml;type=text/xml; charset=utf-8"))
.isEqualTo("utf-8");
assertThat(messageFactory.getCharSetEncoding("application/xop+xml;type=\"text/xml; charset=utf-8\""))
.isEqualTo("utf-8");
}
@Test
public void testRepetitiveReadCaching() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(true);
messageFactory.afterPropertiesSet();
String xml = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'><soapenv:Body>"
+ "<root xmlns='http://springframework.org/spring-ws'><child /></root>"
+ "</soapenv:Body></soapenv:Envelope>";
TransportInputStream tis = new MockTransportInputStream(new ByteArrayInputStream(xml.getBytes()));
WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
StringResult result = new StringResult();
this.transformer.transform(message.getPayloadSource(), result);
this.transformer.transform(message.getPayloadSource(), result);
}
@Test
public void testRepetitiveReadNoCaching() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.afterPropertiesSet();
String xml = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'><soapenv:Body>"
+ "<root xmlns='http://springframework.org/spring-ws'><child /></root>"
+ "</soapenv:Body></soapenv:Envelope>";
TransportInputStream tis = new MockTransportInputStream(new ByteArrayInputStream(xml.getBytes()));
WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
StringResult result = new StringResult();
this.transformer.transform(message.getPayloadSource(), result);
try {
this.transformer.transform(message.getPayloadSource(), result);
fail("TransformerException expected");
}
catch (TransformerException expected) {
// ignore
}
}
/**
* See http://jira.springframework.org/browse/SWS-502
*/
@Test
public void testSWS502() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.afterPropertiesSet();
String envelope = "<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><soapenv:Body>"
+ "<ns1:sendMessageResponse xmlns:ns1='urn:Sole' soapenv:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>"
+ "<sendMessageReturn xsi:type='soapenc:string' xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/'>"
+ "<![CDATA[<?xml version='1.0' encoding='UTF-8'?>" + "<PDresponse>" + "<isStatusOK>true</isStatusOK>"
+ "<status>0</status>"
+ "<payLoad><![CDATA[<?xml version='1.0' encoding='UTF-8'?><response>ok</response>]]]]>><![CDATA[</payLoad>"
+ "</PDresponse>]]></sendMessageReturn>" + "</ns1:sendMessageResponse>"
+ "</soapenv:Body></soapenv:Envelope>";
InputStream inputStream = new ByteArrayInputStream(envelope.getBytes("UTF-8"));
AxiomSoapMessage message = messageFactory.createWebServiceMessage(new MockTransportInputStream(inputStream));
StringResult result = new StringResult();
this.transformer.transform(message.getPayloadSource(), result);
String expectedPayload = "<ns1:sendMessageResponse xmlns:ns1='urn:Sole' xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' soapenv:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>"
+ "<sendMessageReturn xsi:type='soapenc:string' xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/'>"
+ "<![CDATA[<?xml version='1.0' encoding='UTF-8'?>" + "<PDresponse>" + "<isStatusOK>true</isStatusOK>"
+ "<status>0</status>"
+ "<payLoad><![CDATA[<?xml version='1.0' encoding='UTF-8'?><response>ok</response>]]]]>><![CDATA[</payLoad>"
+ "</PDresponse>]]></sendMessageReturn>" + "</ns1:sendMessageResponse>";
XmlAssert.assertThat(result.toString()).and(expectedPayload).ignoreWhitespace().areIdentical();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.soap11.AbstractSoap11MessageTest;
public class AxiomSoap11MessageTest extends AbstractSoap11MessageTest {
@Override
protected String getNS() {
return "soapenv";
}
@Override
protected SoapMessage createSoapMessage() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP11Factory();
return new AxiomSoapMessage(axiomFactory);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.soap11.AbstractSoap11BodyTest;
public class AxiomSoap11NonCachingBodyTest extends AbstractSoap11BodyTest {
@Override
protected SoapBody createSoapBody() {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.setSoapVersion(SoapVersion.SOAP_11);
AxiomSoapMessage axiomSoapMessage = messageFactory.createWebServiceMessage();
return axiomSoapMessage.getSoapBody();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMSourcedElement;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.soap11.AbstractSoap11MessageTest;
import static org.assertj.core.api.Assertions.assertThat;
public class AxiomSoap11NonCachingMessageTest extends AbstractSoap11MessageTest {
@Override
protected String getNS() {
return "soapenv";
}
@Override
protected SoapMessage createSoapMessage() {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.setSoapVersion(SoapVersion.SOAP_11);
return messageFactory.createWebServiceMessage();
}
@Override
public void testWriteToTransportOutputStream() throws Exception {
super.testWriteToTransportOutputStream();
SoapBody body = this.soapMessage.getSoapBody();
OMSourcedElement axiomPayloadEle = (OMSourcedElement) ((AxiomSoapBody) body).getAxiomElement()
.getFirstElement();
assertThat(axiomPayloadEle.isExpanded()).isFalse();
axiomPayloadEle.getFirstElement();
assertThat(axiomPayloadEle.isExpanded()).isTrue();
assertThat(axiomPayloadEle.getLocalName()).isEqualTo("payload");
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.junit.jupiter.api.Test;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.soap12.AbstractSoap12BodyTest;
import org.springframework.xml.transform.StringSource;
public class AxiomSoap12BodyTest extends AbstractSoap12BodyTest {
@Override
protected SoapBody createSoapBody() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP12Factory();
AxiomSoapMessage axiomSoapMessage = new AxiomSoapMessage(axiomFactory);
return axiomSoapMessage.getSoapBody();
}
@Test
public void testPayloadNoCaching() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
AxiomSoapMessage axiomSoapMessage = messageFactory.createWebServiceMessage();
this.soapBody = axiomSoapMessage.getSoapBody();
String payload = "<payload xmlns='http://www.springframework.org' />";
this.transformer.transform(new StringSource(payload), this.soapBody.getPayloadResult());
assertPayloadEqual(payload);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.soap.SoapEnvelope;
import org.springframework.ws.soap.soap12.AbstractSoap12EnvelopeTest;
public class AxiomSoap12EnvelopeTest extends AbstractSoap12EnvelopeTest {
@Override
protected SoapEnvelope createSoapEnvelope() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP12Factory();
AxiomSoapMessage axiomSoapMessage = new AxiomSoapMessage(axiomFactory);
return axiomSoapMessage.getEnvelope();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.soap.SoapHeader;
import org.springframework.ws.soap.soap12.AbstractSoap12HeaderTest;
public class AxiomSoap12HeaderTest extends AbstractSoap12HeaderTest {
@Override
protected SoapHeader createSoapHeader() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP12Factory();
AxiomSoapMessage axiomSoapMessage = new AxiomSoapMessage(axiomFactory);
return axiomSoapMessage.getSoapHeader();
}
@Override
public void testGetResult() {
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.InvalidXmlException;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.soap12.AbstractSoap12MessageFactoryTest;
public class AxiomSoap12MessageFactoryTest extends AbstractSoap12MessageFactoryTest {
@Override
protected WebServiceMessageFactory createMessageFactory() throws Exception {
AxiomSoapMessageFactory factory = new AxiomSoapMessageFactory();
factory.setSoapVersion(SoapVersion.SOAP_12);
factory.afterPropertiesSet();
return factory;
}
@Override
public void doTestCreateSoapMessageIllFormedXml() {
// Axiom parses the contents of XML lazily, so it will not throw an
// InvalidXmlException when a message is parsed
throw new InvalidXmlException(null, null);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPFactory;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.soap12.AbstractSoap12MessageTest;
public class AxiomSoap12MessageTest extends AbstractSoap12MessageTest {
@Override
protected String getNS() {
return "soapenv";
}
@Override
protected SoapMessage createSoapMessage() {
SOAPFactory axiomFactory = OMAbstractFactory.getSOAP12Factory();
return new AxiomSoapMessage(axiomFactory);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.soap12.AbstractSoap12BodyTest;
public class AxiomSoap12NonCachingBodyTest extends AbstractSoap12BodyTest {
@Override
protected SoapBody createSoapBody() {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
AxiomSoapMessage axiomSoapMessage = messageFactory.createWebServiceMessage();
return axiomSoapMessage.getSoapBody();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import org.apache.axiom.om.OMSourcedElement;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.soap12.AbstractSoap12MessageTest;
import static org.assertj.core.api.Assertions.assertThat;
public class AxiomSoap12NonCachingMessageTest extends AbstractSoap12MessageTest {
@Override
protected String getNS() {
return "soapenv";
}
@Override
protected SoapMessage createSoapMessage() {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(false);
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
return messageFactory.createWebServiceMessage();
}
@Override
public void testWriteToTransportOutputStream() throws Exception {
super.testWriteToTransportOutputStream();
SoapBody body = this.soapMessage.getSoapBody();
OMSourcedElement axiomPayloadEle = (OMSourcedElement) ((AxiomSoapBody) body).getAxiomElement()
.getFirstElement();
assertThat(axiomPayloadEle.isExpanded()).isFalse();
axiomPayloadEle.getFirstElement();
assertThat(axiomPayloadEle.isExpanded()).isTrue();
assertThat(axiomPayloadEle.getLocalName()).isEqualTo("payload");
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.StringReader;
import org.apache.axiom.om.OMXMLBuilderFactory;
import org.apache.axiom.soap.SOAPMessage;
import org.apache.axiom.soap.SOAPModelBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapFaultDetail;
import static org.assertj.core.api.Assertions.assertThat;
@SuppressWarnings("Since15")
public class AxiomSoapFaultDetailTest {
private static final String FAILING_FAULT = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n " + "<soapenv:Body>\n "
+ "<soapenv:Fault>\n " + "<faultcode>Client</faultcode>\n "
+ "<faultstring>Client Error</faultstring>\n " + "<detail>\n "
+ "<ns1:dispositionReport xmlns:ns1=\"urn:uddi-org:api_v3\">\n " + "<ns1:result errno=\"10210\"/>\n "
+ "</ns1:dispositionReport>" + "</detail>" + "</soapenv:Fault>" + "</soapenv:Body>" + "</soapenv:Envelope>";
private static final String SUCCEEDING_FAULT = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+ "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" "
+ "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n " + "<soapenv:Body>\n "
+ "<soapenv:Fault>\n " + "<faultcode>Client</faultcode>\n "
+ "<faultstring>Client Error</faultstring>\n " + "<detail>"
+ "<ns1:dispositionReport xmlns:ns1=\"urn:uddi-org:api_v3\">\n " + "<ns1:result errno=\"10210\"/>\n "
+ "</ns1:dispositionReport>" + "</detail>" + "</soapenv:Fault>" + "</soapenv:Body>" + "</soapenv:Envelope>";
private AxiomSoapMessage failingMessage;
private AxiomSoapMessage succeedingMessage;
@BeforeEach
public void setUp() throws Exception {
SOAPModelBuilder builder = OMXMLBuilderFactory.createSOAPModelBuilder(new StringReader(FAILING_FAULT));
SOAPMessage soapMessage = builder.getSOAPMessage();
this.failingMessage = new AxiomSoapMessage(soapMessage, null, false, true);
builder = OMXMLBuilderFactory.createSOAPModelBuilder(new StringReader(SUCCEEDING_FAULT));
soapMessage = builder.getSOAPMessage();
this.succeedingMessage = new AxiomSoapMessage(soapMessage, null, false, true);
}
@Test
public void testGetDetailEntriesWorksWithWhitespaceNodes() {
SoapFault fault = this.failingMessage.getSoapBody().getFault();
assertThat(fault).isNotNull();
assertThat(fault.getFaultDetail()).isNotNull();
SoapFaultDetail detail = fault.getFaultDetail();
assertThat(detail.getDetailEntries().hasNext()).isTrue();
detail.getDetailEntries().next();
}
@Test
public void testGetDetailEntriesWorksWithoutWhitespaceNodes() {
SoapFault fault = this.succeedingMessage.getSoapBody().getFault();
assertThat(fault).isNotNull();
assertThat(fault.getFaultDetail()).isNotNull();
SoapFaultDetail detail = fault.getFaultDetail();
assertThat(detail.getDetailEntries().hasNext()).isTrue();
detail.getDetailEntries().next();
}
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom;
import java.io.StringWriter;
import javax.xml.stream.XMLStreamWriter;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.soap.SOAPBody;
import org.apache.axiom.soap.SOAPFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.util.xml.StaxUtils;
import static org.xmlunit.assertj.XmlAssert.assertThat;
@SuppressWarnings("Since15")
public class NonCachingPayloadTest {
private Payload payload;
private SOAPBody body;
@BeforeEach
public final void setUp() {
SOAPFactory soapFactory = OMAbstractFactory.getSOAP11Factory();
this.body = soapFactory.createSOAPBody();
this.payload = new NonCachingPayload(this.body, soapFactory);
}
@Test
public void testDelegatingStreamWriter() throws Exception {
XMLStreamWriter streamWriter = StaxUtils.getXMLStreamWriter(this.payload.getResult());
String namespace = "http://springframework.org/spring-ws";
streamWriter.setDefaultNamespace(namespace);
streamWriter.writeStartElement(namespace, "root");
streamWriter.writeDefaultNamespace(namespace);
streamWriter.writeStartElement(namespace, "child");
streamWriter.writeCharacters("text");
streamWriter.writeEndElement();
streamWriter.writeEndElement();
streamWriter.flush();
StringWriter writer = new StringWriter();
this.body.serialize(writer);
String expected = "<soapenv:Body xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'>"
+ "<root xmlns='http://springframework.org/spring-ws'>" + "<child>text</child>"
+ "</root></soapenv:Body>";
assertThat(writer.toString()).and(expected).ignoreWhitespace().areIdentical();
}
@Test
public void testDelegatingStreamWriterWriteEndDocument() throws Exception {
XMLStreamWriter streamWriter = StaxUtils.getXMLStreamWriter(this.payload.getResult());
String namespace = "http://springframework.org/spring-ws";
streamWriter.setDefaultNamespace(namespace);
streamWriter.writeStartElement(namespace, "root");
streamWriter.writeDefaultNamespace(namespace);
streamWriter.writeStartElement(namespace, "child");
streamWriter.writeCharacters("text");
streamWriter.writeEndDocument();
streamWriter.flush();
StringWriter writer = new StringWriter();
this.body.serialize(writer);
String expected = "<soapenv:Body xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'>"
+ "<root xmlns='http://springframework.org/spring-ws'>" + "<child>text</child>"
+ "</root></soapenv:Body>";
assertThat(writer.toString()).and(expected).ignoreWhitespace().areIdentical();
}
@Test
public void testDelegatingStreamWriterWriteEmptyElement() throws Exception {
XMLStreamWriter streamWriter = StaxUtils.getXMLStreamWriter(this.payload.getResult());
String namespace = "http://springframework.org/spring-ws";
streamWriter.setDefaultNamespace(namespace);
streamWriter.writeStartElement(namespace, "root");
streamWriter.writeDefaultNamespace(namespace);
streamWriter.writeEmptyElement(namespace, "child");
streamWriter.writeEndElement();
streamWriter.flush();
StringWriter writer = new StringWriter();
this.body.serialize(writer);
String expected = "<soapenv:Body xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'>"
+ "<root xmlns='http://springframework.org/spring-ws'>" + "<child />" + "</root></soapenv:Body>";
assertThat(writer.toString()).and(expected).ignoreWhitespace().areIdentical();
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.axiom.support;
import java.io.StringWriter;
import java.util.Locale;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axiom.om.OMXMLBuilderFactory;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axiom.soap.SOAPMessage;
import org.apache.axiom.soap.SOAPModelBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import org.xmlunit.assertj.XmlAssert;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.FileCopyUtils;
import org.springframework.xml.DocumentBuilderFactoryUtils;
import org.springframework.xml.sax.SaxUtils;
import static org.assertj.core.api.Assertions.assertThat;
public class AxiomUtilsTest {
private OMElement element;
@BeforeEach
public void setUp() throws Exception {
OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace namespace = factory.createOMNamespace("http://www.springframework.org", "prefix");
this.element = factory.createOMElement("element", namespace);
}
@Test
public void testToNamespaceDeclared() {
QName qName = new QName(this.element.getNamespace().getNamespaceURI(), "localPart");
OMNamespace namespace = AxiomUtils.toNamespace(qName, this.element);
assertThat(namespace).isNotNull();
assertThat(namespace.getNamespaceURI()).isEqualTo(qName.getNamespaceURI());
}
@Test
public void testToNamespaceUndeclared() {
QName qName = new QName("http://www.example.com", "localPart");
OMNamespace namespace = AxiomUtils.toNamespace(qName, this.element);
assertThat(namespace).isNotNull();
assertThat(namespace.getNamespaceURI()).isEqualTo(qName.getNamespaceURI());
assertThat(namespace.getPrefix()).isNotEqualTo("prefix");
}
@Test
public void testToNamespacePrefixDeclared() {
QName qName = new QName(this.element.getNamespace().getNamespaceURI(), "localPart", "prefix");
OMNamespace namespace = AxiomUtils.toNamespace(qName, this.element);
assertThat(namespace).isNotNull();
assertThat(namespace.getNamespaceURI()).isEqualTo(qName.getNamespaceURI());
assertThat(namespace.getPrefix()).isEqualTo("prefix");
}
@Test
public void testToNamespacePrefixUndeclared() {
QName qName = new QName("http://www.example.com", "localPart", "otherPrefix");
OMNamespace namespace = AxiomUtils.toNamespace(qName, this.element);
assertThat(namespace).isNotNull();
assertThat(namespace.getNamespaceURI()).isEqualTo(qName.getNamespaceURI());
assertThat(namespace.getPrefix()).isEqualTo(qName.getPrefix());
}
@Test
public void testToLanguage() {
assertThat(AxiomUtils.toLanguage(Locale.CANADA_FRENCH)).isEqualTo("fr-CA");
assertThat(AxiomUtils.toLanguage(Locale.ENGLISH)).isEqualTo("en");
}
@Test
public void testToLocale() {
assertThat(AxiomUtils.toLocale("fr-CA")).isEqualTo(Locale.CANADA_FRENCH);
assertThat(AxiomUtils.toLocale("en")).isEqualTo(Locale.ENGLISH);
}
@Test
@SuppressWarnings("Since15")
public void testToDocument() throws Exception {
Resource resource = new ClassPathResource("org/springframework/ws/soap/soap11/soap11.xml");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactoryUtils.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document expected = documentBuilder.parse(SaxUtils.createInputSource(resource));
SOAPModelBuilder builder = OMXMLBuilderFactory.createSOAPModelBuilder(resource.getInputStream(), null);
SOAPMessage soapMessage = builder.getSOAPMessage();
Document result = AxiomUtils.toDocument(soapMessage.getSOAPEnvelope());
XmlAssert.assertThat(result).and(expected).ignoreWhitespace().areIdentical();
}
@Test
public void testToEnvelope() throws Exception {
Resource resource = new ClassPathResource("org/springframework/ws/soap/soap11/soap11.xml");
byte[] buf = FileCopyUtils.copyToByteArray(resource.getFile());
String expected = new String(buf, "UTF-8");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactoryUtils.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(SaxUtils.createInputSource(resource));
SOAPEnvelope envelope = AxiomUtils.toEnvelope(document);
StringWriter writer = new StringWriter();
envelope.serialize(writer);
String result = writer.toString();
XmlAssert.assertThat(result).and(expected).ignoreWhitespace().areIdentical();
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.ws.soap.server.endpoint.interceptor;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Locale;
@@ -29,19 +30,26 @@ import jakarta.xml.soap.SOAPMessage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.LocatorImpl;
import org.xmlunit.assertj.XmlAssert;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.ws.MockWebServiceMessage;
import org.springframework.ws.MockWebServiceMessageFactory;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.ws.soap.saaj.support.SaajUtils;
import org.springframework.ws.soap.soap11.Soap11Fault;
import org.springframework.ws.soap.soap12.Soap12Fault;
import org.springframework.ws.transport.MockTransportInputStream;
import org.springframework.ws.transport.TransportInputStream;
import org.springframework.xml.transform.TransformerFactoryUtils;
import org.springframework.xml.validation.ValidationErrorHandler;
import org.springframework.xml.xsd.SimpleXsdSchema;
@@ -316,6 +324,30 @@ public class PayloadValidatingInterceptorTest {
assertThat(result).isTrue();
}
@Test
public void testCreateRequestValidationFaultAxiom() throws Exception {
LocatorImpl locator = new LocatorImpl();
locator.setLineNumber(0);
locator.setColumnNumber(0);
SAXParseException[] exceptions = new SAXParseException[] { new SAXParseException("Message 1", locator),
new SAXParseException("Message 2", locator), };
MessageContext messageContext = new DefaultMessageContext(new AxiomSoapMessageFactory());
this.interceptor.handleRequestValidationErrors(messageContext, exceptions);
ByteArrayOutputStream os = new ByteArrayOutputStream();
messageContext.getResponse().writeTo(os);
XmlAssert.assertThat(os.toString())
.and("<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<soapenv:Header/>"
+ "<soapenv:Body>" + "<soapenv:Fault>" + "<faultcode>soapenv:Client</faultcode>"
+ "<faultstring xml:lang='en'>Validation error</faultstring>" + "<detail>"
+ "<spring-ws:ValidationError xmlns:spring-ws=\"http://springframework.org/spring-ws\">Message 1</spring-ws:ValidationError>"
+ "<spring-ws:ValidationError xmlns:spring-ws=\"http://springframework.org/spring-ws\">Message 2</spring-ws:ValidationError>"
+ "</detail>" + "</soapenv:Fault>" + "</soapenv:Body>" + "</soapenv:Envelope>")
.ignoreWhitespace()
.areIdentical();
}
@Test
public void testXsdSchema() throws Exception {
@@ -335,6 +367,46 @@ public class PayloadValidatingInterceptorTest {
assertThat(this.context.hasResponse()).isFalse();
}
@Test
public void testAxiom() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(true);
messageFactory.afterPropertiesSet();
PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
interceptor.setSchema(new ClassPathResource("codexws.xsd", getClass()));
interceptor.afterPropertiesSet();
Resource resource = new ClassPathResource("axiom.xml", getClass());
TransportInputStream tis = new MockTransportInputStream(resource.getInputStream());
WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
MessageContext context = new DefaultMessageContext(message, messageFactory);
boolean result = interceptor.handleRequest(context, null);
assertThat(result).isTrue();
}
@Test
public void testMultipleNamespacesAxiom() throws Exception {
AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
messageFactory.setPayloadCaching(true);
messageFactory.afterPropertiesSet();
PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
interceptor.setSchema(new ClassPathResource("multipleNamespaces.xsd", getClass()));
interceptor.afterPropertiesSet();
Resource resource = new ClassPathResource("multipleNamespaces.xml", getClass());
TransportInputStream tis = new MockTransportInputStream(resource.getInputStream());
WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
MessageContext context = new DefaultMessageContext(message, messageFactory);
boolean result = interceptor.handleRequest(context, null);
assertThat(result).isTrue();
}
@Test
public void customErrorHandler() throws Exception {

View File

@@ -199,7 +199,8 @@ The following example shows how to use the XMPP transport:
==== Message factories
In addition to a message sender, the `WebServiceTemplate` requires a web service message factory.
By default, `SaajSoapMessageFactory` is used.
There are two message factories for SOAP: `SaajSoapMessageFactory` and `AxiomSoapMessageFactory`.
If no message factory is specified (by setting the `messageFactory` property), Spring-WS uses the `SaajSoapMessageFactory` by default.
=== Sending and Receiving a `WebServiceMessage`

View File

@@ -59,7 +59,9 @@ Only when it is necessary to perform SOAP-specific actions (such as adding a hea
Concrete message implementations are created by a `WebServiceMessageFactory`.
This factory can create an empty message or read a message from an input stream.
One concrete implementations of `WebServiceMessageFactory` is provided based on SAAJ, the SOAP with Attachments API for Java.
There are two concrete implementations of `WebServiceMessageFactory`.
One is based on SAAJ, the SOAP with Attachments API for Java.
The other is based on Axis 2s AXIOM (AXis Object Model).
==== `SaajSoapMessageFactory`
@@ -98,14 +100,48 @@ You can wire up a `SaajSoapMessageFactory` as follows:
----
====
NOTE: SAAJ is based on DOM, the Document Object Model.
[NOTE]
====
SAAJ is based on DOM, the Document Object Model.
This means that all SOAP messages are stored in memory.
For larger SOAP messages, this may not be performant.
In that case, the `AxiomSoapMessageFactory` might be more applicable.
====
==== `AxiomSoapMessageFactory`
The `AxiomSoapMessageFactory` uses the AXis 2 Object Model (AXIOM) to create `SoapMessage` implementations.
AXIOM is based on StAX, the Streaming API for XML.
StAX provides a pull-based mechanism for reading XML messages, which can be more efficient for larger messages.
To increase reading performance on the `AxiomSoapMessageFactory`, you can set the `payloadCaching` property to false (default is true).
Doing so causes the contents of the SOAP body to be read directly from the socket stream.
When this setting is enabled, the payload can be read only once.
This means that you have to make sure that any pre-processing (logging or other work) of the message does not consume it.
You can use the `AxiomSoapMessageFactory` as follows:
====
[source,xml]
----
<bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
<property name="payloadCaching" value="true"/>
</bean>
----
====
In addition to payload caching, AXIOM supports full streaming messages, as defined in the `StreamingWebServiceMessage`.
This means that you can directly set the payload on the response message, rather than writing it to a DOM tree or buffer.
Full streaming for AXIOM is used when a handler method returns a JAXB2-supported object.
It automatically sets this marshalled object into the response message and writes it out to the outgoing socket stream when the response is going out.
For more information about full streaming, see {spring-ws-api}/stream/StreamingWebServiceMessage.html[`StreamingWebServiceMessage`] and {spring-ws-api}/stream/StreamingPayload.html[`StreamingPayload`].
[[soap_11_or_12]]
==== SOAP 1.1 or 1.2
`SaajSoapMessageFactory` has a `soapVersion` property, where you can inject a `SoapVersion` constant.
Both the `SaajSoapMessageFactory` and the `AxiomSoapMessageFactory` have a `soapVersion` property, where you can inject a `SoapVersion` constant.
By default, the version is 1.1, but you can set it to 1.2:
====

View File

@@ -729,7 +729,7 @@ WSS4J implements the following standards:
* Username Token profile V1.0.
* X.509 Token Profile V1.0.
This interceptor supports messages created by the `SaajSoapMessageFactory`.
This interceptor supports messages created by the `AxiomSoapMessageFactory` and the `SaajSoapMessageFactory`.
=== Configuring `Wss4jSecurityInterceptor`

View File

@@ -36,6 +36,11 @@ dependencies {
api("org.apache.httpcomponents:httpclient:4.5.14")
api("org.apache.santuario:xmlsec:4.0.3")
api("org.apache.wss4j:wss4j-ws-security-dom:4.0.0")
api("org.apache.ws.commons.axiom:axiom-api:2.0.0")
api("org.apache.ws.commons.axiom:axiom-compat:2.0.0")
api("org.apache.ws.commons.axiom:axiom-dom:2.0.0")
api("org.apache.ws.commons.axiom:axiom-impl:2.0.0")
api("org.apache.ws.commons.axiom:axiom-legacy-attachments:2.0.0")
api("org.apache.ws.xmlschema:xmlschema-core:2.3.1")
api("org.aspectj:aspectjrt:1.9.22")
api("org.aspectj:aspectjweaver:1.9.22")

View File

@@ -22,6 +22,12 @@ dependencies {
api("org.springframework.security:spring-security-core")
optional("com.sun.xml.messaging.saaj:saaj-impl")
optional("org.apache.ws.commons.axiom:axiom-impl") {
exclude(group: "commons-logging", module: "commons-logging")
}
optional("org.apache.ws.commons.axiom:axiom-legacy-attachments") {
exclude(group: "commons-logging", module: "commons-logging")
}
testImplementation("org.apache.logging.log4j:log4j-core")
testImplementation("org.apache.logging.log4j:log4j-slf4j2-impl")

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jInterceptorTest extends Wss4jInterceptorTest {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorEncryptionTest extends Wss4jMessageInterceptorEncryptionTest {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorHeaderTest extends Wss4jMessageInterceptorHeaderTest {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorSamlTest extends Wss4jMessageInterceptorSamlTest {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorSignTest extends Wss4jMessageInterceptorSignTest {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorSoapActionTest extends Wss4jMessageInterceptorSoapActionTest {
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorSpringSecurityCallbackHandlerTest
extends Wss4jMessageInterceptorSpringSecurityCallbackHandlerTest {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorTimestampTest extends Wss4jMessageInterceptorTimestampTest {
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorUsernameTokenSignatureTest
extends Wss4jMessageInterceptorUsernameTokenSignatureTest {
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
public class AxiomWss4jMessageInterceptorUsernameTokenTest extends Wss4jMessageInterceptorUsernameTokenTest {
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2005-2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.soap.security.wss4j2;
/**
* @author tareq
*/
public class AxiomWss4jMessageInterceptorX509Test extends Wss4jMessageInterceptorX509Test {
}

View File

@@ -26,6 +26,8 @@ import jakarta.xml.soap.MessageFactory;
import jakarta.xml.soap.MimeHeaders;
import jakarta.xml.soap.SOAPConstants;
import jakarta.xml.soap.SOAPMessage;
import org.apache.axiom.om.OMXMLBuilderFactory;
import org.apache.axiom.soap.SOAPModelBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
@@ -38,6 +40,9 @@ import org.springframework.ws.context.MessageContext;
import org.springframework.ws.soap.SoapMessage;
import org.springframework.ws.soap.SoapMessageFactory;
import org.springframework.ws.soap.SoapVersion;
import org.springframework.ws.soap.axiom.AxiomSoapMessage;
import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
import org.springframework.ws.soap.axiom.support.AxiomUtils;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
import org.springframework.xml.transform.StringSource;
@@ -51,6 +56,8 @@ public abstract class Wss4jTest {
protected MessageFactory saajSoap12MessageFactory;
protected final boolean axiomTest = this.getClass().getSimpleName().startsWith("Axiom");
protected final boolean saajTest = this.getClass().getSimpleName().startsWith("Saaj");
protected Jaxp13XPathTemplate xpathTemplate = new Jaxp13XPathTemplate();
@@ -58,7 +65,7 @@ public abstract class Wss4jTest {
@BeforeEach
public final void setUp() throws Exception {
if (!this.saajTest) {
if (!this.axiomTest && !this.saajTest) {
throw new IllegalArgumentException("test class name must start with Saaj");
}
@@ -145,22 +152,57 @@ public abstract class Wss4jTest {
}
}
protected Object getMessage(SoapMessage soapMessage) {
protected AxiomSoapMessage loadAxiom11Message(String fileName) throws Exception {
Resource resource = new ClassPathResource(fileName, getClass());
assertThat(resource.exists()).isTrue();
try (InputStream is = resource.getInputStream()) {
SOAPModelBuilder builder = OMXMLBuilderFactory.createSOAPModelBuilder(is, null);
org.apache.axiom.soap.SOAPMessage soapMessage = builder.getSOAPMessage();
builder.detach();
return new AxiomSoapMessage(soapMessage, "", true, true);
}
}
@SuppressWarnings("Since15")
protected AxiomSoapMessage loadAxiom12Message(String fileName) throws Exception {
Resource resource = new ClassPathResource(fileName, getClass());
assertThat(resource.exists()).isTrue();
try (InputStream is = resource.getInputStream()) {
SOAPModelBuilder builder = OMXMLBuilderFactory.createSOAPModelBuilder(is, null);
org.apache.axiom.soap.SOAPMessage soapMessage = builder.getSOAPMessage();
builder.detach();
return new AxiomSoapMessage(soapMessage, "", true, true);
}
}
protected Object getMessage(SoapMessage soapMessage) {
if (soapMessage instanceof AxiomSoapMessage) {
return ((AxiomSoapMessage) soapMessage).getAxiomMessage();
}
if (soapMessage instanceof SaajSoapMessage) {
return ((SaajSoapMessage) soapMessage).getSaajMessage();
}
throw new IllegalArgumentException("Illegal message: " + soapMessage);
}
protected void setMessage(SoapMessage soapMessage, Object message) {
if (soapMessage instanceof AxiomSoapMessage) {
((AxiomSoapMessage) soapMessage).setAxiomMessage((org.apache.axiom.soap.SOAPMessage) message);
return;
}
if (soapMessage instanceof SaajSoapMessage) {
((SaajSoapMessage) soapMessage).setSaajMessage((SOAPMessage) message);
return;
}
throw new IllegalArgumentException("Illegal message: " + message);
}
@@ -168,46 +210,54 @@ public abstract class Wss4jTest {
}
protected SoapMessage loadSoap11Message(String fileName) throws Exception {
if (this.axiomTest) {
return loadAxiom11Message(fileName);
}
if (this.saajTest) {
return loadSaaj11Message(fileName);
}
throw new IllegalArgumentException();
}
protected SoapMessage loadSoap12Message(String fileName) throws Exception {
if (this.axiomTest) {
return loadAxiom12Message(fileName);
}
if (this.saajTest) {
return loadSaaj12Message(fileName);
}
throw new IllegalArgumentException();
}
protected SoapMessageFactory getSoap11MessageFactory() {
if (this.axiomTest) {
return new AxiomSoapMessageFactory();
}
if (this.saajTest) {
return new SaajSoapMessageFactory(this.saajSoap11MessageFactory);
}
throw new IllegalArgumentException();
}
protected SoapMessageFactory getSoap12MessageFactory() {
SoapMessageFactory messageFactory;
if (this.saajTest) {
if (this.axiomTest) {
messageFactory = new AxiomSoapMessageFactory();
}
else if (this.saajTest) {
messageFactory = new SaajSoapMessageFactory(this.saajSoap12MessageFactory);
}
else
else {
throw new IllegalArgumentException();
}
messageFactory.setSoapVersion(SoapVersion.SOAP_12);
return messageFactory;
}
protected Document getDocument(SoapMessage message) {
if (this.axiomTest) {
return AxiomUtils.toDocument(((AxiomSoapMessage) message).getAxiomMessage().getSOAPEnvelope());
}
if (this.saajTest) {
return ((SaajSoapMessage) message).getSaajMessage().getSOAPPart();
}