* System.Web.Services.dll.sources: Added
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / Methods.cs
index c397bd4a471f986efabb0e3bd576a1da62cd5dfd..c68ff7d2a2904939171ad21444004a58b3e0717c 100644 (file)
@@ -3,6 +3,7 @@
 //
 // Author:
 //   Miguel de Icaza
+//   Lluis Sanchez Gual (lluis@ximian.com)
 //
 // (C) 2003 Ximian, Inc.
 //
@@ -12,6 +13,7 @@
 
 using System.Reflection;
 using System.Collections;
+using System.Xml;
 using System.Xml.Serialization;
 using System.Web.Services;
 using System.Web.Services.Description;
@@ -22,12 +24,8 @@ namespace System.Web.Services.Protocols {
        // This class represents all the information we extract from a MethodInfo
        // in the SoapHttpClientProtocol derivative stub class
        //
-       internal class MethodStubInfo {
-               internal LogicalMethodInfo MethodInfo;
-
-               // The name used bythe stub class to reference this method.
-               internal string Name;
-               
+       internal class SoapMethodStubInfo : MethodStubInfo
+       {
                internal string Action;
                internal string Binding;
 
@@ -39,29 +37,55 @@ namespace System.Web.Services.Protocols {
                internal string ResponseName;
                internal string ResponseNamespace;
                
-               internal bool   OneWay;
+               internal bool OneWay;
                internal SoapParameterStyle ParameterStyle;
+               internal SoapBindingStyle SoapBindingStyle;
+               internal SoapBindingUse Use;
 
                internal XmlSerializer RequestSerializer;
                internal XmlSerializer ResponseSerializer;
 
+               internal HeaderInfo[] Headers;
+
+               internal SoapExtensionRuntimeConfig[] SoapExtensions;
+               
+               private XmlMembersMapping[] _membersMapping;
+               
+               internal XmlMembersMapping InputMembersMapping
+               {
+                       get { return _membersMapping[0]; }
+               }
+
+               internal XmlMembersMapping OutputMembersMapping
+               {
+                       get { return _membersMapping[1]; }
+               }
+
                //
                // Constructor
                //
-               MethodStubInfo (TypeStubInfo parent, LogicalMethodInfo source, object kind, XmlReflectionImporter importer)
+               public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
+               : base (typeStub, source)
                {
-                       MethodInfo = source;
-
+                       SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
                        XmlElementAttribute optional_ns = null;
-                       
-                       if (kind is SoapDocumentMethodAttribute){
+
+                       if (kind == null) {
+                               Use = parent.Use;
+                               RequestName = "";
+                               RequestNamespace = parent.WebServiceNamespace;
+                               ResponseName = "";
+                               ResponseNamespace = parent.WebServiceNamespace;
+                               ParameterStyle = parent.ParameterStyle;
+                               SoapBindingStyle = parent.SoapBindingStyle;
+                               OneWay = false;
+                       }
+                       else if (kind is SoapDocumentMethodAttribute){
                                SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
                                
-                               SoapBindingUse use = dma.Use;
-                               if (use == SoapBindingUse.Default)
-                                       use = parent.Use;
-                               if (use != SoapBindingUse.Literal)
-                                       throw new Exception ("Only SoapBindingUse.Literal supported");
+                               Use = dma.Use;
+                               if (Use == SoapBindingUse.Default)
+                                       Use = parent.Use;
                                
                                Action = dma.Action;
                                Binding = dma.Binding;
@@ -73,27 +97,26 @@ namespace System.Web.Services.Protocols {
                                if (ParameterStyle == SoapParameterStyle.Default)
                                        ParameterStyle = parent.ParameterStyle;
                                OneWay = dma.OneWay;
+                               SoapBindingStyle = SoapBindingStyle.Document;
                        } else {
                                SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
+                               Use = SoapBindingUse.Encoded;   // RPC always use encoded
 
-                               // Assuem that the TypeStub already caught any possible use of Encoded
                                Action = rma.Action;
                                Binding = rma.Binding;
                                RequestName = rma.RequestElementName;
                                RequestNamespace = rma.RequestNamespace;
                                ResponseNamespace = rma.ResponseNamespace;
                                ResponseName = rma.ResponseElementName;
+                               ParameterStyle = SoapParameterStyle.Wrapped;
                                OneWay = rma.OneWay;
+                               SoapBindingStyle = SoapBindingStyle.Rpc;
 
                                // For RPC calls, make all arguments be part of the empty namespace
                                optional_ns = new XmlElementAttribute ();
                                optional_ns.Namespace = "";
                        }
-                       if (Binding == "")
-                               Binding = parent.BindingName;
-                       if (RequestName == "")
-                               RequestName = source.Name;
-                       
+
                        if (OneWay){
                                if (source.ReturnType != typeof (void))
                                        throw new Exception ("OneWay methods should not have a return value");
@@ -101,71 +124,77 @@ namespace System.Web.Services.Protocols {
                                        throw new Exception ("OneWay methods should not have out/ref parameters");
                        }
                        
-                       object [] o = source.GetCustomAttributes (typeof (WebMethodAttribute));
-                       if (o.Length == 1){
-                               WebMethodAttribute wma = (WebMethodAttribute) o [0];
+                       if (RequestNamespace == "") RequestNamespace = parent.WebServiceNamespace;
+                       if (ResponseNamespace == "") ResponseNamespace = parent.WebServiceNamespace;
+                       if (RequestName == "") RequestName = Name;
+                       if (ResponseName == "") ResponseName = Name + "Response";
+                       if (Binding == null || Binding == "") Binding = parent.DefaultBinding;
+                       else if (parent.GetBinding (Binding) == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'");
+                               
+                       if (Action == null || Action == "")
+                               Action = RequestNamespace.EndsWith("/") ? (RequestNamespace + Name) : (RequestNamespace + "/" + Name);
+                       
+                       bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
+                       
+                       XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
+                       XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
 
-                               Name = wma.MessageName;
-                               if (Name == "")
-                                       Name = source.Name;
-                       } else
-                               Name = source.Name;
+                       _membersMapping = new XmlMembersMapping [2];
 
-                       if (ResponseName == "")
-                               ResponseName = Name + "Response";
-                       
-                       MakeRequestSerializer (importer, optional_ns);
-                       MakeResponseSerializer (importer, optional_ns);
-               }
+                       if (Use == SoapBindingUse.Literal) {
+                               _membersMapping [0] = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
+                               _membersMapping [1] = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
+                       }
+                       else {
+                               _membersMapping [0] = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, true);
+                               _membersMapping [1] = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, true);
+                       }
 
-               static internal MethodStubInfo Create (TypeStubInfo parent, LogicalMethodInfo lmi, XmlReflectionImporter importer)
-               {
-                       object [] o = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
-                       if (o.Length == 0){
-                               o = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
-                               if (o.Length == 0)
-                                       return null;
-                               return new MethodStubInfo (parent, lmi, o [0], importer);
-                       } else 
-                               return new MethodStubInfo (parent, lmi, o [0], importer);
+                       XmlSerializer [] s = null;
+                       s = XmlSerializer.FromMappings (_membersMapping);
+                       RequestSerializer = s [0];
+                       ResponseSerializer = s [1];
+
+                       object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
+                       Headers = new HeaderInfo[o.Length];
+                       for (int i = 0; i < o.Length; i++) {
+                               SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
+                               MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
+                               if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName);
+                               
+                               Type headerType = (mems[0] is FieldInfo) ? ((FieldInfo)mems[0]).FieldType : ((PropertyInfo)mems[0]).PropertyType;
+                               Headers [i] = new HeaderInfo (mems[0], att);
+                               parent.RegisterHeaderType (headerType);
+                       }
+
+                       SoapExtensions = SoapExtension.GetMethodExtensions (source);
                }
 
-               void MakeRequestSerializer (XmlReflectionImporter importer, XmlElementAttribute optional_ns)
+               XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
                {
                        ParameterInfo [] input = MethodInfo.InParameters;
                        XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
 
-                       for (int i = 0; i < input.Length; i++){
+                       for (int i = 0; i < input.Length; i++)
+                       {
                                XmlReflectionMember m = new XmlReflectionMember ();
                                m.IsReturnValue = false;
                                m.MemberName = input [i].Name;
                                m.MemberType = input [i].ParameterType;
+
+                               m.XmlAttributes = new XmlAttributes (input[i]);
+                               m.SoapAttributes = new SoapAttributes (input[i]);
+
                                if (m.MemberType.IsByRef)
                                        m.MemberType = m.MemberType.GetElementType ();
-
                                if (optional_ns != null)
                                        m.XmlAttributes.XmlElements.Add (optional_ns);
                                in_members [i] = m;
                        }
-
-                       XmlMembersMapping [] members = new XmlMembersMapping [1];
-                       try {
-                               members [0] = importer.ImportMembersMapping (RequestName, RequestNamespace, in_members, true);
-                               XmlSerializer [] s = null;
-                               s = XmlSerializer.FromMappings (members);
-                               RequestSerializer = s [0];
-                       } catch {
-                               Console.WriteLine ("Got exception while creating serializer");
-                               Console.WriteLine ("Method name: " + RequestName + " parameters are:");
-
-                               for (int i = 0; i < input.Length; i++){
-                                       Console.WriteLine ("    {0}: {1} {2}", i, in_members [i].MemberName, in_members [i].MemberType);
-                               }
-                               throw;
-                       }
+                       return in_members;
                }
-
-               void MakeResponseSerializer (XmlReflectionImporter importer, XmlElementAttribute optional_ns)
+               
+               XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
                {
                        ParameterInfo [] output = MethodInfo.OutParameters;
                        bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
@@ -173,22 +202,30 @@ namespace System.Web.Services.Protocols {
                        XmlReflectionMember m;
                        int idx = 0;
 
-                       if (has_return_value){
+                       if (has_return_value)
+                       {
                                m = new XmlReflectionMember ();
                                m.IsReturnValue = true;
                                m.MemberName = RequestName + "Result";
                                m.MemberType = MethodInfo.ReturnType;
+
+                               m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
+                               m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
+
                                if (optional_ns != null)
                                        m.XmlAttributes.XmlElements.Add (optional_ns);
                                idx++;
                                out_members [0] = m;
                        }
                        
-                       for (int i = 0; i < output.Length; i++){
+                       for (int i = 0; i < output.Length; i++)
+                       {
                                m = new XmlReflectionMember ();
                                m.IsReturnValue = false;
                                m.MemberName = output [i].Name;
                                m.MemberType = output [i].ParameterType;
+                               m.XmlAttributes = new XmlAttributes (output[i]);
+                               m.SoapAttributes = new SoapAttributes (output[i]);
 
                                if (m.MemberType.IsByRef)
                                        m.MemberType = m.MemberType.GetElementType ();
@@ -196,73 +233,97 @@ namespace System.Web.Services.Protocols {
                                        m.XmlAttributes.XmlElements.Add (optional_ns);
                                out_members [i + idx] = m;
                        }
+                       return out_members;
+               }
 
-                       try {
-                               XmlMembersMapping [] members = new XmlMembersMapping [1];
-                               members [0] = importer.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, true);
-                               XmlSerializer [] s = XmlSerializer.FromMappings (members);
-                               ResponseSerializer = s [0];
-                       } catch {
-                               Console.WriteLine ("Got exception while creating serializer");
-                               Console.WriteLine ("Method name: " + ResponseName + " parameters are:");
-
-                               for (int i = 0; i < out_members.Length; i++){
-                                       Console.WriteLine ("    {0}: {1} {2}", i, out_members [i].MemberName, out_members [i].MemberType);
-                               }
-                               throw;
-                       }
+               public HeaderInfo GetHeaderInfo (Type headerType)
+               {
+                       foreach (HeaderInfo headerInfo in Headers)
+                               if (headerInfo.HeaderType == headerType) return headerInfo;
+                       return null;
+               }
+       }
+
+       internal class HeaderInfo
+       {
+               internal MemberInfo Member;
+               internal SoapHeaderAttribute AttributeInfo;
+               internal Type HeaderType;
+
+               public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
+               {
+                       Member = member;
+                       AttributeInfo = attributeInfo;
+                       if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
+                       else HeaderType = ((FieldInfo)Member).FieldType;
+               }
+               
+               public object GetHeaderValue (object ob)
+               {
+                       if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
+                       else return ((FieldInfo)Member).GetValue (ob);
+               }
 
-                       ResponseSerializer.UnknownNode += new XmlNodeEventHandler (e);
+               public void SetHeaderValue (object ob, object value)
+               {
+                       if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
+                       else ((FieldInfo)Member).SetValue (ob, value);
                }
 
-               static void e (object o, XmlNodeEventArgs a)
+               public SoapHeaderDirection Direction
                {
-                       Console.WriteLine ("Unexpected Node: {5}:{6} {0}/{1}/{2}/{3}/{4}",
-                                          a.LocalName, a.Name, a.NamespaceURI, a.NodeType, a.Text,
-                                          a.LineNumber, a.LinePosition);
-                       throw new Exception ();
+                       get { return AttributeInfo.Direction; }
                }
        }
 
+       internal class Fault
+       {
+               public Fault () {}
+
+               public Fault (SoapException ex) 
+               {
+                       faultcode = ex.Code;
+                       faultstring = ex.Message;
+                       faultactor = ex.Actor;
+                       detail = ex.Detail;
+               }
+
+               public XmlQualifiedName faultcode;
+               public string faultstring;
+               public string faultactor;
+               public XmlNode detail;
+       }
+       
        //
        // Holds the metadata loaded from the type stub, as well as
        // the metadata for all the methods in the type
        //
-       internal class TypeStubInfo {
-               Hashtable name_to_method = new Hashtable ();
+       internal class SoapTypeStubInfo : TypeStubInfo
+       {
+               Hashtable header_serializers = new Hashtable ();
+               Hashtable header_serializers_byname = new Hashtable ();
 
                // Precomputed
                internal SoapParameterStyle      ParameterStyle;
                internal SoapServiceRoutingStyle RoutingStyle;
                internal SoapBindingUse          Use;
-               internal string                  WebServiceName;
-               internal string                  WebServiceNamespace;
-               internal string                  BindingLocation;
-               internal string                  BindingName;
-               internal string                  BindingNamespace;
-
-               void GetTypeAttributes (Type t)
+               internal XmlSerializer           FaultSerializer;
+               internal SoapExtensionRuntimeConfig[][] SoapExtensions;
+               internal SoapBindingStyle SoapBindingStyle;
+               internal XmlReflectionImporter  xmlImporter;
+               internal SoapReflectionImporter soapImporter;
+
+               public SoapTypeStubInfo (Type t)
+               : base (t)
                {
+                       xmlImporter = new XmlReflectionImporter ();
+                       soapImporter = new SoapReflectionImporter ();
+                       
                        object [] o;
 
                        o = t.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
-                       if (o.Length != 1)
-                               throw new Exception ("Expected WebServiceBindingAttribute on "+ t.Name);
-                       WebServiceBindingAttribute b = (WebServiceBindingAttribute) o [0];
-                       BindingLocation = b.Location;
-                       BindingName = b.Name;
-                       BindingNamespace = b.Namespace;
-
-                       o = t.GetCustomAttributes (typeof (WebService), false);
-                       if (o.Length == 1){
-                               WebServiceAttribute a = (WebServiceAttribute) o [0];
-
-                               WebServiceName = a.Name;
-                               WebServiceNamespace = a.Namespace;
-                       } else {
-                               WebServiceName = t.Name;
-                               WebServiceNamespace = WebServiceAttribute.DefaultNamespace;
-                       }
+                       foreach (WebServiceBindingAttribute at in o)
+                               Bindings.Add (new BindingInfo (at, WebServiceNamespace));
 
                        o = t.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
                        if (o.Length == 1){
@@ -271,6 +332,7 @@ namespace System.Web.Services.Protocols {
                                ParameterStyle = a.ParameterStyle;
                                RoutingStyle = a.RoutingStyle;
                                Use = a.Use;
+                               SoapBindingStyle = SoapBindingStyle.Document;
                        } else {
                                o = t.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
                                if (o.Length == 1){
@@ -278,79 +340,72 @@ namespace System.Web.Services.Protocols {
                                        
                                        ParameterStyle = SoapParameterStyle.Wrapped;
                                        RoutingStyle = srs.RoutingStyle;
-                                       Use = SoapBindingUse.Literal;
+                                       Use = SoapBindingUse.Encoded;
+                                       SoapBindingStyle = SoapBindingStyle.Rpc;
                                } else {
                                        ParameterStyle = SoapParameterStyle.Wrapped;
                                        RoutingStyle = SoapServiceRoutingStyle.SoapAction;
                                        Use = SoapBindingUse.Literal;
+                                       SoapBindingStyle = SoapBindingStyle.Document;
                                }
                        }
+                       
+                       if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
+                       if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
+
+                       FaultSerializer = new XmlSerializer (typeof(Fault));
+                       SoapExtensions = SoapExtension.GetTypeExtensions (t);
                }
 
-               //
-               // Extract all method information
-               //
-               void GetTypeMethods (Type t, XmlReflectionImporter importer)
+               public override XmlReflectionImporter XmlImporter 
                {
-                       MethodInfo [] type_methods = t.GetMethods (BindingFlags.Instance | BindingFlags.Public);
-                       LogicalMethodInfo [] methods = LogicalMethodInfo.Create (type_methods, LogicalMethodTypes.Sync);
-
-                       foreach (LogicalMethodInfo mi in methods){
-                               MethodStubInfo msi = MethodStubInfo.Create (this, mi, importer);
-
-                               if (msi == null)
-                                       continue;
-
-                               name_to_method [msi.Name] = msi;
-                       }
+                       get { return xmlImporter; }
                }
-               
-               internal TypeStubInfo (Type t)
-               {
-                       GetTypeAttributes (t);
 
-                       XmlReflectionImporter importer = new XmlReflectionImporter ();
-                       GetTypeMethods (t, importer);
+               public override SoapReflectionImporter SoapImporter 
+               {
+                       get { return soapImporter; }
                }
-
-               internal MethodStubInfo GetMethod (string name)
+               
+               public override string ProtocolName
                {
-                       return (MethodStubInfo) name_to_method [name];
+                       get { return "Soap"; }
                }
-       }
-       
-       //
-       // Manages 
-       //
-       internal class TypeStubManager {
-               static Hashtable type_to_manager;
                
-               static TypeStubManager ()
+               protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
                {
-                       type_to_manager = new Hashtable ();
+                       object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
+                       if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
+
+                       if (ats.Length == 0 && isClientProxy)
+                               return null;
+                       else if (ats.Length == 0)
+                               return new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
+                       else
+                               return new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
                }
-
-               //
-               // This needs to be thread safe
-               //
-               static internal TypeStubInfo GetTypeStub (Type t)
+               
+               internal void RegisterHeaderType (Type type)
                {
-                       TypeStubInfo tm = (TypeStubInfo) type_to_manager [t];
-
-                       if (tm != null)
-                               return tm;
+                       XmlSerializer s = (XmlSerializer) header_serializers [type];
+                       if (s != null) return;
 
-                       lock (typeof (TypeStubInfo)){
-                               tm = (TypeStubInfo) type_to_manager [t];
+                       XmlReflectionImporter ri = new XmlReflectionImporter ();
+                       XmlTypeMapping tm = ri.ImportTypeMapping (type, WebServiceAttribute.DefaultNamespace);
+                       s = new XmlSerializer (tm);
 
-                               if (tm != null)
-                                       return tm;
-                               
-                               tm = new TypeStubInfo (t);
-                               type_to_manager [t] = tm;
+                       header_serializers [type] = s;
+                       header_serializers_byname [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = s;
+               }
 
-                               return tm;
-                       }
+               internal XmlSerializer GetHeaderSerializer (Type type)
+               {
+                       return (XmlSerializer) header_serializers [type];
                }
+       
+               internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname)
+               {
+                       return (XmlSerializer) header_serializers_byname [qname];
+               }               
        }
 }