/*
* SoapUI, Copyright (C) 2004-2016 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.wsdl;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.submit.WsdlMessageExchange;
import com.eviware.soapui.model.iface.Attachment;
import com.eviware.soapui.model.testsuite.AssertionError;
import com.eviware.soapui.settings.WsdlSettings;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.xml.XmlUtils;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.SchemaGlobalElement;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlLineNumber;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlValidationError;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.wsdl.Binding;
import javax.wsdl.BindingFault;
import javax.wsdl.BindingOperation;
import javax.wsdl.Part;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.extensions.mime.MIMEContent;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Class for validating SOAP requests/responses against their definition and
* schema, requires that the messages follow basic-profile requirements
*
* @author Ole.Matzura
*/
public class WsdlValidator {
private final WsdlContext wsdlContext;
private final static Logger log = Logger.getLogger(WsdlValidator.class);
public WsdlValidator(WsdlContext wsdlContext) {
this.wsdlContext = wsdlContext;
}
public AssertionError[] assertRequest(WsdlMessageExchange messageExchange, boolean envelopeOnly) {
List<XmlError> errors = new ArrayList<XmlError>();
try {
String requestContent = messageExchange.getRequestContent();
wsdlContext.getSoapVersion().validateSoapEnvelope(requestContent, errors);
if (errors.isEmpty() && !envelopeOnly) {
wsdlContext.getSoapVersion().validateSoapEnvelope(requestContent, errors);
WsdlOperation operation = messageExchange.getOperation();
BindingOperation bindingOperation = operation.getBindingOperation();
if (bindingOperation == null) {
errors.add(XmlError.forMessage("Missing operation [" + operation.getBindingOperationName()
+ "] in wsdl definition"));
} else {
Part[] inputParts = WsdlUtils.getInputParts(bindingOperation);
validateMessage(messageExchange, requestContent, bindingOperation, inputParts, errors, false);
// validateInputAttachments(request, errors, bindingOperation,
// inputParts);
}
}
} catch (Exception e) {
errors.add(XmlError.forMessage(e.getMessage()));
}
return convertErrors(errors);
}
private void validateInputAttachments(WsdlMessageExchange messageExchange, List<XmlError> errors,
BindingOperation bindingOperation, Part[] inputParts) {
for (Part part : inputParts) {
MIMEContent[] contents = WsdlUtils.getInputMultipartContent(part, bindingOperation);
if (contents.length == 0) {
continue;
}
Attachment[] attachments = messageExchange.getRequestAttachmentsForPart(part.getName());
if (attachments.length == 0) {
errors.add(XmlError.forMessage("Missing attachment for part [" + part.getName() + "]"));
} else if (attachments.length == 1) {
Attachment attachment = attachments[0];
String types = "";
for (MIMEContent content : contents) {
String type = content.getType();
if (type.equals(attachment.getContentType()) || type.toUpperCase().startsWith("MULTIPART")) {
types = null;
break;
}
if (types.length() > 0) {
types += ",";
}
types += type;
}
if (types != null) {
String msg = "Missing attachment for part [" + part.getName() + "] with content-type [" + types + "],"
+ " content type is [" + attachment.getContentType() + "]";
if (SoapUI.getSettings().getBoolean(WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE)) {
log.warn(msg);
} else {
errors.add(XmlError.forMessage(msg));
}
}
} else {
String types = "";
for (MIMEContent content : contents) {
String type = content.getType();
if (type.toUpperCase().startsWith("MULTIPART")) {
types = null;
break;
}
if (types.length() > 0) {
types += ",";
}
types += type;
}
if (types == null) {
String msg = "Too many attachments for part [" + part.getName() + "] with content-type [" + types + "]";
if (SoapUI.getSettings().getBoolean(WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE)) {
log.warn(msg);
} else {
errors.add(XmlError.forMessage(msg));
}
}
}
if (attachments.length > 0) {
validateAttachmentsReadability(errors, attachments);
}
}
}
private void validateOutputAttachments(WsdlMessageExchange messageExchange, XmlObject xml, List<XmlError> errors,
BindingOperation bindingOperation, Part[] outputParts) throws Exception {
for (Part part : outputParts) {
MIMEContent[] contents = WsdlUtils.getOutputMultipartContent(part, bindingOperation);
if (contents.length == 0) {
continue;
}
Attachment[] attachments = messageExchange.getResponseAttachmentsForPart(part.getName());
// check for rpc
if (attachments.length == 0 && WsdlUtils.isRpc(wsdlContext.getDefinition(), bindingOperation)) {
XmlObject[] rpcBodyPart = getRpcBodyPart(bindingOperation, xml, true);
if (rpcBodyPart.length == 1) {
XmlObject[] children = rpcBodyPart[0].selectChildren(new QName(part.getName()));
if (children.length == 1) {
String href = ((Element) children[0].getDomNode()).getAttribute("href");
if (href != null) {
if (href.startsWith("cid:")) {
href = href.substring(4);
}
attachments = messageExchange.getResponseAttachmentsForPart(href);
}
}
}
}
if (attachments.length == 0) {
errors.add(XmlError.forMessage("Missing attachment for part [" + part.getName() + "]"));
} else if (attachments.length == 1) {
Attachment attachment = attachments[0];
String types = "";
for (MIMEContent content : contents) {
String type = content.getType();
if (type.equals(attachment.getContentType()) || type.toUpperCase().startsWith("MULTIPART")) {
types = null;
break;
}
if (types.length() > 0) {
types += ",";
}
types += type;
}
if (types != null) {
String msg = "Missing attachment for part [" + part.getName() + "] with content-type [" + types
+ "], content type is [" + attachment.getContentType() + "]";
if (SoapUI.getSettings().getBoolean(WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE)) {
log.warn(msg);
} else {
errors.add(XmlError.forMessage(msg));
}
}
} else {
String types = "";
for (MIMEContent content : contents) {
String type = content.getType();
if (type.toUpperCase().startsWith("MULTIPART")) {
types = null;
break;
}
if (types.length() > 0) {
types += ",";
}
types += type;
}
if (types != null) {
String msg = "Too many attachments for part [" + part.getName() + "] with content-type [" + types + "]";
if (SoapUI.getSettings().getBoolean(WsdlSettings.ALLOW_INCORRECT_CONTENTTYPE)) {
log.warn(msg);
} else {
errors.add(XmlError.forMessage(msg));
}
}
}
if (attachments.length > 0) {
validateAttachmentsReadability(errors, attachments);
}
}
}
private void validateAttachmentsReadability(List<XmlError> errors, Attachment[] attachments) {
for (Attachment attachment : attachments) {
try {
attachment.getInputStream();
} catch (Exception e) {
errors.add(XmlError.forMessage(e.toString()));
}
}
}
public XmlObject[] getMessageParts(String messageContent, String operationName, boolean isResponse)
throws Exception {
BindingOperation bindingOperation = findBindingOperation(operationName);
if (bindingOperation == null) {
throw new Exception("Missing operation [" + operationName + "] in wsdl definition");
}
if (!wsdlContext.hasSchemaTypes()) {
throw new Exception("Missing schema types for message");
}
// XmlObject msgXml = XmlObject.Factory.parse( messageContent );
XmlObject msgXml = XmlUtils.createXmlObject(messageContent);
Part[] parts = isResponse ? WsdlUtils.getOutputParts(bindingOperation) : WsdlUtils
.getInputParts(bindingOperation);
if (parts == null || parts.length == 0) {
throw new Exception("Missing parts for operation [" + operationName + "]");
}
List<XmlObject> result = new ArrayList<XmlObject>();
if (WsdlUtils.isRpc(wsdlContext.getDefinition(), bindingOperation)) {
// get root element
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
+ WsdlUtils.getTargetNamespace(wsdlContext.getDefinition()) + "';" + "$this/env:Envelope/env:Body/ns:"
+ bindingOperation.getName() + (isResponse ? "Response" : ""));
if (paths.length != 1) {
throw new Exception("Missing message wrapper element ["
+ WsdlUtils.getTargetNamespace(wsdlContext.getDefinition()) + "@" + bindingOperation.getName()
+ (isResponse ? "Response]" : "]"));
} else {
XmlObject wrapper = paths[0];
for (int i = 0; i < parts.length; i++) {
Part part = parts[i];
if ((isResponse && WsdlUtils.isAttachmentOutputPart(part, bindingOperation))
|| (!isResponse && WsdlUtils.isAttachmentInputPart(part, bindingOperation))) {
continue;
}
QName partName = part.getElementName();
if (partName == null) {
partName = new QName(part.getName());
}
XmlObject[] children = wrapper.selectChildren(partName);
if (children.length != 1) {
log.error("Missing message part [" + part.getName() + "]");
} else {
QName typeName = part.getTypeName();
if (typeName == null) {
typeName = partName;
SchemaGlobalElement type = wsdlContext.getSchemaTypeLoader().findElement(typeName);
if (type != null) {
result.add(children[0].copy().changeType(type.getType()));
} else {
log.error("Missing element [" + typeName + "] in associated schema for part ["
+ part.getName() + "]");
}
} else {
SchemaType type = wsdlContext.getSchemaTypeLoader().findType(typeName);
if (type != null) {
result.add(children[0].copy().changeType(type));
} else {
log.error("Missing type [" + typeName + "] in associated schema for part [" + part.getName()
+ "]");
}
}
}
}
}
} else {
Part part = parts[0];
QName elementName = part.getElementName();
if (elementName != null) {
// just check for correct message element, other elements are
// avoided (should create an error)
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
+ elementName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:"
+ elementName.getLocalPart());
if (paths.length == 1) {
SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement(elementName);
if (elm != null) {
result.add(paths[0].copy().changeType(elm.getType()));
} else {
throw new Exception("Missing part type in associated schema");
}
} else {
throw new Exception("Missing message part with name [" + elementName + "]");
}
}
}
return result.toArray(new XmlObject[result.size()]);
}
@SuppressWarnings("unchecked")
public void validateXml(String request, List<XmlError> errors) {
try {
XmlOptions xmlOptions = new XmlOptions();
xmlOptions.setLoadLineNumbers();
xmlOptions.setErrorListener(errors);
xmlOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT);
// XmlObject.Factory.parse( request, xmlOptions );
XmlUtils.createXmlObject(request, xmlOptions);
} catch (XmlException e) {
if (e.getErrors() != null) {
errors.addAll(e.getErrors());
}
errors.add(XmlError.forMessage(e.getMessage()));
} catch (Exception e) {
errors.add(XmlError.forMessage(e.getMessage()));
}
}
private AssertionError[] convertErrors(List<XmlError> errors) {
if (errors.size() > 0) {
List<AssertionError> response = new ArrayList<AssertionError>();
for (Iterator<XmlError> i = errors.iterator(); i.hasNext(); ) {
XmlError error = i.next();
if (error instanceof XmlValidationError) {
XmlValidationError e = ((XmlValidationError) error);
QName offendingQName = e.getOffendingQName();
if (offendingQName != null) {
if (offendingQName.equals(new QName(wsdlContext.getSoapVersion().getEnvelopeNamespace(),
"encodingStyle"))) {
log.debug("ignoring encodingStyle validation..");
continue;
} else if (offendingQName.equals(new QName(wsdlContext.getSoapVersion().getEnvelopeNamespace(),
"mustUnderstand"))) {
log.debug("ignoring mustUnderstand validation..");
continue;
}
}
}
AssertionError assertionError = new AssertionError(error);
if (!response.contains(assertionError)) {
response.add(assertionError);
}
}
return response.toArray(new AssertionError[response.size()]);
}
return new AssertionError[0];
}
@SuppressWarnings("unchecked")
public void validateMessage(WsdlMessageExchange messageExchange, String message, BindingOperation bindingOperation,
Part[] parts, List<XmlError> errors, boolean isResponse) {
try {
if (!wsdlContext.hasSchemaTypes()) {
errors.add(XmlError.forMessage("Missing schema types for message"));
} else {
if (!WsdlUtils.isOutputSoapEncoded(bindingOperation)) {
XmlOptions xmlOptions = new XmlOptions();
xmlOptions.setLoadLineNumbers();
xmlOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT);
// XmlObject xml = XmlObject.Factory.parse( message, xmlOptions
// );
XmlObject xml = XmlUtils.createXmlObject(message, xmlOptions);
XmlObject[] paths = xml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';"
+ "$this/env:Envelope/env:Body/env:Fault");
if (paths.length > 0) {
validateSoapFault(bindingOperation, paths[0], errors);
} else if (WsdlUtils.isRpc(wsdlContext.getDefinition(), bindingOperation)) {
validateRpcLiteral(bindingOperation, parts, xml, errors, isResponse);
} else {
validateDocLiteral(bindingOperation, parts, xml, errors, isResponse);
}
if (isResponse) {
validateOutputAttachments(messageExchange, xml, errors, bindingOperation, parts);
} else {
validateInputAttachments(messageExchange, errors, bindingOperation, parts);
}
} else {
errors.add(XmlError.forMessage("Validation of SOAP-Encoded messages not supported"));
}
}
} catch (XmlException e) {
if (e.getErrors() != null) {
errors.addAll(e.getErrors());
}
errors.add(XmlError.forMessage(e.getMessage()));
} catch (Exception e) {
errors.add(XmlError.forMessage(e.getMessage()));
}
}
private BindingOperation findBindingOperation(String operationName) throws Exception {
Map<?, ?> services = wsdlContext.getDefinition().getAllServices();
Iterator<?> i = services.keySet().iterator();
while (i.hasNext()) {
Service service = (Service) wsdlContext.getDefinition().getService((QName) i.next());
Map<?, ?> ports = service.getPorts();
Iterator<?> iterator = ports.keySet().iterator();
while (iterator.hasNext()) {
Port port = (Port) service.getPort((String) iterator.next());
Binding binding = port.getBinding();
if (binding.getQName().equals(wsdlContext.getInterface().getBindingName())) {
BindingOperation bindingOperation = binding.getBindingOperation(operationName, null, null);
if (bindingOperation != null) {
return bindingOperation;
}
}
}
}
Map<?, ?> bindings = wsdlContext.getDefinition().getAllBindings();
i = bindings.keySet().iterator();
while (i.hasNext()) {
Binding binding = (Binding) bindings.get(i.next());
if (binding.getQName().equals(wsdlContext.getInterface().getBindingName())) {
BindingOperation bindingOperation = binding.getBindingOperation(operationName, null, null);
if (bindingOperation != null) {
return bindingOperation;
}
}
}
return null;
}
public AssertionError[] assertResponse(WsdlMessageExchange messageExchange, boolean envelopeOnly) {
List<XmlError> errors = new ArrayList<XmlError>();
try {
String response = messageExchange.getResponseContent();
if (StringUtils.isNullOrEmpty(response)) {
if (!messageExchange.getOperation().isOneWay()) {
errors.add(XmlError.forMessage("Response is missing or empty"));
}
} else {
wsdlContext.getSoapVersion().validateSoapEnvelope(response, errors);
if (errors.isEmpty() && !envelopeOnly) {
WsdlOperation operation = messageExchange.getOperation();
BindingOperation bindingOperation = operation.getBindingOperation();
if (bindingOperation == null) {
errors.add(XmlError.forMessage("Missing operation [" + operation.getBindingOperationName()
+ "] in wsdl definition"));
} else {
Part[] outputParts = WsdlUtils.getOutputParts(bindingOperation);
validateMessage(messageExchange, response, bindingOperation, outputParts, errors, true);
}
}
}
} catch (Exception e) {
e.printStackTrace();
errors.add(XmlError.forMessage(e.getMessage()));
}
return convertErrors(errors);
}
private void validateDocLiteral(BindingOperation bindingOperation, Part[] parts, XmlObject msgXml,
List<XmlError> errors, boolean isResponse) throws Exception {
Part part = null;
// start by finding body part
for (int c = 0; c < parts.length; c++) {
// content part?
if ((isResponse && !WsdlUtils.isAttachmentOutputPart(parts[c], bindingOperation))
|| (!isResponse && !WsdlUtils.isAttachmentInputPart(parts[c], bindingOperation))) {
// already found?
if (part != null) {
errors.add(XmlError.forMessage("DocLiteral message must contain 1 body part definition"));
return;
}
part = parts[c];
}
}
QName elementName = part.getElementName();
if (elementName != null) {
// just check for correct message element, other elements are avoided
// (should create an error)
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
+ elementName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:" + elementName.getLocalPart());
if (paths.length == 1) {
SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement(elementName);
if (elm != null) {
validateMessageBody(errors, elm.getType(), paths[0]);
// ensure no other elements in body
NodeList children = XmlUtils.getChildElements((Element) paths[0].getDomNode().getParentNode());
for (int c = 0; c < children.getLength(); c++) {
QName childName = XmlUtils.getQName(children.item(c));
if (!elementName.equals(childName)) {
XmlCursor cur = paths[0].newCursor();
cur.toParent();
cur.toChild(childName);
errors.add(XmlError.forCursor("Invalid element [" + childName + "] in SOAP Body", cur));
cur.dispose();
}
}
} else {
errors.add(XmlError.forMessage("Missing part type [" + elementName + "] in associated schema"));
}
} else {
errors.add(XmlError.forMessage("Missing message part with name [" + elementName + "]"));
}
} else if (part.getTypeName() != null) {
QName typeName = part.getTypeName();
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
+ typeName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:" + part.getName());
if (paths.length == 1) {
SchemaType type = wsdlContext.getSchemaTypeLoader().findType(typeName);
if (type != null) {
validateMessageBody(errors, type, paths[0]);
// XmlObject obj = paths[0].copy().changeType( type );
// obj.validate( new XmlOptions().setErrorListener( errors ));
} else {
errors.add(XmlError.forMessage("Missing part type in associated schema"));
}
} else {
errors.add(XmlError.forMessage("Missing message part with name:type [" + part.getName() + ":" + typeName
+ "]"));
}
}
}
private void validateMessageBody(List<XmlError> errors, SchemaType type, XmlObject msg) throws XmlException {
// need to create new body element of correct type from xml text
// since we want to retain line-numbers
XmlOptions xmlOptions = new XmlOptions();
xmlOptions.setLoadLineNumbers();
xmlOptions.setLoadLineNumbers(XmlOptions.LOAD_LINE_NUMBERS_END_ELEMENT);
XmlCursor cur = msg.newCursor();
Map<String, String> map = new HashMap<String, String>();
while (cur.hasNextToken()) {
if (cur.toNextToken().isNamespace()) {
map.put(cur.getName().getLocalPart(), cur.getTextValue());
}
}
xmlOptions.setUseDefaultNamespace();
xmlOptions.setSaveOuter();
// problem: prefixes might get redefined/changed when saving which can
// cause xsi:type refs to
// reference wrong/non-existing namespace.. solution would probably be to
// manually walk through document and
// update xsi:type refs with new prefix. The setUseDefaultNamespace()
// above helps here but is not a definitive fix
String xmlText = msg.copy().changeType(type).xmlText(xmlOptions);
xmlOptions.setLoadAdditionalNamespaces(map);
XmlObject obj = type.getTypeSystem().parse(xmlText, type, xmlOptions);
obj = obj.changeType(type);
// create internal error list
ArrayList<Object> list = new ArrayList<Object>();
xmlOptions = new XmlOptions();
xmlOptions.setErrorListener(list);
xmlOptions.setValidateTreatLaxAsSkip();
try {
obj.validate(xmlOptions);
} catch (Exception e) {
SoapUI.logError(e);
list.add("Internal Error - see error log for details - [" + e + "]");
}
// transfer errors for "real" line numbers
for (int c = 0; c < list.size(); c++) {
XmlError error = (XmlError) list.get(c);
if (error instanceof XmlValidationError) {
XmlValidationError validationError = ((XmlValidationError) error);
if (wsdlContext.getSoapVersion().shouldIgnore(validationError)) {
continue;
}
// ignore cid: related errors
if (validationError.getErrorCode().equals("base64Binary")
|| validationError.getErrorCode().equals("hexBinary")) {
XmlCursor cursor = validationError.getCursorLocation();
if (cursor.toParent()) {
String text = cursor.getTextValue();
// special handling for soapui/MTOM -> add option for
// disabling?
if (text.startsWith("cid:") || text.startsWith("file:")) {
// ignore
continue;
}
}
}
}
int line = error.getLine() == -1 ? 0 : error.getLine() - 1;
errors.add(XmlError.forLocation(error.getMessage(), error.getSourceName(), getLine(msg) + line,
error.getColumn(), error.getOffset()));
}
}
private int getLine(XmlObject object) {
List<?> list = new ArrayList<Object>();
object.newCursor().getAllBookmarkRefs(list);
for (int c = 0; c < list.size(); c++) {
if (list.get(c) instanceof XmlLineNumber) {
return ((XmlLineNumber) list.get(c)).getLine();
}
}
return -1;
}
private void validateRpcLiteral(BindingOperation bindingOperation, Part[] parts, XmlObject msgXml,
List<XmlError> errors, boolean isResponse) throws Exception {
if (parts.length == 0) {
return;
}
XmlObject[] bodyParts = getRpcBodyPart(bindingOperation, msgXml, isResponse);
if (bodyParts.length != 1) {
errors.add(XmlError.forMessage("Missing message wrapper element ["
+ WsdlUtils.getTargetNamespace(wsdlContext.getDefinition()) + "@" + bindingOperation.getName()
+ (isResponse ? "Response" : "")));
} else {
XmlObject wrapper = bodyParts[0];
for (int i = 0; i < parts.length; i++) {
Part part = parts[i];
// skip attachment parts
if (isResponse) {
if (WsdlUtils.isAttachmentOutputPart(part, bindingOperation)) {
continue;
}
} else {
if (WsdlUtils.isAttachmentInputPart(part, bindingOperation)) {
continue;
}
}
// find part in message
XmlObject[] children = wrapper.selectChildren(new QName(part.getName()));
// not found?
if (children.length != 1) {
// try element name (loophole in basic-profile spec?)
QName elementName = part.getElementName();
if (elementName != null) {
bodyParts = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='"
+ wsdlContext.getDefinition().getTargetNamespace() + "';" + "declare namespace ns2='"
+ elementName.getNamespaceURI() + "';" + "$this/env:Envelope/env:Body/ns:"
+ bindingOperation.getName() + (isResponse ? "Response" : "") + "/ns2:"
+ elementName.getLocalPart());
if (bodyParts.length == 1) {
SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement(elementName);
if (elm != null) {
validateMessageBody(errors, elm.getType(), bodyParts[0]);
} else {
errors.add(XmlError.forMessage("Missing part type in associated schema for [" + elementName
+ "]"));
}
} else {
errors.add(XmlError.forMessage("Missing message part with name [" + elementName + "]"));
}
} else {
errors.add(XmlError.forMessage("Missing message part [" + part.getName() + "]"));
}
} else {
QName typeName = part.getTypeName();
SchemaType type = wsdlContext.getSchemaTypeLoader().findType(typeName);
if (type != null) {
validateMessageBody(errors, type, children[0]);
} else {
errors.add(XmlError.forMessage("Missing type in associated schema for part [" + part.getName()
+ "]"));
}
}
}
}
}
private XmlObject[] getRpcBodyPart(BindingOperation bindingOperation, XmlObject msgXml, boolean isResponse)
throws Exception {
// rpc requests should use the operation name as root element and soapbind
// namespaceuri attribute as ns
String ns = WsdlUtils.getSoapBodyNamespace(isResponse ? bindingOperation.getBindingOutput()
.getExtensibilityElements() : bindingOperation.getBindingInput().getExtensibilityElements());
if (ns == null || ns.trim().length() == 0) {
ns = WsdlUtils.getTargetNamespace(wsdlContext.getDefinition());
}
// get root element
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "';" + "declare namespace ns='" + ns + "';"
+ "$this/env:Envelope/env:Body/ns:" + bindingOperation.getName() + (isResponse ? "Response" : ""));
return paths;
}
@SuppressWarnings("unchecked")
private void validateSoapFault(BindingOperation bindingOperation, XmlObject msgXml, List<XmlError> errors)
throws Exception {
Map faults = bindingOperation.getBindingFaults();
Iterator<BindingFault> i = faults.values().iterator();
// create internal error list
List<?> list = new ArrayList<Object>();
XmlOptions xmlOptions = new XmlOptions();
xmlOptions.setErrorListener(list);
xmlOptions.setValidateTreatLaxAsSkip();
msgXml.validate(xmlOptions);
for (Object o : list) {
if (o instanceof XmlError) {
errors.add((XmlError) o);
} else {
errors.add(XmlError.forMessage(o.toString()));
}
}
while (i.hasNext()) {
BindingFault bindingFault = i.next();
String faultName = bindingFault.getName();
Part[] faultParts = WsdlUtils.getFaultParts(bindingOperation, faultName);
if (faultParts.length == 0) {
log.warn("Missing fault parts in wsdl for fault [" + faultName + "] in bindingOperation ["
+ bindingOperation.getName() + "]");
continue;
}
if (faultParts.length != 1) {
log.info("Too many fault parts in wsdl for fault [" + faultName + "] in bindingOperation ["
+ bindingOperation.getName() + "]");
continue;
}
Part part = faultParts[0];
QName elementName = part.getElementName();
if (elementName != null) {
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='"
+ wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" + "declare namespace ns='"
+ elementName.getNamespaceURI() + "';" + "//env:Fault/flt:detail/ns:" + elementName.getLocalPart());
if (paths.length == 1) {
SchemaGlobalElement elm = wsdlContext.getSchemaTypeLoader().findElement(elementName);
if (elm != null) {
validateMessageBody(errors, elm.getType(), paths[0]);
} else {
errors.add(XmlError.forMessage("Missing fault part element [" + elementName + "] for fault ["
+ part.getName() + "] in associated schema"));
}
return;
}
}
// this is not allowed by Basic Profile.. remove?
else if (part.getTypeName() != null) {
QName typeName = part.getTypeName();
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='"
+ wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';" + "declare namespace ns='"
+ typeName.getNamespaceURI() + "';" + "//env:Fault/flt:detail/ns:" + part.getName());
if (paths.length == 1) {
SchemaType type = wsdlContext.getSchemaTypeLoader().findType(typeName);
if (type != null) {
validateMessageBody(errors, type, paths[0]);
} else {
errors.add(XmlError.forMessage("Missing fault part type [" + typeName + "] for fault ["
+ part.getName() + "] in associated schema"));
}
return;
}
}
}
// if we get here, no matching fault was found.. this is not an error but
// should be warned..
XmlObject[] paths = msgXml.selectPath("declare namespace env='"
+ wsdlContext.getSoapVersion().getEnvelopeNamespace() + "'; declare namespace flt='"
+ wsdlContext.getSoapVersion().getFaultDetailNamespace() + "';//env:Fault/flt:detail");
if (paths.length == 0) {
log.warn("Missing matching Fault in wsdl for bindingOperation [" + bindingOperation.getName() + "]");
} else {
String xmlText = paths[0].xmlText(new XmlOptions().setSaveOuter());
log.warn("Missing matching Fault in wsdl for Fault Detail element ["
+ XmlUtils.removeUnneccessaryNamespaces(xmlText) + "] in bindingOperation ["
+ bindingOperation.getName() + "]");
}
}
}