/* * 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() + "]"); } } }