* Methods.cs: Set the correct namespaces for Fault. This fixes bug #53117.
[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         [XmlRoot (Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
283         public class Fault
284         {
285                 public Fault () {}
286
287                 public Fault (SoapException ex) 
288                 {
289                         faultcode = ex.Code;
290                         faultstring = ex.Message;
291                         faultactor = ex.Actor;
292                         detail = ex.Detail;
293                 }
294
295                 [XmlElement (Namespace="")]
296                 public XmlQualifiedName faultcode;
297                 
298                 [XmlElement (Namespace="")]
299                 public string faultstring;
300                 
301                 [XmlElement (Namespace="")]
302                 public string faultactor;
303                 
304                 [SoapIgnore]
305                 public XmlNode detail;
306         }
307         
308         //
309         // Holds the metadata loaded from the type stub, as well as
310         // the metadata for all the methods in the type
311         //
312         internal class SoapTypeStubInfo : TypeStubInfo
313         {
314                 Hashtable[] header_serializers = new Hashtable [3];
315                 Hashtable[] header_serializers_byname = new Hashtable [3];
316                 Hashtable methods_byaction = new Hashtable (); 
317
318                 // Precomputed
319                 internal SoapParameterStyle      ParameterStyle;
320                 internal SoapServiceRoutingStyle RoutingStyle;
321                 internal SoapBindingUse          Use;
322                 internal int                     faultSerializerId = -1;
323                 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
324                 internal SoapBindingStyle SoapBindingStyle;
325                 internal XmlReflectionImporter  xmlImporter;
326                 internal SoapReflectionImporter soapImporter;
327
328                 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
329                 : base (logicalTypeInfo)
330                 {
331                         xmlImporter = new XmlReflectionImporter ();
332                         soapImporter = new SoapReflectionImporter ();
333                         
334                         object [] o;
335
336                         o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
337                         foreach (WebServiceBindingAttribute at in o)
338                                 AddBinding (new BindingInfo (at, LogicalType.WebServiceNamespace));
339
340                         o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
341                         if (o.Length == 1){
342                                 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
343
344                                 ParameterStyle = a.ParameterStyle;
345                                 RoutingStyle = a.RoutingStyle;
346                                 Use = a.Use;
347                                 SoapBindingStyle = SoapBindingStyle.Document;
348                         } else {
349                                 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
350                                 if (o.Length == 1){
351                                         SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
352                                         
353                                         ParameterStyle = SoapParameterStyle.Wrapped;
354                                         RoutingStyle = srs.RoutingStyle;
355                                         Use = SoapBindingUse.Encoded;
356                                         SoapBindingStyle = SoapBindingStyle.Rpc;
357                                 } else {
358                                         ParameterStyle = SoapParameterStyle.Wrapped;
359                                         RoutingStyle = SoapServiceRoutingStyle.SoapAction;
360                                         Use = SoapBindingUse.Literal;
361                                         SoapBindingStyle = SoapBindingStyle.Document;
362                                 }
363                         }
364                         
365                         if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
366                         if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
367
368                         SoapExtensions = SoapExtension.GetTypeExtensions (Type);
369                 }
370
371                 public override XmlReflectionImporter XmlImporter 
372                 {
373                         get { return xmlImporter; }
374                 }
375
376                 public override SoapReflectionImporter SoapImporter 
377                 {
378                         get { return soapImporter; }
379                 }
380                 
381                 public override string ProtocolName
382                 {
383                         get { return "Soap"; }
384                 }
385                 
386                 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
387                 {
388                         SoapMethodStubInfo res = null;
389                         object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
390                         if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
391
392                         if (ats.Length == 0 && isClientProxy)
393                                 return null;
394                         else if (ats.Length == 0)
395                                 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
396                         else
397                                 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
398                                 
399                         if (faultSerializerId == -1)
400                         {
401                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
402                                 XmlTypeMapping tm = ri.ImportTypeMapping (typeof(Fault));
403                                 faultSerializerId = RegisterSerializer (tm);
404                         }
405                         methods_byaction [res.Action] = res;
406                         return res;
407                 }
408                 
409                 public XmlSerializer GetFaultSerializer ()
410                 {
411                         return GetSerializer (faultSerializerId);
412                 }
413                 
414                 internal void RegisterHeaderType (Type type, string serviceNamespace, SoapBindingUse use)
415                 {
416                         Hashtable serializers = header_serializers [(int)use];
417                         if (serializers == null) {
418                                 serializers = new Hashtable ();
419                                 header_serializers [(int)use] = serializers;
420                                 header_serializers_byname [(int)use] = new Hashtable ();
421                         }
422                         
423                         if (serializers.ContainsKey (type)) 
424                                 return;
425
426                         XmlTypeMapping tm;
427                         if (use == SoapBindingUse.Literal) {
428                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
429                                 
430                                 // MS.NET reflects header classes in a weird way. The root element
431                                 // name is the CLR class name unless it is specified in an XmlRootAttribute.
432                                 // The usual is to use the xml type name by default, but not in this case.
433                                 
434                                 XmlRootAttribute root;
435                                 XmlAttributes ats = new XmlAttributes (type);
436                                 if (ats.XmlRoot != null) root = ats.XmlRoot;
437                                 else root = new XmlRootAttribute (type.Name);
438                                 
439                                 if (root.Namespace == null) root.Namespace = LogicalType.GetWebServiceLiteralNamespace (serviceNamespace);
440                                 if (root.ElementName == null) root.ElementName = type.Name;
441                                 
442                                 tm = ri.ImportTypeMapping (type, root);
443                         }
444                         else {
445                                 SoapReflectionImporter ri = new SoapReflectionImporter ();
446                                 tm = ri.ImportTypeMapping (type, LogicalType.GetWebServiceEncodedNamespace (serviceNamespace));
447                         }
448                         
449                         int sid = RegisterSerializer (tm);
450
451                         serializers [type] = sid;
452                         header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
453                 }
454
455                 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
456                 {
457                         Hashtable table = header_serializers [(int)use];
458                         if (table == null) return null;
459                                 
460                         return GetSerializer ((int) table [type]);
461                 }
462         
463                 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
464                 {
465                         Hashtable table = header_serializers_byname [(int)use];
466                         if (table == null) return null;
467                                 
468                         return GetSerializer ((int) table [qname]);
469                 }               
470                 
471                 public SoapMethodStubInfo GetMethodForSoapAction (string name)
472                 {
473                         return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];
474                 }
475         }
476 }