// // System.Web.Services.Description.ProtocolReflector.cs // // Author: // Tim Coleman (tim@timcoleman.com) // Lluis Sanchez Gual (lluis@ximian.com) // // Copyright (C) Tim Coleman, 2002 // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Web.Services; using System.Web.Services.Protocols; using System.Xml.Serialization; using System.Xml; using System.Xml.Schema; using System.Collections; namespace System.Web.Services.Description { public abstract class ProtocolReflector { #region Fields Binding binding; string defaultNamespace; MessageCollection headerMessages; Message inputMessage; LogicalMethodInfo[] methods; Operation operation; OperationBinding operationBinding; Message outputMessage; Port port; PortType portType; string protocolName; XmlSchemaExporter schemaExporter; Service service; ServiceDescription serviceDescription; Type serviceType; string serviceUrl; SoapSchemaExporter soapSchemaExporter; MethodStubInfo methodStubInfo; TypeStubInfo typeInfo; ArrayList extensionReflectors; ServiceDescriptionReflector serviceReflector; XmlReflectionImporter reflectionImporter; SoapReflectionImporter soapReflectionImporter; CodeIdentifiers portNames; #endregion // Fields #region Constructors protected ProtocolReflector () { defaultNamespace = WebServiceAttribute.DefaultNamespace; extensionReflectors = ExtensionManager.BuildExtensionReflectors (); } #endregion // Constructors #region Properties internal ServiceDescriptionReflector Parent { get { return serviceReflector; } } public Binding Binding { get { return binding; } } public string DefaultNamespace { get { return defaultNamespace; } } public MessageCollection HeaderMessages { get { return headerMessages; } // TODO: set } public Message InputMessage { get { return inputMessage; } } public LogicalMethodInfo Method { get { return methodStubInfo.MethodInfo; } } public WebMethodAttribute MethodAttribute { get { return methodStubInfo.MethodAttribute; } } public LogicalMethodInfo[] Methods { get { return typeInfo.LogicalType.LogicalMethods; } } public Operation Operation { get { return operation; } } public OperationBinding OperationBinding { get { return operationBinding; } } public Message OutputMessage { get { return outputMessage; } } public Port Port { get { return port; } } public PortType PortType { get { return portType; } } public abstract string ProtocolName { get; } public XmlReflectionImporter ReflectionImporter { get { if (reflectionImporter == null) { reflectionImporter = typeInfo.XmlImporter; if (reflectionImporter == null) reflectionImporter = new XmlReflectionImporter(); } return reflectionImporter; } } internal SoapReflectionImporter SoapReflectionImporter { get { if (soapReflectionImporter == null) { soapReflectionImporter = typeInfo.SoapImporter; if (soapReflectionImporter == null) soapReflectionImporter = new SoapReflectionImporter(); } return soapReflectionImporter; } } public XmlSchemaExporter SchemaExporter { get { return schemaExporter; } } internal SoapSchemaExporter SoapSchemaExporter { get { return soapSchemaExporter; } } public XmlSchemas Schemas { get { return serviceReflector.Schemas; } } public Service Service { get { return service; } } public ServiceDescription ServiceDescription { get { return serviceDescription; } } public ServiceDescriptionCollection ServiceDescriptions { get { return serviceReflector.ServiceDescriptions; } } public Type ServiceType { get { return serviceType; } } public string ServiceUrl { get { return serviceUrl; } } internal MethodStubInfo MethodStubInfo { get { return methodStubInfo; } } internal TypeStubInfo TypeInfo { get { return typeInfo; } } #endregion // Properties #region Methods internal void Reflect (ServiceDescriptionReflector serviceReflector, Type type, string url, XmlSchemaExporter xxporter, SoapSchemaExporter sxporter) { portNames = new CodeIdentifiers (); this.serviceReflector = serviceReflector; serviceUrl = url; serviceType = type; schemaExporter = xxporter; soapSchemaExporter = sxporter; typeInfo = TypeStubManager.GetTypeStub (type, ProtocolName); ServiceDescription desc = ServiceDescriptions [typeInfo.LogicalType.WebServiceNamespace]; if (desc == null) { desc = new ServiceDescription (); desc.TargetNamespace = typeInfo.LogicalType.WebServiceNamespace; desc.Name = typeInfo.LogicalType.WebServiceName; ServiceDescriptions.Add (desc); } ImportService (desc, typeInfo, url); } void ImportService (ServiceDescription desc, TypeStubInfo typeInfo, string url) { service = desc.Services [typeInfo.LogicalType.WebServiceName]; if (service == null) { service = new Service (); service.Name = typeInfo.LogicalType.WebServiceName; service.Documentation = typeInfo.LogicalType.Description; desc.Services.Add (service); } foreach (BindingInfo binfo in typeInfo.Bindings) ImportBinding (desc, service, typeInfo, url, binfo); } void ImportBinding (ServiceDescription desc, Service service, TypeStubInfo typeInfo, string url, BindingInfo binfo) { port = new Port (); port.Name = portNames.AddUnique (binfo.Name, port); bool bindingFull = true; if (binfo.Namespace != desc.TargetNamespace) { if (binfo.Location == null || binfo.Location == string.Empty) { ServiceDescription newDesc = new ServiceDescription(); newDesc.TargetNamespace = binfo.Namespace; newDesc.Name = binfo.Name; bindingFull = ImportBindingContent (newDesc, typeInfo, url, binfo); if (bindingFull) { int id = ServiceDescriptions.Add (newDesc); AddImport (desc, binfo.Namespace, GetWsdlUrl (url,id)); } } else { AddImport (desc, binfo.Namespace, binfo.Location); bindingFull = true; } } else bindingFull = ImportBindingContent (desc, typeInfo, url, binfo); if (bindingFull) { port.Binding = new XmlQualifiedName (binding.Name, binfo.Namespace); int n = 0; string name = binfo.Name; bool found; do { found = false; foreach (Port p in service.Ports) if (p.Name == name) { found = true; n++; name = binfo.Name + n; break; } } while (found); port.Name = name; service.Ports.Add (port); } if (binfo.WebServiceBindingAttribute != null && binfo.WebServiceBindingAttribute.ConformsTo != WsiProfiles.None && String.IsNullOrEmpty (binfo.WebServiceBindingAttribute.Name)) { BasicProfileViolationCollection violations = new BasicProfileViolationCollection (); desc.Types.Schemas.Add (Schemas); ServiceDescriptionCollection col = new ServiceDescriptionCollection (); col.Add (desc); ConformanceCheckContext ctx = new ConformanceCheckContext (col, violations); ctx.ServiceDescription = desc; ConformanceChecker[] checkers = WebServicesInteroperability.GetCheckers (binfo.WebServiceBindingAttribute.ConformsTo); foreach (ConformanceChecker checker in checkers) { ctx.Checker = checker; WebServicesInteroperability.Check (ctx, checker, binding); if (violations.Count > 0) throw new InvalidOperationException (violations [0].ToString ()); } } } bool ImportBindingContent (ServiceDescription desc, TypeStubInfo typeInfo, string url, BindingInfo binfo) { serviceDescription = desc; // Look for an unused name int n=0; string name = binfo.Name; bool found; do { found = false; foreach (Binding bi in desc.Bindings) if (bi.Name == name) { found = true; n++; name = binfo.Name+n; break; } } while (found); // Create the binding binding = new Binding (); binding.Name = name; binding.Type = new XmlQualifiedName (binding.Name, binfo.Namespace); if (binfo.WebServiceBindingAttribute != null && binfo.WebServiceBindingAttribute.EmitConformanceClaims) { XmlDocument doc = new XmlDocument (); XmlElement docElement = doc.CreateElement ("wsdl", "documentation", "http://schemas.xmlsoap.org/wsdl/"); XmlElement claimsElement = doc.CreateElement ("wsi", "Claim", "http://ws-i.org/schemas/conformanceClaim/"); claimsElement.Attributes.Append (doc.CreateAttribute ("conformsTo")).Value = "http://ws-i.org/profiles/basic/1.1"; docElement.AppendChild (claimsElement); binding.DocumentationElement = docElement; } portType = new PortType (); portType.Name = binding.Name; BeginClass (); foreach (SoapExtensionReflector reflector in extensionReflectors) { reflector.ReflectionContext = this; reflector.ReflectDescription (); } foreach (MethodStubInfo method in typeInfo.Methods) { methodStubInfo = method; string metBinding = ReflectMethodBinding (); if (typeInfo.GetBinding (metBinding) != binfo) continue; operation = new Operation (); operation.Name = method.OperationName; operation.Documentation = method.MethodAttribute.Description; // FIXME: SOAP 1.1 and SOAP 1.2 should share // the same message definitions. inputMessage = new Message (); inputMessage.Name = method.Name + ProtocolName + "In"; ServiceDescription.Messages.Add (inputMessage); outputMessage = new Message (); outputMessage.Name = method.Name + ProtocolName + "Out"; ServiceDescription.Messages.Add (outputMessage); OperationInput inOp = new OperationInput (); if (method.Name != method.OperationName) inOp.Name = method.Name; Operation.Messages.Add (inOp); inOp.Message = new XmlQualifiedName (inputMessage.Name, ServiceDescription.TargetNamespace); OperationOutput outOp = new OperationOutput (); if (method.Name != method.OperationName) outOp.Name = method.Name; Operation.Messages.Add (outOp); outOp.Message = new XmlQualifiedName (outputMessage.Name, ServiceDescription.TargetNamespace); portType.Operations.Add (operation); ImportOperationBinding (); if (!ReflectMethod ()) { // (It is somewhat hacky) If we don't // add input/output Messages, update // portType/input/@message and // porttype/output/@message. Message dupIn = Parent.MappedMessagesIn [method.MethodInfo]; ServiceDescription.Messages.Remove (inputMessage); inOp.Message = new XmlQualifiedName (dupIn.Name, ServiceDescription.TargetNamespace); Message dupOut = Parent.MappedMessagesOut [method.MethodInfo]; ServiceDescription.Messages.Remove (outputMessage); outOp.Message = new XmlQualifiedName (dupOut.Name, ServiceDescription.TargetNamespace); } foreach (SoapExtensionReflector reflector in extensionReflectors) { reflector.ReflectionContext = this; reflector.ReflectMethod (); } } EndClass (); if (portType.Operations.Count > 0) { desc.Bindings.Add (binding); desc.PortTypes.Add (portType); return true; } else return false; } void ImportOperationBinding () { operationBinding = new OperationBinding (); operationBinding.Name = methodStubInfo.OperationName; InputBinding inOp = new InputBinding (); operationBinding.Input = inOp; OutputBinding outOp = new OutputBinding (); operationBinding.Output = outOp; if (methodStubInfo.OperationName != methodStubInfo.Name) inOp.Name = outOp.Name = methodStubInfo.Name; binding.Operations.Add (operationBinding); } internal static void AddImport (ServiceDescription desc, string ns, string location) { Import im = new Import(); im.Namespace = ns; im.Location = location; desc.Imports.Add (im); } string GetWsdlUrl (string baseUrl, int id) { return baseUrl + "?wsdl=" + id; } protected virtual void BeginClass () { } protected virtual void EndClass () { } public ServiceDescription GetServiceDescription (string ns) { return ServiceDescriptions [ns]; } protected abstract bool ReflectMethod (); protected virtual string ReflectMethodBinding () { return null; } [MonoNotSupported("Not Implemented")] protected virtual void ReflectDescription () { throw new NotImplementedException (); } #endregion } }