/** * 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.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.wsdl.Binding; import javax.wsdl.BindingOperation; import javax.wsdl.Definition; import javax.wsdl.Fault; import javax.wsdl.Import; import javax.wsdl.Operation; import javax.wsdl.Part; import javax.wsdl.Port; import javax.wsdl.PortType; import javax.wsdl.Service; import javax.xml.namespace.QName; import javax.xml.stream.Location; import javax.xml.xpath.XPathConstants; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.cxf.common.i18n.Message; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.common.xmlschema.SchemaCollection; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.helpers.XPathUtils; import org.apache.cxf.service.model.SchemaInfo; import org.apache.cxf.service.model.ServiceSchemaInfo; import org.apache.cxf.tools.common.ToolException; import org.apache.cxf.tools.validator.internal.model.FailureLocation; import org.apache.cxf.tools.validator.internal.model.XBinding; import org.apache.cxf.tools.validator.internal.model.XDef; import org.apache.cxf.tools.validator.internal.model.XFault; import org.apache.cxf.tools.validator.internal.model.XInput; import org.apache.cxf.tools.validator.internal.model.XMessage; import org.apache.cxf.tools.validator.internal.model.XNode; import org.apache.cxf.tools.validator.internal.model.XOperation; import org.apache.cxf.tools.validator.internal.model.XOutput; import org.apache.cxf.tools.validator.internal.model.XPort; import org.apache.cxf.tools.validator.internal.model.XPortType; import org.apache.cxf.tools.validator.internal.model.XService; import org.apache.cxf.wsdl.WSDLConstants; import org.apache.cxf.wsdl.WSDLManager; import org.apache.cxf.wsdl11.SchemaUtil; import org.apache.ws.commons.schema.XmlSchemaElement; import org.apache.ws.commons.schema.XmlSchemaType; public class WSDLRefValidator extends AbstractDefinitionValidator { protected static final Logger LOG = LogUtils.getL7dLogger(WSDLRefValidator.class); protected List<XNode> vNodes = new ArrayList<>(); private Set<QName> portTypeRefNames = new HashSet<>(); private Set<QName> messageRefNames = new HashSet<>(); private Map<QName, Service> services = new HashMap<>(); private ValidationResult vResults = new ValidationResult(); private Definition definition; private Document baseDoc; private List<Definition> importedDefinitions; private SchemaCollection schemaCollection = new SchemaCollection(); private boolean suppressWarnings; public WSDLRefValidator(Definition wsdl, Document doc) { this(wsdl, doc, BusFactory.getThreadDefaultBus()); } public WSDLRefValidator(Definition wsdl, Document doc, Bus bus) { this.definition = wsdl; baseDoc = doc; importedDefinitions = new ArrayList<>(); parseImports(wsdl); processSchemas(bus); } private void getSchemas(Bus bus) { Map<String, Element> schemaList = new HashMap<>(); SchemaUtil schemaUtil = new SchemaUtil(bus, schemaList); List<SchemaInfo> si = new ArrayList<>(); schemaUtil.getSchemas(definition, schemaCollection, si); ServiceSchemaInfo ssi = new ServiceSchemaInfo(); ssi.setSchemaCollection(schemaCollection); ssi.setSchemaInfoList(si); ssi.setSchemaElementList(schemaList); bus.getExtension(WSDLManager.class).putSchemasForDefinition(definition, ssi); } private void processSchemas(Bus bus) { try { ServiceSchemaInfo info = bus.getExtension(WSDLManager.class) .getSchemasForDefinition(definition); if (info == null) { getSchemas(bus); } else { schemaCollection = info.getSchemaCollection(); } checkTargetNamespace(this.definition.getTargetNamespace()); } catch (Exception ex) { throw new ToolException(ex); } } private Collection<Import> getImports(final Definition wsdlDef) { Collection<Import> importList = new ArrayList<>(); Map<?, ?> imports = wsdlDef.getImports(); for (Map.Entry<?, ?> entry : imports.entrySet()) { List<Import> lst = CastUtils.cast((List<?>)entry.getValue()); importList.addAll(lst); } return importList; } private void parseImports(Definition def) { for (Import impt : getImports(def)) { if (!importedDefinitions.contains(impt.getDefinition())) { importedDefinitions.add(impt.getDefinition()); parseImports(impt.getDefinition()); } } } private void checkTargetNamespace(String path) { // no check as any namespace URI is already a valid target namespace string. } public void setSuppressWarnings(boolean s) { this.suppressWarnings = s; } public ValidationResult getValidationResults() { return this.vResults; } private Document getWSDLDocument(final String wsdl) throws URISyntaxException { return new Stax2DOM().getDocument(wsdl); } private Document getWSDLDocument() throws Exception { if (baseDoc != null) { return baseDoc; } return getWSDLDocument(this.definition.getDocumentBaseURI()); } private List<Document> getWSDLDocuments() { List<Document> docs = new ArrayList<>(); try { docs.add(getWSDLDocument()); if (null != importedDefinitions) { for (Definition d : importedDefinitions) { docs.add(getWSDLDocument(d.getDocumentBaseURI())); } } } catch (Exception e) { e.printStackTrace(); // ignore } return docs; } private boolean isExist(List<Document> docs, XNode vNode) { for (Document doc : docs) { if (vNode.matches(doc)) { return true; } } return false; } private FailureLocation getFailureLocation(List<Document> docs, XNode fNode) { if (fNode == null) { return null; } XPathUtils xpather = new XPathUtils(fNode.getNSMap()); for (Document doc : docs) { Node node = (Node) xpather.getValue(fNode.toString(), doc, XPathConstants.NODE); if (null != node) { try { return new FailureLocation((Location)node.getUserData("location"), doc.getDocumentURI()); } catch (Exception ex) { //ignore, probably not DOM level 3 } } } return null; } public boolean isValid() { try { loadServices(); collectValidationPoints(); List<Document> wsdlDocs = getWSDLDocuments(); for (XNode vNode : vNodes) { if (!isExist(wsdlDocs, vNode)) { //System.out.println("Fail: " + vNode.getXPath()); FailureLocation loc = getFailureLocation(wsdlDocs, vNode.getFailurePoint()); vResults.addError(new Message("FAILED_AT_POINT", LOG, loc.getLocation().getLineNumber(), loc.getLocation().getColumnNumber(), loc.getDocumentURI(), vNode.getPlainText())); } } } catch (Exception e) { this.vResults.addError(e.getMessage()); return false; } return vResults.isSuccessful(); } private void addServices(final Definition wsdlDef) { Iterator<QName> sNames = CastUtils.cast(wsdlDef.getServices().keySet().iterator()); while (sNames.hasNext()) { QName sName = sNames.next(); services.put(sName, definition.getService(sName)); } } private void loadServices() { addServices(this.definition); if (importedDefinitions != null) { for (Definition d : importedDefinitions) { addServices(d); } } } private Map<QName, XNode> getBindings(Service service) { Map<QName, XNode> bindings = new HashMap<>(); if (service.getPorts().values().size() == 0) { throw new ToolException("Service " + service.getQName() + " does not contain any usable ports"); } Collection<Port> ports = CastUtils.cast(service.getPorts().values()); for (Port port : ports) { Binding binding = port.getBinding(); bindings.put(binding.getQName(), getXNode(service, port)); if (WSDLConstants.NS_WSDL11.equals(binding.getQName().getNamespaceURI())) { throw new ToolException("Binding " + binding.getQName().getLocalPart() + " namespace set improperly."); } } return bindings; } private Map<QName, Operation> getOperations(PortType portType) { Map<QName, Operation> operations = new HashMap<>(); Collection<Operation> pops = CastUtils.cast(portType.getOperations()); for (Operation op : pops) { operations.put(new QName(portType.getQName().getNamespaceURI(), op.getName()), op); } return operations; } private XNode getXNode(Service service, Port port) { XNode vService = getXNode(service); XPort pNode = new XPort(); pNode.setName(port.getName()); pNode.setParentNode(vService); return pNode; } private XNode getXNode(Service service) { XDef xdef = new XDef(); xdef.setTargetNamespace(service.getQName().getNamespaceURI()); XService sNode = new XService(); sNode.setName(service.getQName().getLocalPart()); sNode.setParentNode(xdef); return sNode; } private XNode getXNode(Binding binding) { XDef xdef = new XDef(); xdef.setTargetNamespace(binding.getQName().getNamespaceURI()); XBinding bNode = new XBinding(); bNode.setName(binding.getQName().getLocalPart()); bNode.setParentNode(xdef); return bNode; } private XNode getXNode(PortType portType) { XDef xdef = new XDef(); xdef.setTargetNamespace(portType.getQName().getNamespaceURI()); XPortType pNode = new XPortType(); pNode.setName(portType.getQName().getLocalPart()); pNode.setParentNode(xdef); return pNode; } private XNode getOperationXNode(XNode pNode, String opName) { XOperation node = new XOperation(); node.setName(opName); node.setParentNode(pNode); return node; } private XNode getInputXNode(XNode opVNode, String name) { XInput oNode = new XInput(); oNode.setName(name); oNode.setParentNode(opVNode); if (name != null && name.equals(opVNode.getAttributeValue() + "Request")) { oNode.setDefaultAttributeValue(true); } return oNode; } private XNode getOutputXNode(XNode opVNode, String name) { XOutput oNode = new XOutput(); oNode.setName(name); oNode.setParentNode(opVNode); if (name != null && name.equals(opVNode.getAttributeValue() + "Response")) { oNode.setDefaultAttributeValue(true); } return oNode; } private XNode getFaultXNode(XNode opVNode, String name) { XFault oNode = new XFault(); oNode.setName(name); oNode.setParentNode(opVNode); return oNode; } private XNode getXNode(javax.wsdl.Message msg) { XDef xdef = new XDef(); xdef.setTargetNamespace(msg.getQName().getNamespaceURI()); XMessage mNode = new XMessage(); mNode.setName(msg.getQName().getLocalPart()); mNode.setParentNode(xdef); return mNode; } private void addWarning(String warningMsg) { if (suppressWarnings) { return; } vResults.addWarning(warningMsg); } private void collectValidationPoints() throws Exception { if (services.size() == 0) { LOG.log(Level.WARNING, "WSDL document " + this.definition.getDocumentBaseURI() + " does not define any services"); //addWarning("WSDL document does not define any services"); Collection<QName> ports = CastUtils.cast(this.definition.getAllPortTypes().keySet()); portTypeRefNames.addAll(ports); } else { collectValidationPointsForBindings(); } collectValidationPointsForPortTypes(); collectValidationPointsForMessages(); } private void collectValidationPointsForBindings() throws Exception { Map<QName, XNode> vBindingNodes = new HashMap<>(); for (Service service : services.values()) { vBindingNodes.putAll(getBindings(service)); } for (Map.Entry<QName, XNode> entry : vBindingNodes.entrySet()) { QName bName = entry.getKey(); Binding binding = this.definition.getBinding(bName); if (binding == null) { LOG.log(Level.SEVERE, bName.toString() + " is not correct, please check that the correct namespace is being used"); throw new Exception(bName.toString() + " is not correct, please check that the correct namespace is being used"); } XNode vBindingNode = getXNode(binding); vBindingNode.setFailurePoint(entry.getValue()); vNodes.add(vBindingNode); if (binding.getPortType() == null) { continue; } portTypeRefNames.add(binding.getPortType().getQName()); XNode vPortTypeNode = getXNode(binding.getPortType()); vPortTypeNode.setFailurePoint(vBindingNode); vNodes.add(vPortTypeNode); Collection<BindingOperation> bops = CastUtils.cast(binding.getBindingOperations()); for (BindingOperation bop : bops) { XNode vOpNode = getOperationXNode(vPortTypeNode, bop.getName()); XNode vBopNode = getOperationXNode(vBindingNode, bop.getName()); vOpNode.setFailurePoint(vBopNode); vNodes.add(vOpNode); if (bop.getBindingInput() != null) { String inName = bop.getBindingInput().getName(); if (!StringUtils.isEmpty(inName)) { XNode vInputNode = getInputXNode(vOpNode, inName); vInputNode.setFailurePoint(getInputXNode(vBopNode, inName)); vNodes.add(vInputNode); } } if (bop.getBindingOutput() != null) { String outName = bop.getBindingOutput().getName(); if (!StringUtils.isEmpty(outName)) { XNode vOutputNode = getOutputXNode(vOpNode, outName); vOutputNode.setFailurePoint(getOutputXNode(vBopNode, outName)); vNodes.add(vOutputNode); } } for (Iterator<?> iter1 = bop.getBindingFaults().keySet().iterator(); iter1.hasNext();) { String faultName = (String) iter1.next(); XNode vFaultNode = getFaultXNode(vOpNode, faultName); vFaultNode.setFailurePoint(getFaultXNode(vBopNode, faultName)); vNodes.add(vFaultNode); } } } } private javax.wsdl.Message getMessage(QName msgName) { javax.wsdl.Message message = this.definition.getMessage(msgName); if (message == null) { for (Definition d : importedDefinitions) { message = d.getMessage(msgName); if (message != null) { break; } } } return message; } private void collectValidationPointsForMessages() { for (QName msgName : messageRefNames) { javax.wsdl.Message message = getMessage(msgName); for (Iterator<?> iter = message.getParts().values().iterator(); iter.hasNext();) { Part part = (Part) iter.next(); QName elementName = part.getElementName(); QName typeName = part.getTypeName(); if (elementName == null && typeName == null) { vResults.addError(new Message("PART_NO_TYPES", LOG)); continue; } if (elementName != null && typeName != null) { vResults.addError(new Message("PART_NOT_UNIQUE", LOG)); continue; } if (elementName != null && typeName == null) { boolean valid = validatePartType(elementName.getNamespaceURI(), elementName.getLocalPart(), true); if (!valid) { vResults.addError(new Message("TYPE_REF_NOT_FOUND", LOG, message.getQName(), part.getName(), elementName)); } } if (typeName != null && elementName == null) { boolean valid = validatePartType(typeName.getNamespaceURI(), typeName.getLocalPart(), false); if (!valid) { vResults.addError(new Message("TYPE_REF_NOT_FOUND", LOG, message.getQName(), part.getName(), typeName)); } } } } } private PortType getPortType(QName ptName) { PortType portType = this.definition.getPortType(ptName); if (portType == null) { for (Definition d : importedDefinitions) { portType = d.getPortType(ptName); if (portType != null) { break; } } } return portType; } private void collectValidationPointsForPortTypes() { for (QName ptName : portTypeRefNames) { PortType portType = getPortType(ptName); if (portType == null) { vResults.addError(new Message("NO_PORTTYPE", LOG, ptName)); continue; } XNode vPortTypeNode = getXNode(portType); for (Operation operation : getOperations(portType).values()) { XNode vOperationNode = getOperationXNode(vPortTypeNode, operation.getName()); if (operation.getInput() == null) { vResults.addError(new Message("WRONG_MEP", LOG, operation.getName(), portType.getQName())); continue; } javax.wsdl.Message inMsg = operation.getInput().getMessage(); if (inMsg == null) { addWarning("Operation " + operation.getName() + " in PortType: " + portType.getQName() + " has no input message"); } else { XNode vInMsgNode = getXNode(inMsg); vInMsgNode.setFailurePoint(getInputXNode(vOperationNode, operation.getInput().getName())); vNodes.add(vInMsgNode); messageRefNames.add(inMsg.getQName()); } if (operation.getOutput() != null) { javax.wsdl.Message outMsg = operation.getOutput().getMessage(); if (outMsg == null) { addWarning("Operation " + operation.getName() + " in PortType: " + portType.getQName() + " has no output message"); } else { XNode vOutMsgNode = getXNode(outMsg); vOutMsgNode.setFailurePoint(getOutputXNode(vOperationNode, operation.getOutput().getName())); vNodes.add(vOutMsgNode); messageRefNames.add(outMsg.getQName()); } } for (Iterator<?> iter = operation.getFaults().values().iterator(); iter.hasNext();) { Fault fault = (Fault) iter.next(); javax.wsdl.Message faultMsg = fault.getMessage(); XNode vFaultMsgNode = getXNode(faultMsg); vFaultMsgNode.setFailurePoint(getFaultXNode(vOperationNode, fault.getName())); vNodes.add(vFaultMsgNode); messageRefNames.add(faultMsg.getQName()); } } } } private boolean validatePartType(String namespace, String name, boolean isElement) { boolean partvalid = false; if (namespace.equals(WSDLConstants.NS_SCHEMA_XSD)) { if (isElement) { XmlSchemaElement schemaEle = schemaCollection.getElementByQName(new QName(WSDLConstants.NS_SCHEMA_XSD, name)); partvalid = schemaEle != null ? true : false; } else { if ("anyType".equals(name)) { return true; } XmlSchemaType schemaType = schemaCollection.getTypeByQName(new QName(WSDLConstants.NS_SCHEMA_XSD, name)); partvalid = schemaType != null ? true : false; } } else { if (isElement) { if (schemaCollection.getElementByQName(new QName(namespace, name)) != null) { partvalid = true; } } else { if (schemaCollection.getTypeByQName(new QName(namespace, name)) != null) { partvalid = true; } } } return partvalid; } public String getErrorMessage() { return vResults.toString(); } public Definition getDefinition() { return this.definition; } }