* Methods.cs: Get the method namespace from the binding, not from the web
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / Methods.cs
1 //
2 // Methods.cs: Information about a method and its mapping to a SOAP web service.
3 //
4 // Author:
5 //   Miguel de Icaza
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // (C) 2003 Ximian, Inc.
9 //
10
11 using System.Reflection;
12 using System.Collections;
13 using System.Xml;
14 using System.Xml.Serialization;
15 using System.Web.Services;
16 using System.Web.Services.Description;
17
18 namespace System.Web.Services.Protocols {
19
20         //
21         // This class represents all the information we extract from a MethodInfo
22         // in the SoapHttpClientProtocol derivative stub class
23         //
24         internal class SoapMethodStubInfo : MethodStubInfo
25         {
26                 internal string Action;
27                 internal string Binding;
28
29                 // The name/namespace of the request 
30                 internal string RequestName;
31                 internal string RequestNamespace;
32
33                 // The name/namespace of the response.
34                 internal string ResponseName;
35                 internal string ResponseNamespace;
36                 
37                 internal bool OneWay;
38                 internal SoapParameterStyle ParameterStyle;
39                 internal SoapBindingStyle SoapBindingStyle;
40                 internal SoapBindingUse Use;
41
42                 internal HeaderInfo[] Headers;
43                 internal SoapExtensionRuntimeConfig[] SoapExtensions;
44                 
45                 internal XmlMembersMapping InputMembersMapping;
46                 internal XmlMembersMapping OutputMembersMapping;
47                 
48                 private int requestSerializerId;
49                 private int responseSerializerId;
50                 
51                 internal XmlSerializer RequestSerializer
52                 {
53                         get { return TypeStub.GetSerializer (requestSerializerId); }
54                 }
55                 
56                 internal XmlSerializer ResponseSerializer
57                 {
58                         get { return TypeStub.GetSerializer (responseSerializerId); }
59                 }
60
61                 //
62                 // Constructor
63                 //
64                 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
65                 : base (typeStub, source)
66                 {
67                         SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
68                         XmlElementAttribute optional_ns = null;
69
70                         if (kind == null) {
71                                 Use = parent.Use;
72                                 RequestName = "";
73                                 RequestNamespace = "";
74                                 ResponseName = "";
75                                 ResponseNamespace = "";
76                                 ParameterStyle = parent.ParameterStyle;
77                                 SoapBindingStyle = parent.SoapBindingStyle;
78                                 OneWay = false;
79                         }
80                         else if (kind is SoapDocumentMethodAttribute){
81                                 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
82                                 
83                                 Use = dma.Use;
84                                 if (Use == SoapBindingUse.Default) {
85                                         if (parent.SoapBindingStyle == SoapBindingStyle.Document)
86                                                 Use = parent.Use;
87                                         else
88                                                 Use = SoapBindingUse.Literal;
89                                 }
90                                 
91                                 Action = dma.Action;
92                                 Binding = dma.Binding;
93                                 RequestName = dma.RequestElementName;
94                                 RequestNamespace = dma.RequestNamespace;
95                                 ResponseName = dma.ResponseElementName;
96                                 ResponseNamespace = dma.ResponseNamespace;
97                                 ParameterStyle = dma.ParameterStyle;
98                                 if (ParameterStyle == SoapParameterStyle.Default)
99                                         ParameterStyle = parent.ParameterStyle;
100                                 OneWay = dma.OneWay;
101                                 SoapBindingStyle = SoapBindingStyle.Document;
102                         } else {
103                                 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
104                                 Use = SoapBindingUse.Encoded;   // RPC always use encoded
105
106                                 Action = rma.Action;
107                                 Binding = rma.Binding;
108                                 RequestName = rma.RequestElementName;
109                                 RequestNamespace = rma.RequestNamespace;
110                                 ResponseNamespace = rma.ResponseNamespace;
111                                 ResponseName = rma.ResponseElementName;
112                                 ParameterStyle = SoapParameterStyle.Wrapped;
113                                 OneWay = rma.OneWay;
114                                 SoapBindingStyle = SoapBindingStyle.Rpc;
115
116                                 // For RPC calls, make all arguments be part of the empty namespace
117                                 optional_ns = new XmlElementAttribute ();
118                                 optional_ns.Namespace = "";
119                         }
120
121                         if (OneWay){
122                                 if (source.ReturnType != typeof (void))
123                                         throw new Exception ("OneWay methods should not have a return value");
124                                 if (source.OutParameters.Length != 0)
125                                         throw new Exception ("OneWay methods should not have out/ref parameters");
126                         }
127                         
128                         if (Binding == null || Binding == "") Binding = parent.DefaultBinding;
129                         BindingInfo binfo = parent.GetBinding (Binding);
130                         if (binfo == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'");
131                         
132                         string serviceNamespace = binfo.Namespace;
133                                 
134                         if (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
135                         if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
136                         if (RequestName == "") RequestName = Name;
137                         if (ResponseName == "") ResponseName = Name + "Response";
138                         if (Action == null || Action == "")
139                                 Action = RequestNamespace.EndsWith("/") ? (RequestNamespace + Name) : (RequestNamespace + "/" + Name);
140                         
141                         bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
142                         bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
143                         
144                         XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
145                         XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
146
147                         if (Use == SoapBindingUse.Literal) {
148                                 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
149                                 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
150                         }
151                         else {
152                                 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, writeAccessors);
153                                 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, writeAccessors);
154                         }
155
156                         requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
157                         responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
158
159                         object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
160                         Headers = new HeaderInfo[o.Length];
161                         for (int i = 0; i < o.Length; i++) {
162                                 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
163                                 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
164                                 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName);
165                                 
166                                 Type headerType = (mems[0] is FieldInfo) ? ((FieldInfo)mems[0]).FieldType : ((PropertyInfo)mems[0]).PropertyType;
167                                 Headers [i] = new HeaderInfo (mems[0], att);
168                                 parent.RegisterHeaderType (headerType, serviceNamespace, Use);
169                         }
170
171                         SoapExtensions = SoapExtension.GetMethodExtensions (source);
172                 }
173
174                 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
175                 {
176                         ParameterInfo [] input = MethodInfo.InParameters;
177                         XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
178
179                         for (int i = 0; i < input.Length; i++)
180                         {
181                                 XmlReflectionMember m = new XmlReflectionMember ();
182                                 m.IsReturnValue = false;
183                                 m.MemberName = input [i].Name;
184                                 m.MemberType = input [i].ParameterType;
185
186                                 m.XmlAttributes = new XmlAttributes (input[i]);
187                                 m.SoapAttributes = new SoapAttributes (input[i]);
188
189                                 if (m.MemberType.IsByRef)
190                                         m.MemberType = m.MemberType.GetElementType ();
191                                 if (optional_ns != null)
192                                         m.XmlAttributes.XmlElements.Add (optional_ns);
193                                 in_members [i] = m;
194                         }
195                         return in_members;
196                 }
197                 
198                 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
199                 {
200                         ParameterInfo [] output = MethodInfo.OutParameters;
201                         bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
202                         XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
203                         XmlReflectionMember m;
204                         int idx = 0;
205
206                         if (has_return_value)
207                         {
208                                 m = new XmlReflectionMember ();
209                                 m.IsReturnValue = true;
210                                 m.MemberName = RequestName + "Result";
211                                 m.MemberType = MethodInfo.ReturnType;
212
213                                 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
214                                 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
215
216                                 if (optional_ns != null)
217                                         m.XmlAttributes.XmlElements.Add (optional_ns);
218                                 idx++;
219                                 out_members [0] = m;
220                         }
221                         
222                         for (int i = 0; i < output.Length; i++)
223                         {
224                                 m = new XmlReflectionMember ();
225                                 m.IsReturnValue = false;
226                                 m.MemberName = output [i].Name;
227                                 m.MemberType = output [i].ParameterType;
228                                 m.XmlAttributes = new XmlAttributes (output[i]);
229                                 m.SoapAttributes = new SoapAttributes (output[i]);
230
231                                 if (m.MemberType.IsByRef)
232                                         m.MemberType = m.MemberType.GetElementType ();
233                                 if (optional_ns != null)
234                                         m.XmlAttributes.XmlElements.Add (optional_ns);
235                                 out_members [i + idx] = m;
236                         }
237                         return out_members;
238                 }
239
240                 public HeaderInfo GetHeaderInfo (Type headerType)
241                 {
242                         foreach (HeaderInfo headerInfo in Headers)
243                                 if (headerInfo.HeaderType == headerType) return headerInfo;
244                         return null;
245                 }
246         }
247
248         internal class HeaderInfo
249         {
250                 internal MemberInfo Member;
251                 internal SoapHeaderAttribute AttributeInfo;
252                 internal Type HeaderType;
253
254                 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
255                 {
256                         Member = member;
257                         AttributeInfo = attributeInfo;
258                         if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
259                         else HeaderType = ((FieldInfo)Member).FieldType;
260                 }
261                 
262                 public object GetHeaderValue (object ob)
263                 {
264                         if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
265                         else return ((FieldInfo)Member).GetValue (ob);
266                 }
267
268                 public void SetHeaderValue (object ob, object value)
269                 {
270                         if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
271                         else ((FieldInfo)Member).SetValue (ob, value);
272                 }
273
274                 public SoapHeaderDirection Direction
275                 {
276                         get { return AttributeInfo.Direction; }
277                 }
278         }
279
280         // FIXME: this class should be internal, but it needs to be public in
281         // order to be serialized using XmlSerializer.
282         [SoapType (Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
283         [XmlType (Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
284         public class Fault
285         {
286                 public Fault () {}
287
288                 public Fault (SoapException ex) 
289                 {
290                         faultcode = ex.Code;
291                         faultstring = ex.Message;
292                         faultactor = ex.Actor;
293                         detail = ex.Detail;
294                 }
295
296                 public XmlQualifiedName faultcode;
297                 public string faultstring;
298                 public string faultactor;
299                 
300                 [SoapIgnore]
301                 public XmlNode detail;
302         }
303         
304         //
305         // Holds the metadata loaded from the type stub, as well as
306         // the metadata for all the methods in the type
307         //
308         internal class SoapTypeStubInfo : TypeStubInfo
309         {
310                 Hashtable[] header_serializers = new Hashtable [3];
311                 Hashtable[] header_serializers_byname = new Hashtable [3];
312                 Hashtable methods_byaction = new Hashtable (); 
313
314                 // Precomputed
315                 internal SoapParameterStyle      ParameterStyle;
316                 internal SoapServiceRoutingStyle RoutingStyle;
317                 internal SoapBindingUse          Use;
318                 internal int                     faultSerializerId = -1;
319                 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
320                 internal SoapBindingStyle SoapBindingStyle;
321                 internal XmlReflectionImporter  xmlImporter;
322                 internal SoapReflectionImporter soapImporter;
323
324                 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
325                 : base (logicalTypeInfo)
326                 {
327                         xmlImporter = new XmlReflectionImporter ();
328                         soapImporter = new SoapReflectionImporter ();
329                         
330                         object [] o;
331
332                         o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
333                         foreach (WebServiceBindingAttribute at in o)
334                                 AddBinding (new BindingInfo (at, LogicalType.WebServiceNamespace));
335
336                         o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
337                         if (o.Length == 1){
338                                 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
339
340                                 ParameterStyle = a.ParameterStyle;
341                                 RoutingStyle = a.RoutingStyle;
342                                 Use = a.Use;
343                                 SoapBindingStyle = SoapBindingStyle.Document;
344                         } else {
345                                 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
346                                 if (o.Length == 1){
347                                         SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
348                                         
349                                         ParameterStyle = SoapParameterStyle.Wrapped;
350                                         RoutingStyle = srs.RoutingStyle;
351                                         Use = SoapBindingUse.Encoded;
352                                         SoapBindingStyle = SoapBindingStyle.Rpc;
353                                 } else {
354                                         ParameterStyle = SoapParameterStyle.Wrapped;
355                                         RoutingStyle = SoapServiceRoutingStyle.SoapAction;
356                                         Use = SoapBindingUse.Literal;
357                                         SoapBindingStyle = SoapBindingStyle.Document;
358                                 }
359                         }
360                         
361                         if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
362                         if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
363
364                         SoapExtensions = SoapExtension.GetTypeExtensions (Type);
365                 }
366
367                 public override XmlReflectionImporter XmlImporter 
368                 {
369                         get { return xmlImporter; }
370                 }
371
372                 public override SoapReflectionImporter SoapImporter 
373                 {
374                         get { return soapImporter; }
375                 }
376                 
377                 public override string ProtocolName
378                 {
379                         get { return "Soap"; }
380                 }
381                 
382                 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
383                 {
384                         SoapMethodStubInfo res = null;
385                         object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
386                         if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
387
388                         if (ats.Length == 0 && isClientProxy)
389                                 return null;
390                         else if (ats.Length == 0)
391                                 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
392                         else
393                                 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
394                                 
395                         if (faultSerializerId == -1)
396                         {
397                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
398                                 XmlTypeMapping tm = ri.ImportTypeMapping (typeof(Fault));
399                                 faultSerializerId = RegisterSerializer (tm);
400                         }
401                         methods_byaction [res.Action] = res;
402                         return res;
403                 }
404                 
405                 public XmlSerializer GetFaultSerializer ()
406                 {
407                         return GetSerializer (faultSerializerId);
408                 }
409                 
410                 internal void RegisterHeaderType (Type type, string serviceNamespace, SoapBindingUse use)
411                 {
412                         Hashtable serializers = header_serializers [(int)use];
413                         if (serializers == null) {
414                                 serializers = new Hashtable ();
415                                 header_serializers [(int)use] = serializers;
416                                 header_serializers_byname [(int)use] = new Hashtable ();
417                         }
418                         
419                         if (serializers.ContainsKey (type)) 
420                                 return;
421
422                         XmlTypeMapping tm;
423                         if (use == SoapBindingUse.Literal) {
424                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
425                                 
426                                 // MS.NET reflects header classes in a weird way. The root element
427                                 // name is the CLR class name unless it is specified in an XmlRootAttribute.
428                                 // The usual is to use the xml type name by default, but not in this case.
429                                 
430                                 XmlRootAttribute root;
431                                 XmlAttributes ats = new XmlAttributes (type);
432                                 if (ats.XmlRoot != null) root = ats.XmlRoot;
433                                 else root = new XmlRootAttribute (type.Name);
434                                 
435                                 if (root.Namespace == null) root.Namespace = LogicalType.GetWebServiceLiteralNamespace (serviceNamespace);
436                                 if (root.ElementName == null) root.ElementName = type.Name;
437                                 
438                                 tm = ri.ImportTypeMapping (type, root);
439                         }
440                         else {
441                                 SoapReflectionImporter ri = new SoapReflectionImporter ();
442                                 tm = ri.ImportTypeMapping (type, LogicalType.GetWebServiceEncodedNamespace (serviceNamespace));
443                         }
444                         
445                         int sid = RegisterSerializer (tm);
446
447                         serializers [type] = sid;
448                         header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
449                 }
450
451                 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
452                 {
453                         Hashtable table = header_serializers [(int)use];
454                         if (table == null) return null;
455                                 
456                         return GetSerializer ((int) table [type]);
457                 }
458         
459                 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
460                 {
461                         Hashtable table = header_serializers_byname [(int)use];
462                         if (table == null) return null;
463                                 
464                         return GetSerializer ((int) table [qname]);
465                 }               
466                 
467                 public SoapMethodStubInfo GetMethodForSoapAction (string name)
468                 {
469                         return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];
470                 }
471         }
472 }