/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 * * http://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.apache.cxf.tools.validator.internal; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.jws.soap.SOAPBinding; import javax.wsdl.Binding; import javax.wsdl.BindingOperation; import javax.wsdl.Definition; import javax.wsdl.Fault; import javax.wsdl.Message; import javax.wsdl.Operation; import javax.wsdl.Part; import javax.wsdl.PortType; import javax.wsdl.WSDLElement; import javax.wsdl.extensions.mime.MIMEContent; import javax.wsdl.extensions.mime.MIMEMultipartRelated; import javax.wsdl.extensions.mime.MIMEPart; import javax.xml.namespace.QName; import org.apache.cxf.binding.soap.SOAPBindingUtil; import org.apache.cxf.binding.soap.wsdl.extensions.SoapBody; import org.apache.cxf.binding.soap.wsdl.extensions.SoapFault; import org.apache.cxf.binding.soap.wsdl.extensions.SoapHeader; import org.apache.cxf.common.util.CollectionUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.tools.common.ToolException; import org.apache.cxf.wsdl.WSDLHelper; public class WSIBPValidator extends AbstractDefinitionValidator { private List<String> operationMap = new ArrayList<>(); private WSDLHelper wsdlHelper = new WSDLHelper(); public WSIBPValidator(Definition def) { super(def); } public boolean isValid() { boolean valid = true; for (Method m : getClass().getMethods()) { if (m.getName().startsWith("check") && m.getGenericReturnType() == boolean.class && m.getGenericParameterTypes().length == 0) { try { Boolean res = (Boolean) m.invoke(this); if (!res) { valid = false; } } catch (Exception e) { throw new ToolException(e); } } } return valid; } private boolean checkR2716(final BindingOperation bop) { SoapBody inSoapBody = SOAPBindingUtil.getBindingInputSOAPBody(bop); SoapBody outSoapBody = SOAPBindingUtil.getBindingOutputSOAPBody(bop); if (inSoapBody != null && !StringUtils.isEmpty(inSoapBody.getNamespaceURI()) || outSoapBody != null && !StringUtils.isEmpty(outSoapBody.getNamespaceURI())) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2716") + "Operation '" + bop.getName() + "' soapBody MUST NOT have namespace attribute"); return false; } SoapHeader inSoapHeader = SOAPBindingUtil.getBindingInputSOAPHeader(bop); SoapHeader outSoapHeader = SOAPBindingUtil.getBindingOutputSOAPHeader(bop); if (inSoapHeader != null && !StringUtils.isEmpty(inSoapHeader.getNamespaceURI()) || outSoapHeader != null && !StringUtils.isEmpty(outSoapHeader.getNamespaceURI())) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2716") + "Operation '" + bop.getName() + "' soapHeader MUST NOT have namespace attribute"); return false; } List<SoapFault> soapFaults = SOAPBindingUtil.getBindingOperationSoapFaults(bop); for (SoapFault fault : soapFaults) { if (!StringUtils.isEmpty(fault.getNamespaceURI())) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2716") + "Operation '" + bop.getName() + "' soapFault MUST NOT have namespace attribute"); return false; } } return true; } private boolean checkR2717AndR2726(final BindingOperation bop) { if (null == bop) { return true; } SoapBody inSoapBody = SOAPBindingUtil.getBindingInputSOAPBody(bop); SoapBody outSoapBody = SOAPBindingUtil.getBindingOutputSOAPBody(bop); if (inSoapBody != null && StringUtils.isEmpty(inSoapBody.getNamespaceURI()) || outSoapBody != null && StringUtils.isEmpty(outSoapBody.getNamespaceURI())) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2717") + "soapBody in the input/output of the binding operation '" + bop.getName() + "' MUST have namespace attribute"); return false; } SoapHeader inSoapHeader = SOAPBindingUtil.getBindingInputSOAPHeader(bop); SoapHeader outSoapHeader = SOAPBindingUtil.getBindingOutputSOAPHeader(bop); if (inSoapHeader != null && !StringUtils.isEmpty(inSoapHeader.getNamespaceURI()) || outSoapHeader != null && !StringUtils.isEmpty(outSoapHeader.getNamespaceURI())) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2726") + "Operation '" + bop.getName() + "' soapHeader MUST NOT have namespace attribute"); return false; } List<SoapFault> soapFaults = SOAPBindingUtil.getBindingOperationSoapFaults(bop); for (SoapFault fault : soapFaults) { if (!StringUtils.isEmpty(fault.getNamespaceURI())) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2726") + "Operation '" + bop.getName() + "' soapFault MUST NOT have namespace attribute"); return false; } } return true; } private boolean checkR2201Input(final Operation operation, final BindingOperation bop) { List<Part> partsList = wsdlHelper.getInMessageParts(operation); int inmessagePartsCount = partsList.size(); SoapBody soapBody = SOAPBindingUtil.getBindingInputSOAPBody(bop); if (soapBody != null) { List<?> parts = soapBody.getParts(); int boundPartSize = parts == null ? inmessagePartsCount : parts.size(); SoapHeader soapHeader = SOAPBindingUtil.getBindingInputSOAPHeader(bop); boundPartSize = soapHeader != null && soapHeader.getMessage().equals( operation.getInput().getMessage() .getQName()) ? boundPartSize - 1 : boundPartSize; if (parts != null) { Iterator<?> partsIte = parts.iterator(); while (partsIte.hasNext()) { String partName = (String)partsIte.next(); boolean isDefined = false; for (Part part : partsList) { if (partName.equalsIgnoreCase(part.getName())) { isDefined = true; break; } } if (!isDefined) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2201") + "Operation '" + operation.getName() + "' soapBody parts : " + partName + " not found in the message, wrong WSDL"); return false; } } } else { if (partsList.size() > 1) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2210") + "Operation '" + operation.getName() + "' more than one part bound to body"); return false; } } if (boundPartSize > 1) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2201") + "Operation '" + operation.getName() + "' more than one part bound to body"); return false; } } return true; } private boolean checkR2201Output(final Operation operation, final BindingOperation bop) { int outmessagePartsCount = wsdlHelper.getOutMessageParts(operation).size(); SoapBody soapBody = SOAPBindingUtil.getBindingOutputSOAPBody(bop); if (soapBody != null) { List<?> parts = soapBody.getParts(); int boundPartSize = parts == null ? outmessagePartsCount : parts.size(); SoapHeader soapHeader = SOAPBindingUtil.getBindingOutputSOAPHeader(bop); boundPartSize = soapHeader != null && soapHeader.getMessage().equals( operation.getOutput().getMessage() .getQName()) ? boundPartSize - 1 : boundPartSize; if (parts != null) { Iterator<?> partsIte = parts.iterator(); while (partsIte.hasNext()) { String partName = (String)partsIte.next(); boolean isDefined = false; for (Part part : wsdlHelper.getOutMessageParts(operation)) { if (partName.equalsIgnoreCase(part.getName())) { isDefined = true; break; } } if (!isDefined) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2201") + "Operation '" + operation.getName() + "' soapBody parts : " + partName + " not found in the message, wrong WSDL"); return false; } } } else { if (wsdlHelper.getOutMessageParts(operation).size() > 1) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2210") + "Operation '" + operation.getName() + "' more than one part bound to body"); return false; } } if (boundPartSize > 1) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2201") + "Operation '" + operation.getName() + "' more than one part bound to body"); return false; } } return true; } private boolean checkR2209(final Operation operation, final BindingOperation bop) { if ((bop.getBindingInput() == null && operation.getInput() != null) || (bop.getBindingOutput() == null && operation.getOutput() != null)) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2209") + "Unbound PortType elements in Operation '" + operation.getName() + "'"); return false; } return true; } public boolean checkBinding() { for (PortType portType : wsdlHelper.getPortTypes(def)) { Iterator<?> ite = portType.getOperations().iterator(); while (ite.hasNext()) { Operation operation = (Operation)ite.next(); if (isOverloading(operation.getName())) { continue; } BindingOperation bop = wsdlHelper.getBindingOperation(def, operation.getName()); if (bop == null) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2718") + "A wsdl:binding in a DESCRIPTION MUST have the same set of " + "wsdl:operations as the wsdl:portType to which it refers. " + operation.getName() + " not found in wsdl:binding."); return false; } Binding binding = wsdlHelper.getBinding(bop, def); String bindingStyle = binding != null ? SOAPBindingUtil.getBindingStyle(binding) : ""; String style = StringUtils.isEmpty(SOAPBindingUtil.getSOAPOperationStyle(bop)) ? bindingStyle : SOAPBindingUtil.getSOAPOperationStyle(bop); if ("DOCUMENT".equalsIgnoreCase(style) || StringUtils.isEmpty(style)) { boolean passed = checkR2201Input(operation, bop) && checkR2201Output(operation, bop) && checkR2209(operation, bop) && checkR2716(bop); if (!passed) { return false; } } else { if (!checkR2717AndR2726(bop)) { return false; } } } } return true; } private boolean isHeaderPart(final BindingOperation bop, final Part part) { QName elementName = part.getElementName(); if (elementName != null) { String partName = elementName.getLocalPart(); SoapHeader inSoapHeader = SOAPBindingUtil.getBindingInputSOAPHeader(bop); if (inSoapHeader != null) { return partName.equals(inSoapHeader.getPart()); } SoapHeader outSoapHeader = SOAPBindingUtil.getBindingOutputSOAPHeader(bop); if (outSoapHeader != null) { return partName.equals(outSoapHeader.getPart()); } } return false; } public boolean checkR2203And2204() { Collection<Binding> bindings = CastUtils.cast(def.getBindings().values()); for (Binding binding : bindings) { String style = SOAPBindingUtil.getCanonicalBindingStyle(binding); if (binding.getPortType() == null) { return true; } // for (Iterator<?> ite2 = binding.getPortType().getOperations().iterator(); ite2.hasNext();) { Operation operation = (Operation)ite2.next(); BindingOperation bop = wsdlHelper.getBindingOperation(def, operation.getName()); if (operation.getInput() != null && operation.getInput().getMessage() != null) { Message inMess = operation.getInput().getMessage(); Set<String> ignorableParts = getIgnorableParts(bop.getBindingInput()); for (Iterator<?> ite3 = inMess.getParts().values().iterator(); ite3.hasNext();) { Part p = (Part)ite3.next(); if (SOAPBinding.Style.RPC.name().equalsIgnoreCase(style) && p.getTypeName() == null && !isHeaderPart(bop, p) && !isIgnorablePart(p.getName(), ignorableParts)) { addErrorMessage("An rpc-literal binding in a DESCRIPTION MUST refer, " + "in its soapbind:body element(s), only to " + "wsdl:part element(s) that have been defined " + "using the type attribute."); return false; } if (SOAPBinding.Style.DOCUMENT.name().equalsIgnoreCase(style) && p.getElementName() == null && !isIgnorablePart(p.getName(), ignorableParts)) { addErrorMessage("A document-literal binding in a DESCRIPTION MUST refer, " + "in each of its soapbind:body element(s)," + "only to wsdl:part element(s)" + " that have been defined using the element attribute."); return false; } } } if (operation.getOutput() != null && operation.getOutput().getMessage() != null) { Message outMess = operation.getOutput().getMessage(); Set<String> ignorableParts = getIgnorableParts(bop.getBindingOutput()); for (Iterator<?> ite3 = outMess.getParts().values().iterator(); ite3.hasNext();) { Part p = (Part)ite3.next(); if (style.equalsIgnoreCase(SOAPBinding.Style.RPC.name()) && p.getTypeName() == null && !isHeaderPart(bop, p) && !isIgnorablePart(p.getName(), ignorableParts)) { addErrorMessage("An rpc-literal binding in a DESCRIPTION MUST refer, " + "in its soapbind:body element(s), only to " + "wsdl:part element(s) that have been defined " + "using the type attribute."); return false; } if (style.equalsIgnoreCase(SOAPBinding.Style.DOCUMENT.name()) && p.getElementName() == null && !isIgnorablePart(p.getName(), ignorableParts)) { addErrorMessage("A document-literal binding in a DESCRIPTION MUST refer, " + "in each of its soapbind:body element(s)," + "only to wsdl:part element(s)" + " that have been defined using the element attribute."); return false; } } } } } return true; } private static boolean isIgnorablePart(String name, Set<String> ignorableParts) { return ignorableParts != null && ignorableParts.contains(name); } private static Set<String> getIgnorableParts(WSDLElement ext) { Set<String> parts = null; if (ext != null && ext.getExtensibilityElements() != null && ext.getExtensibilityElements().size() > 0 && ext.getExtensibilityElements().get(0) instanceof MIMEMultipartRelated) { MIMEMultipartRelated mpr = (MIMEMultipartRelated)ext.getExtensibilityElements().get(0); List<MIMEPart> mps = CastUtils.cast(mpr.getMIMEParts()); parts = new HashSet<>(mps.size()); for (Iterator<MIMEPart> it = mps.iterator(); it.hasNext();) { MIMEPart mp = it.next(); if (mp.getExtensibilityElements() != null && mp.getExtensibilityElements().size() > 0 && mp.getExtensibilityElements().get(0) instanceof MIMEContent) { parts.add(((MIMEContent)mp.getExtensibilityElements().get(0)).getPart()); } } } return parts; } // TODO: Should also check SoapHeader/SoapHeaderFault public boolean checkR2205() { Collection<Binding> bindings = CastUtils.cast(def.getBindings().values()); for (Binding binding : bindings) { if (!SOAPBindingUtil.isSOAPBinding(binding)) { System.err.println("WSIBP Validator found <" + binding.getQName() + "> is NOT a SOAP binding"); continue; } if (binding.getPortType() == null) { //will error later continue; } for (Iterator<?> ite2 = binding.getPortType().getOperations().iterator(); ite2.hasNext();) { Operation operation = (Operation)ite2.next(); Collection<Fault> faults = CastUtils.cast(operation.getFaults().values()); if (CollectionUtils.isEmpty(faults)) { continue; } for (Fault fault : faults) { Message message = fault.getMessage(); Collection<Part> parts = CastUtils.cast(message.getParts().values()); for (Part part : parts) { if (part.getElementName() == null) { addErrorMessage(getErrorPrefix("WSI-BP-1.0 R2205") + "In Message " + message.getQName() + ", part " + part.getName() + " must specify a 'element' attribute"); return false; } } } } } return true; } public boolean checkR2705() { Collection<Binding> bindings = CastUtils.cast(def.getBindings().values()); for (Binding binding : bindings) { if (SOAPBindingUtil.isMixedStyle(binding)) { addErrorMessage("Mixed style, invalid WSDL"); return false; } } return true; } private boolean isOverloading(String operationName) { if (operationMap.contains(operationName)) { return true; } else { operationMap.add(operationName); } return false; } private static String getErrorPrefix(String ruleBroken) { return ruleBroken + " violation: "; } }