/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.xml.ws.typeprovider; /** * This represents a wsdl, which is a combination of schema, service definitions, and * other types. One of these objects exist for each wsdl found. */ import gw.fs.IFile; import gw.internal.xml.ws.typeprovider.validator.WsdlValidator; import gw.internal.xml.xsd.ResourceFileXmlSchemaSource; import gw.internal.xml.xsd.typeprovider.IXmlTypeData; import gw.internal.xml.xsd.typeprovider.XmlSchemaImportInfo; import gw.internal.xml.xsd.typeprovider.XmlSchemaIndex; import gw.internal.xml.xsd.typeprovider.schema.*; import gw.internal.xml.xsd.typeprovider.schemaparser.SoapVersion; import gw.util.ILogger; import gw.util.Pair; import gw.xml.ws.WsdlValidationException; import gw.lang.reflect.IType; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; import java.net.URL; import java.util.*; public class Wsdl extends XmlSchemaIndex<Object> { private Map<String, IXmlTypeData> _types; private Set<String> _allTypeNames; private IFile _resourceFile; private Map<QName,WsdlFaultTypeData> _definedFaultTypes; /** * This will create a new WSDL object for a wsdl file. * * * @param typeProvider the wsdl type loader for this module * @param gwNamespace the namespace * @param resourceFile the file containing the wsdl * @throws java.io.IOException if can not read the resource file */ public Wsdl( WsdlTypeLoader typeProvider, String gwNamespace, IFile resourceFile ) throws IOException { super( typeProvider, gwNamespace, new ResourceFileXmlSchemaSource( resourceFile ), null ); _definedFaultTypes = new HashMap<QName, WsdlFaultTypeData>(); try { _resourceFile = resourceFile; // getLogger().debug("Wsdl for " + resourceFile.getPath() + " package=" + gwNamespace); } catch ( Throwable t ) { throw new WsdlValidationException( "Unable to process WSDL " + resourceFile, t ); } } public ILogger getLogger() { return getTypeProvider().getLogger(); } /** * If the types need to be initialized, this will create the parser and * parse the wsdl document. */ private void maybeInit() { if ( _types != null ) { // already inited return; } try { _allTypeNames = new HashSet<String>(); createTypes(); return; } catch (Exception ex) { getLogger().warn("Unable to parse WSDL: " + _resourceFile.getPath(), ex); } _types = Collections.emptyMap(); _allTypeNames = Collections.emptySet(); } /** * Once the wsdl has been parsed, then we need to create the various types * The first level of this is the Service, within each service is a port definition. * * This first pass just creates the types it does not populate the type info. They will be populated * when the actual type is referenced (typically the service). */ private void createTypes() { getLogger().debug("Wsdl.createTypes from " + _resourceFile); @SuppressWarnings({"unchecked"}) Map<QName, WsdlService> services = getWsdlDefinitions().getServices(); _types = new HashMap<String, IXmlTypeData>(); for ( Map.Entry<QName, WsdlService> entry : services.entrySet() ) { QName qname = entry.getKey(); WsdlService service = entry.getValue(); if ( getLogger().isDebugEnabled()) { getLogger().debug("Wsdl.createTypes on " + qname + " service=" + service.getQName()); } try { Map<WsdlBindingOperation,WsdlSoapHeadersTypeDataClass> soapHeadersTypeDataByBindingOperation = Collections.emptyMap(); Map<Pair<String,Set<QName>>,WsdlSoapHeadersTypeDataClass> existingSoapHeadersTypeDatasByBindingOperationNameAndHeaders = new HashMap<Pair<String, Set<QName>>, WsdlSoapHeadersTypeDataClass>(); List<WsdlPort> ports = service.getPorts(); WsdlPort preferredPort = null; for ( WsdlPort port : ports ) { boolean foundSupportedBinding = false; WsdlBinding binding = port.getBinding(); if ( binding.getSoapBinding() != null ) { WsdlSoapBinding soapBinding = binding.getSoapBinding(); if ("rpc".equals(soapBinding.getStyle())) { getLogger().warn("Skipping rpc style binding " + binding.getQName() + " in " + _resourceFile); } else if ( soapBinding.getSoapVersion() == SoapVersion.SOAP_12 ) { preferredPort = port; foundSupportedBinding = true; } else if ( soapBinding.getSoapVersion() == SoapVersion.SOAP_11 ) { if ( preferredPort == null ) { preferredPort = port; } foundSupportedBinding = true; } } if ( foundSupportedBinding ) { String portName = port.getQName().getLocalPart(); String serviceName = service.getQName().getLocalPart(); if ( portName.startsWith( serviceName ) ) { portName = portName.substring( serviceName.length() ); } if ( portName.startsWith( "_" ) || portName.startsWith( "-" ) ) { portName = portName.substring( 1 ); } String fqPortName = makeUniqueTypeName( getPackageName() + ".ports", XmlSchemaIndex.makeCamelCase( serviceName, NormalizationMode.PROPERCASE ) + "_" + XmlSchemaIndex.makeCamelCase( portName, NormalizationMode.PROPERCASE ) ); fqPortName = addUniqueToAllTypeNames( fqPortName ); // create types for any header sets for each operation for ( WsdlBindingOperation bindingOperation : binding.getBindingOperations() ) { List<WsdlSoapHeader> soapHeaders = bindingOperation.getBindingInput().getSoapHeaders(); if ( ! soapHeaders.isEmpty() ) { List<XmlSchemaElement> headerElements = new ArrayList<XmlSchemaElement>( soapHeaders.size() ); Set<QName> soapHeaderQNames = new HashSet<QName>(); for ( WsdlSoapHeader soapHeader : soapHeaders ) { XmlSchemaElement headerElement = soapHeader.getMessage().getPartByName( soapHeader.getPartName() ).getElement(); headerElements.add( headerElement ); soapHeaderQNames.add( headerElement.getQName() ); } Pair<String, Set<QName>> pair = new Pair<String, Set<QName>>( bindingOperation.getName(), soapHeaderQNames ); WsdlSoapHeadersTypeDataClass headersTypeData = existingSoapHeadersTypeDatasByBindingOperationNameAndHeaders.get( pair ); if ( headersTypeData == null ) { // create a type to represent the set of headers for this operation String bindingOperationNameForType = bindingOperation.getName(); bindingOperationNameForType = Character.toUpperCase( bindingOperationNameForType.charAt( 0 ) ) + bindingOperationNameForType.substring( 1 ); String headersTypeName = makeUniqueTypeName( getPackageName() + ".soapheaders", XmlSchemaIndex.makeCamelCase( bindingOperationNameForType + "Headers", NormalizationMode.PROPERCASE ) ); headersTypeName = addUniqueToAllTypeNames( headersTypeName ); headersTypeData = createWsdlSoapHeadersTypeData( headersTypeName, port, this, service, _resourceFile, false, headerElements ); _types.put( headersTypeName, headersTypeData ); existingSoapHeadersTypeDatasByBindingOperationNameAndHeaders.put( pair, headersTypeData ); } if ( soapHeadersTypeDataByBindingOperation.isEmpty() ) { soapHeadersTypeDataByBindingOperation = new HashMap<WsdlBindingOperation, WsdlSoapHeadersTypeDataClass>(); } soapHeadersTypeDataByBindingOperation.put( bindingOperation, headersTypeData ); } } _types.put( fqPortName, createWsdlPortTypeData( fqPortName, port, this, service, _resourceFile, false, soapHeadersTypeDataByBindingOperation ) ); } } if ( preferredPort != null ) { String typeName = makeUniqueTypeName( getPackageName(), XmlSchemaIndex.makeCamelCase( service.getQName().getLocalPart(), NormalizationMode.PROPERCASE ) ); typeName = addUniqueToAllTypeNames(typeName); IXmlTypeData type = createWsdlPortTypeData( typeName, preferredPort, this, service, _resourceFile, true, soapHeadersTypeDataByBindingOperation ); _types.put( typeName, type ); } } catch (Exception e) { getLogger().error("Wsdl.createTypes on " + qname + " service=" + service.getQName() + ":", e); } } for ( WsdlPortType wsdlPortType : getWsdlDefinitions().getPortTypes() ) { for ( WsdlPortTypeOperation wsdlPortTypeOperation : wsdlPortType.getOperations() ) { for ( WsdlPortTypeFault wsdlPortTypeFault : wsdlPortTypeOperation.getFaults() ) { WsdlMessage message = wsdlPortTypeFault.getMessage(); Collection<WsdlPart> parts = message.getParts(); if ( parts.size() != 1 ){ getLogger().warn( "In WSDL " + getPackageName() + ", fault " + wsdlPortTypeFault.getName() + " was ignored since multiple fault parts are not supported" ); } else { WsdlPart part = parts.iterator().next(); QName faultElementQName = part.getElementName(); WsdlFaultTypeData faultTypeData = _definedFaultTypes.get( faultElementQName ); if ( faultTypeData == null ) { String faultName = faultElementQName.getLocalPart(); String fqFaultTypeName = makeUniqueTypeName( getPackageName() + ".faults", XmlSchemaIndex.makeCamelCase(faultName, null) ); faultTypeData = new WsdlFaultTypeData( this, fqFaultTypeName, part ); _types.put( fqFaultTypeName, faultTypeData ); _definedFaultTypes.put( faultElementQName, faultTypeData ); } } } } } } public IType getWsdlFaultTypeByElementQName(QName name) { WsdlFaultTypeData data = _definedFaultTypes.get(name); return data == null ? null : data.getType(); } protected WsdlPortTypeData createWsdlPortTypeData( String typeName, WsdlPort preferredPort, Wsdl wsdl, WsdlService service, IFile resourceFile, boolean isService, Map<WsdlBindingOperation, WsdlSoapHeadersTypeDataClass> soapHeadersTypeDataByBindingOperation ) { return new WsdlPortTypeData( typeName, preferredPort, wsdl, service, resourceFile, isService, soapHeadersTypeDataByBindingOperation ); } protected WsdlSoapHeadersTypeDataClass createWsdlSoapHeadersTypeData( String typeName, WsdlPort preferredPort, Wsdl wsdl, WsdlService service, IFile resourceFile, boolean isService, List<XmlSchemaElement> headerElements ) { return new WsdlSoapHeadersTypeDataClass( typeName, preferredPort, wsdl, service, resourceFile, isService, headerElements ); } /** * This ensures that the type is unique for this wsdl * * @param typeName the type name to ensure uniqueness * @return a unique type name */ private String addUniqueToAllTypeNames(String typeName) { if ( _allTypeNames.contains( typeName ) ) { int suffix = 2; String newTypeName; do { newTypeName = typeName + suffix++; } while ( _allTypeNames.contains( newTypeName ) ); typeName = newTypeName; if ( getLogger().isDebugEnabled() ) { getLogger().debug("Wsdl.addUniqueToAllTypeNames type was not unique " + typeName); } } _allTypeNames.add( typeName ); return typeName; } /** * Given a fully qualified name it will return a reference to the type from the typesystem * * @param fullyQualifiedName the typename * @return a reference to the type */ @Override public IXmlTypeData getAdditionalTypeData( String fullyQualifiedName ) { maybeInit(); return _types.get( fullyQualifiedName ); } /** * This will return all types from the wsdl * * @return set of string */ @Override public Set<String> getAdditionalTypeNames() { maybeInit(); return _allTypeNames; } public XmlSchemaImportInfo getImportInfo() { return new XmlSchemaImportInfo( getWsdlDefinitions().getTargetNamespace(), getXSDSourcePath(), true ); } /** * Get the typeloader * * @return this will get the type loader for this wsdl */ public WsdlTypeLoader getTypeProvider() { return (WsdlTypeLoader) super.getTypeLoader(); } public void validate( Map<Pair<URL, String>, XmlSchema> caches ) { super.validate( caches ); WsdlValidator.validateWsdl( getWsdlDefinitions() ); } public long getFingerprint() { return 0L; // TODO } }