/*
* Copyright 2004-2014 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wsdl.support.soap;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import javax.wsdl.BindingInput;
import javax.wsdl.BindingOperation;
import javax.wsdl.BindingOutput;
import javax.wsdl.Message;
import javax.wsdl.Part;
import javax.xml.namespace.QName;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.SchemaGlobalElement;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlInterface;
import com.eviware.soapui.impl.wsdl.support.Constants;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlContext;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils.SoapHeader;
import com.eviware.soapui.impl.wsdl.support.xsd.SampleXmlUtil;
import com.eviware.soapui.model.iface.Interface;
import com.eviware.soapui.model.iface.MessageBuilder;
import com.eviware.soapui.model.iface.MessagePart.FaultPart;
import com.eviware.soapui.settings.WsdlSettings;
import com.eviware.soapui.support.xml.XmlUtils;
/**
* Builds SOAP requests according to WSDL/XSD definitions
*
* @author Ole.Matzura
*/
public class SoapMessageBuilder implements MessageBuilder {
private final static Logger log = Logger.getLogger(SoapMessageBuilder.class);
private WsdlContext wsdlContext;
private WsdlInterface iface;
private Map<QName, String[]> multiValues = null;
public SoapMessageBuilder(WsdlInterface iface) throws Exception {
this.iface = iface;
this.wsdlContext = iface.getWsdlContext();
}
public SoapMessageBuilder(WsdlContext wsdlContext) {
this.wsdlContext = wsdlContext;
}
public void setMultiValues(Map<QName, String[]> multiValues) {
this.multiValues = multiValues;
}
public Interface getInterface() {
return iface;
}
public String buildFault(String faultcode, String faultstring) {
return buildFault(faultcode, faultstring, getSoapVersion());
}
public SoapVersion getSoapVersion() {
return iface == null ? wsdlContext.getSoapVersion() : iface.getSoapVersion();
}
public static String buildFault(String faultcode, String faultstring, SoapVersion soapVersion) {
SampleXmlUtil generator = new SampleXmlUtil(false);
generator.setTypeComment(false);
generator.setIgnoreOptional(true);
String emptyResponse = buildEmptyFault(generator, soapVersion);
if (soapVersion == SoapVersion.Soap11) {
emptyResponse = XmlUtils.setXPathContent(emptyResponse, "//faultcode", faultcode);
emptyResponse = XmlUtils.setXPathContent(emptyResponse, "//faultstring", faultstring);
} else if (soapVersion == SoapVersion.Soap12) {
emptyResponse = XmlUtils.setXPathContent(emptyResponse, "//soap:Value", faultcode);
emptyResponse = XmlUtils.setXPathContent(emptyResponse, "//soap:Text", faultstring);
emptyResponse = XmlUtils.setXPathContent(emptyResponse, "//soap:Text/@xml:lang", "en");
}
return emptyResponse;
}
public String buildEmptyFault() {
return buildEmptyFault(getSoapVersion());
}
public static String buildEmptyFault(SoapVersion soapVersion) {
SampleXmlUtil generator = new SampleXmlUtil(false);
String emptyResponse = buildEmptyFault(generator, soapVersion);
return emptyResponse;
}
private static String buildEmptyFault(SampleXmlUtil generator, SoapVersion soapVersion) {
String emptyResponse = buildEmptyMessage(soapVersion);
try {
// XmlObject xmlObject = XmlObject.Factory.parse( emptyResponse );
XmlObject xmlObject = XmlUtils.createXmlObject(emptyResponse);
XmlCursor cursor = xmlObject.newCursor();
if (cursor.toChild(soapVersion.getEnvelopeQName()) && cursor.toChild(soapVersion.getBodyQName())) {
SchemaType faultType = soapVersion.getFaultType();
Node bodyNode = cursor.getDomNode();
Document dom = XmlUtils.parseXml(generator.createSample(faultType));
bodyNode.appendChild(bodyNode.getOwnerDocument().importNode(dom.getDocumentElement(), true));
}
cursor.dispose();
emptyResponse = xmlObject.toString();
} catch (Exception e) {
SoapUI.logError(e);
}
return emptyResponse;
}
public String buildEmptyMessage() {
return buildEmptyMessage(getSoapVersion());
}
public static String buildEmptyMessage(SoapVersion soapVersion) {
SampleXmlUtil generator = new SampleXmlUtil(false);
generator.setTypeComment(false);
generator.setIgnoreOptional(true);
return generator.createSample(soapVersion.getEnvelopeType());
}
public String buildSoapMessageFromInput(BindingOperation bindingOperation, boolean buildOptional) throws Exception {
return buildSoapMessageFromInput(bindingOperation, buildOptional, true);
}
public String buildSoapMessageFromInput(BindingOperation bindingOperation, boolean buildOptional,
boolean alwaysBuildHeaders) throws Exception {
boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded(bindingOperation);
SampleXmlUtil xmlGenerator = new SampleXmlUtil(inputSoapEncoded);
xmlGenerator.setMultiValues(multiValues);
xmlGenerator.setIgnoreOptional(!buildOptional);
XmlObject object = XmlObject.Factory.newInstance();
XmlCursor cursor = object.newCursor();
cursor.toNextToken();
cursor.beginElement(wsdlContext.getSoapVersion().getEnvelopeQName());
if (inputSoapEncoded) {
cursor.insertNamespace("xsi", Constants.XSI_NS);
cursor.insertNamespace("xsd", Constants.XSD_NS);
}
cursor.toFirstChild();
cursor.beginElement(wsdlContext.getSoapVersion().getBodyQName());
cursor.toFirstChild();
if (WsdlUtils.isRpc(wsdlContext.getDefinition(), bindingOperation)) {
buildRpcRequest(bindingOperation, cursor, xmlGenerator);
} else {
buildDocumentRequest(bindingOperation, cursor, xmlGenerator);
}
if (alwaysBuildHeaders) {
BindingInput bindingInput = bindingOperation.getBindingInput();
if (bindingInput != null) {
List<?> extensibilityElements = bindingInput.getExtensibilityElements();
List<SoapHeader> soapHeaders = WsdlUtils.getSoapHeaders(extensibilityElements);
addHeaders(soapHeaders, cursor, xmlGenerator);
}
}
cursor.dispose();
try {
StringWriter writer = new StringWriter();
XmlUtils.serializePretty(object, writer);
return writer.toString();
} catch (Exception e) {
SoapUI.logError(e);
return object.xmlText();
}
}
private void addHeaders(List<SoapHeader> headers, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception {
// reposition
cursor.toStartDoc();
cursor.toChild(wsdlContext.getSoapVersion().getEnvelopeQName());
cursor.toFirstChild();
cursor.beginElement(wsdlContext.getSoapVersion().getHeaderQName());
cursor.toFirstChild();
for (int i = 0; i < headers.size(); i++) {
SoapHeader header = headers.get(i);
Message message = wsdlContext.getDefinition().getMessage(header.getMessage());
if (message == null) {
log.error("Missing message for header: " + header.getMessage());
continue;
}
Part part = message.getPart(header.getPart());
if (part != null) {
createElementForPart(part, cursor, xmlGenerator);
} else {
log.error("Missing part for header; " + header.getPart());
}
}
}
public void createElementForPart(Part part, XmlCursor cursor, SampleXmlUtil xmlGenerator) throws Exception {
QName elementName = part.getElementName();
QName typeName = part.getTypeName();
if (elementName != null) {
cursor.beginElement(elementName);
if (wsdlContext.hasSchemaTypes()) {
SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement(elementName);
if (elm != null) {
cursor.toFirstChild();
xmlGenerator.createSampleForType(elm.getType(), cursor);
} else {
log.error("Could not find element [" + elementName + "] specified in part [" + part.getName() + "]");
}
}
cursor.toParent();
} else {
// cursor.beginElement( new QName(
// wsdlContext.getWsdlDefinition().getTargetNamespace(), part.getName()
// ));
cursor.beginElement(part.getName());
if (typeName != null && wsdlContext.hasSchemaTypes()) {
SchemaType type = wsdlContext.getSchemaTypeLoader().findType(typeName);
if (type != null) {
cursor.toFirstChild();
xmlGenerator.createSampleForType(type, cursor);
} else {
log.error("Could not find type [" + typeName + "] specified in part [" + part.getName() + "]");
}
}
cursor.toParent();
}
}
private void buildDocumentRequest(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator)
throws Exception {
Part[] parts = WsdlUtils.getInputParts(bindingOperation);
for (int i = 0; i < parts.length; i++) {
Part part = parts[i];
if (!WsdlUtils.isAttachmentInputPart(part, bindingOperation)
&& (part.getElementName() != null || part.getTypeName() != null)) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
createElementForPart(part, c, xmlGenerator);
c.dispose();
}
}
}
private void buildDocumentResponse(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator)
throws Exception {
Part[] parts = WsdlUtils.getOutputParts(bindingOperation);
for (int i = 0; i < parts.length; i++) {
Part part = parts[i];
if (!WsdlUtils.isAttachmentOutputPart(part, bindingOperation)
&& (part.getElementName() != null || part.getTypeName() != null)) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
createElementForPart(part, c, xmlGenerator);
c.dispose();
}
}
}
private void buildRpcRequest(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator)
throws Exception {
// rpc requests use the operation name as root element
String ns = WsdlUtils.getSoapBodyNamespace(bindingOperation.getBindingInput().getExtensibilityElements());
if (ns == null) {
ns = WsdlUtils.getTargetNamespace(wsdlContext.getDefinition());
log.warn("missing namespace on soapbind:body for RPC request, using targetNamespace instead (BP violation)");
}
cursor.beginElement(new QName(ns, bindingOperation.getName()));
if (xmlGenerator.isSoapEnc()) {
cursor.insertAttributeWithValue(new QName(wsdlContext.getSoapVersion().getEnvelopeNamespace(),
"encodingStyle"), wsdlContext.getSoapVersion().getEncodingNamespace());
}
Part[] inputParts = WsdlUtils.getInputParts(bindingOperation);
for (int i = 0; i < inputParts.length; i++) {
Part part = inputParts[i];
if (WsdlUtils.isAttachmentInputPart(part, bindingOperation)) {
if (iface.getSettings().getBoolean(WsdlSettings.ATTACHMENT_PARTS)) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.beginElement(part.getName());
c.insertAttributeWithValue("href", part.getName() + "Attachment");
c.dispose();
}
} else {
if (wsdlContext.hasSchemaTypes()) {
QName typeName = part.getTypeName();
if (typeName != null) {
SchemaType type = wsdlContext.getInterfaceDefinition().findType(typeName);
if (type != null) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement(part.getName());
c.toPrevToken();
xmlGenerator.createSampleForType(type, c);
c.dispose();
} else {
log.warn("Failed to find type [" + typeName + "]");
}
} else {
SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement(part.getElementName());
if (element != null) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement(element.getName());
c.toPrevToken();
xmlGenerator.createSampleForType(element.getType(), c);
c.dispose();
} else {
log.warn("Failed to find element [" + part.getElementName() + "]");
}
}
}
}
}
}
private void buildRpcResponse(BindingOperation bindingOperation, XmlCursor cursor, SampleXmlUtil xmlGenerator)
throws Exception {
// rpc requests use the operation name as root element
BindingOutput bindingOutput = bindingOperation.getBindingOutput();
String ns = bindingOutput == null ? null : WsdlUtils.getSoapBodyNamespace(bindingOutput
.getExtensibilityElements());
if (ns == null) {
ns = WsdlUtils.getTargetNamespace(wsdlContext.getDefinition());
log.warn("missing namespace on soapbind:body for RPC response, using targetNamespace instead (BP violation)");
}
cursor.beginElement(new QName(ns, bindingOperation.getName() + "Response"));
if (xmlGenerator.isSoapEnc()) {
cursor.insertAttributeWithValue(new QName(wsdlContext.getSoapVersion().getEnvelopeNamespace(),
"encodingStyle"), wsdlContext.getSoapVersion().getEncodingNamespace());
}
Part[] inputParts = WsdlUtils.getOutputParts(bindingOperation);
for (int i = 0; i < inputParts.length; i++) {
Part part = inputParts[i];
if (WsdlUtils.isAttachmentOutputPart(part, bindingOperation)) {
if (iface.getSettings().getBoolean(WsdlSettings.ATTACHMENT_PARTS)) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.beginElement(part.getName());
c.insertAttributeWithValue("href", part.getName() + "Attachment");
c.dispose();
}
} else {
if (wsdlContext.hasSchemaTypes()) {
QName typeName = part.getTypeName();
if (typeName != null) {
SchemaType type = wsdlContext.getInterfaceDefinition().findType(typeName);
if (type != null) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement(part.getName());
c.toPrevToken();
xmlGenerator.createSampleForType(type, c);
c.dispose();
} else {
log.warn("Failed to find type [" + typeName + "]");
}
} else {
SchemaGlobalElement element = wsdlContext.getSchemaTypeLoader().findElement(part.getElementName());
if (element != null) {
XmlCursor c = cursor.newCursor();
c.toLastChild();
c.insertElement(element.getName());
c.toPrevToken();
xmlGenerator.createSampleForType(element.getType(), c);
c.dispose();
} else {
log.warn("Failed to find element [" + part.getElementName() + "]");
}
}
}
}
}
}
public void setWsdlContext(WsdlContext wsdlContext) {
this.wsdlContext = wsdlContext;
}
public void setInterface(WsdlInterface iface) {
this.iface = iface;
}
public String buildSoapMessageFromOutput(BindingOperation bindingOperation, boolean buildOptional)
throws Exception {
return buildSoapMessageFromOutput(bindingOperation, buildOptional, true);
}
public String buildSoapMessageFromOutput(BindingOperation bindingOperation, boolean buildOptional,
boolean alwaysBuildHeaders) throws Exception {
boolean inputSoapEncoded = WsdlUtils.isInputSoapEncoded(bindingOperation);
SampleXmlUtil xmlGenerator = new SampleXmlUtil(inputSoapEncoded);
xmlGenerator.setIgnoreOptional(!buildOptional);
xmlGenerator.setMultiValues(multiValues);
XmlObject object = XmlObject.Factory.newInstance();
XmlCursor cursor = object.newCursor();
cursor.toNextToken();
cursor.beginElement(wsdlContext.getSoapVersion().getEnvelopeQName());
if (inputSoapEncoded) {
cursor.insertNamespace("xsi", Constants.XSI_NS);
cursor.insertNamespace("xsd", Constants.XSD_NS);
}
cursor.toFirstChild();
cursor.beginElement(wsdlContext.getSoapVersion().getBodyQName());
cursor.toFirstChild();
if (WsdlUtils.isRpc(wsdlContext.getDefinition(), bindingOperation)) {
buildRpcResponse(bindingOperation, cursor, xmlGenerator);
} else {
buildDocumentResponse(bindingOperation, cursor, xmlGenerator);
}
if (alwaysBuildHeaders) {
// bindingOutput will be null for one way operations,
// but then we shouldn't be here in the first place???
BindingOutput bindingOutput = bindingOperation.getBindingOutput();
if (bindingOutput != null) {
List<?> extensibilityElements = bindingOutput.getExtensibilityElements();
List<SoapHeader> soapHeaders = WsdlUtils.getSoapHeaders(extensibilityElements);
addHeaders(soapHeaders, cursor, xmlGenerator);
}
}
cursor.dispose();
try {
StringWriter writer = new StringWriter();
XmlUtils.serializePretty(object, writer);
return writer.toString();
} catch (Exception e) {
SoapUI.logError(e);
return object.xmlText();
}
}
public String buildFault(FaultPart faultPart) {
SampleXmlUtil generator = new SampleXmlUtil(false);
generator.setExampleContent(false);
generator.setTypeComment(false);
generator.setMultiValues(multiValues);
String faultResponse = iface.getMessageBuilder().buildEmptyFault();
XmlCursor cursor = null;
try {
// XmlObject xmlObject = XmlObject.Factory.parse( faultResponse );
XmlObject xmlObject = XmlUtils.createXmlObject(faultResponse);
XmlObject[] detail = xmlObject.selectPath("//detail");
if (detail.length > 0) {
cursor = detail[0].newCursor();
cursor.toFirstContentToken();
generator.setTypeComment(true);
generator.setIgnoreOptional(iface.getSettings().getBoolean(
WsdlSettings.XML_GENERATION_ALWAYS_INCLUDE_OPTIONAL_ELEMENTS));
for (Part part : faultPart.getWsdlParts()) {
createElementForPart(part, cursor, generator);
}
}
faultResponse = xmlObject.xmlText(new XmlOptions().setSaveAggressiveNamespaces().setSavePrettyPrint());
} catch (Exception e1) {
SoapUI.logError(e1);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
return faultResponse;
}
}