* Methods.cs: Added special handling for RPC format.
[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 (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (Use);
129                         if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (Use);
130                         if (RequestName == "") RequestName = Name;
131                         if (ResponseName == "") ResponseName = Name + "Response";
132                         if (Binding == null || Binding == "") Binding = parent.DefaultBinding;
133                         else if (parent.GetBinding (Binding) == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'");
134                                 
135                         if (Action == null || Action == "")
136                                 Action = RequestNamespace.EndsWith("/") ? (RequestNamespace + Name) : (RequestNamespace + "/" + Name);
137                         
138                         bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
139                         bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
140                         
141                         XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
142                         XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
143
144                         if (Use == SoapBindingUse.Literal) {
145                                 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
146                                 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
147                         }
148                         else {
149                                 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, writeAccessors);
150                                 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, writeAccessors);
151                         }
152
153                         requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
154                         responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
155
156                         object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
157                         Headers = new HeaderInfo[o.Length];
158                         for (int i = 0; i < o.Length; i++) {
159                                 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
160                                 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
161                                 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName);
162                                 
163                                 Type headerType = (mems[0] is FieldInfo) ? ((FieldInfo)mems[0]).FieldType : ((PropertyInfo)mems[0]).PropertyType;
164                                 Headers [i] = new HeaderInfo (mems[0], att);
165                                 parent.RegisterHeaderType (headerType, Use);
166                         }
167
168                         SoapExtensions = SoapExtension.GetMethodExtensions (source);
169                 }
170
171                 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
172                 {
173                         ParameterInfo [] input = MethodInfo.InParameters;
174                         XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
175
176                         for (int i = 0; i < input.Length; i++)
177                         {
178                                 XmlReflectionMember m = new XmlReflectionMember ();
179                                 m.IsReturnValue = false;
180                                 m.MemberName = input [i].Name;
181                                 m.MemberType = input [i].ParameterType;
182
183                                 m.XmlAttributes = new XmlAttributes (input[i]);
184                                 m.SoapAttributes = new SoapAttributes (input[i]);
185
186                                 if (m.MemberType.IsByRef)
187                                         m.MemberType = m.MemberType.GetElementType ();
188                                 if (optional_ns != null)
189                                         m.XmlAttributes.XmlElements.Add (optional_ns);
190                                 in_members [i] = m;
191                         }
192                         return in_members;
193                 }
194                 
195                 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
196                 {
197                         ParameterInfo [] output = MethodInfo.OutParameters;
198                         bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
199                         XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
200                         XmlReflectionMember m;
201                         int idx = 0;
202
203                         if (has_return_value)
204                         {
205                                 m = new XmlReflectionMember ();
206                                 m.IsReturnValue = true;
207                                 m.MemberName = RequestName + "Result";
208                                 m.MemberType = MethodInfo.ReturnType;
209
210                                 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
211                                 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
212
213                                 if (optional_ns != null)
214                                         m.XmlAttributes.XmlElements.Add (optional_ns);
215                                 idx++;
216                                 out_members [0] = m;
217                         }
218                         
219                         for (int i = 0; i < output.Length; i++)
220                         {
221                                 m = new XmlReflectionMember ();
222                                 m.IsReturnValue = false;
223                                 m.MemberName = output [i].Name;
224                                 m.MemberType = output [i].ParameterType;
225                                 m.XmlAttributes = new XmlAttributes (output[i]);
226                                 m.SoapAttributes = new SoapAttributes (output[i]);
227
228                                 if (m.MemberType.IsByRef)
229                                         m.MemberType = m.MemberType.GetElementType ();
230                                 if (optional_ns != null)
231                                         m.XmlAttributes.XmlElements.Add (optional_ns);
232                                 out_members [i + idx] = m;
233                         }
234                         return out_members;
235                 }
236
237                 public HeaderInfo GetHeaderInfo (Type headerType)
238                 {
239                         foreach (HeaderInfo headerInfo in Headers)
240                                 if (headerInfo.HeaderType == headerType) return headerInfo;
241                         return null;
242                 }
243         }
244
245         internal class HeaderInfo
246         {
247                 internal MemberInfo Member;
248                 internal SoapHeaderAttribute AttributeInfo;
249                 internal Type HeaderType;
250
251                 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
252                 {
253                         Member = member;
254                         AttributeInfo = attributeInfo;
255                         if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
256                         else HeaderType = ((FieldInfo)Member).FieldType;
257                 }
258                 
259                 public object GetHeaderValue (object ob)
260                 {
261                         if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
262                         else return ((FieldInfo)Member).GetValue (ob);
263                 }
264
265                 public void SetHeaderValue (object ob, object value)
266                 {
267                         if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
268                         else ((FieldInfo)Member).SetValue (ob, value);
269                 }
270
271                 public SoapHeaderDirection Direction
272                 {
273                         get { return AttributeInfo.Direction; }
274                 }
275         }
276
277         // FIXME: this class should be internal, but it needs to be public in
278         // order to be serialized using XmlSerializer.
279         [SoapType (Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
280         [XmlType (Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
281         public class Fault
282         {
283                 public Fault () {}
284
285                 public Fault (SoapException ex) 
286                 {
287                         faultcode = ex.Code;
288                         faultstring = ex.Message;
289                         faultactor = ex.Actor;
290                         detail = ex.Detail;
291                 }
292
293                 public XmlQualifiedName faultcode;
294                 public string faultstring;
295                 public string faultactor;
296                 
297                 [SoapIgnore]
298                 public XmlNode detail;
299         }
300         
301         //
302         // Holds the metadata loaded from the type stub, as well as
303         // the metadata for all the methods in the type
304         //
305         internal class SoapTypeStubInfo : TypeStubInfo
306         {
307                 Hashtable[] header_serializers = new Hashtable [3];
308                 Hashtable[] header_serializers_byname = new Hashtable [3];
309
310                 // Precomputed
311                 internal SoapParameterStyle      ParameterStyle;
312                 internal SoapServiceRoutingStyle RoutingStyle;
313                 internal SoapBindingUse          Use;
314                 internal int                     faultSerializerLitId = -1;
315                 internal int                     faultSerializerEncId = -1;
316                 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
317                 internal SoapBindingStyle SoapBindingStyle;
318                 internal XmlReflectionImporter  xmlImporter;
319                 internal SoapReflectionImporter soapImporter;
320
321                 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
322                 : base (logicalTypeInfo)
323                 {
324                         xmlImporter = new XmlReflectionImporter ();
325                         soapImporter = new SoapReflectionImporter ();
326                         
327                         object [] o;
328
329                         o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
330                         foreach (WebServiceBindingAttribute at in o)
331                                 Bindings.Add (new BindingInfo (at, LogicalType.WebServiceNamespace));
332
333                         o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
334                         if (o.Length == 1){
335                                 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
336
337                                 ParameterStyle = a.ParameterStyle;
338                                 RoutingStyle = a.RoutingStyle;
339                                 Use = a.Use;
340                                 SoapBindingStyle = SoapBindingStyle.Document;
341                         } else {
342                                 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
343                                 if (o.Length == 1){
344                                         SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
345                                         
346                                         ParameterStyle = SoapParameterStyle.Wrapped;
347                                         RoutingStyle = srs.RoutingStyle;
348                                         Use = SoapBindingUse.Encoded;
349                                         SoapBindingStyle = SoapBindingStyle.Rpc;
350                                 } else {
351                                         ParameterStyle = SoapParameterStyle.Wrapped;
352                                         RoutingStyle = SoapServiceRoutingStyle.SoapAction;
353                                         Use = SoapBindingUse.Literal;
354                                         SoapBindingStyle = SoapBindingStyle.Document;
355                                 }
356                         }
357                         
358                         if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
359                         if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
360
361                         SoapExtensions = SoapExtension.GetTypeExtensions (Type);
362                 }
363
364                 public override XmlReflectionImporter XmlImporter 
365                 {
366                         get { return xmlImporter; }
367                 }
368
369                 public override SoapReflectionImporter SoapImporter 
370                 {
371                         get { return soapImporter; }
372                 }
373                 
374                 public override string ProtocolName
375                 {
376                         get { return "Soap"; }
377                 }
378                 
379                 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
380                 {
381                         SoapMethodStubInfo res = null;
382                         object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
383                         if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
384
385                         if (ats.Length == 0 && isClientProxy)
386                                 return null;
387                         else if (ats.Length == 0)
388                                 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
389                         else
390                                 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
391                                 
392                         if (faultSerializerEncId == -1 && res.Use == SoapBindingUse.Encoded)
393                         {
394                                 SoapReflectionImporter ri = new SoapReflectionImporter ();
395                                 XmlTypeMapping tm = ri.ImportTypeMapping (typeof(Fault));
396                                 faultSerializerEncId = RegisterSerializer (tm);
397                         }
398                         else if (faultSerializerLitId == -1 && res.Use == SoapBindingUse.Literal)
399                         {
400                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
401                                 XmlTypeMapping tm = ri.ImportTypeMapping (typeof(Fault));
402                                 faultSerializerLitId = RegisterSerializer (tm);
403                         }
404                         return res;
405                 }
406                 
407                 public XmlSerializer GetFaultSerializer (SoapBindingUse use)
408                 {
409                         if (use == SoapBindingUse.Literal)
410                                 return GetSerializer (faultSerializerLitId);
411                         else
412                                 return GetSerializer (faultSerializerEncId);
413                 }
414                 
415                 internal void RegisterHeaderType (Type type, SoapBindingUse use)
416                 {
417                         Hashtable serializers = header_serializers [(int)use];
418                         if (serializers == null) {
419                                 serializers = new Hashtable ();
420                                 header_serializers [(int)use] = serializers;
421                                 header_serializers_byname [(int)use] = new Hashtable ();
422                         }
423                         
424                         if (serializers.ContainsKey (type)) 
425                                 return;
426
427                         XmlTypeMapping tm;
428                         if (use == SoapBindingUse.Literal) {
429                                 XmlReflectionImporter ri = new XmlReflectionImporter ();
430                                 tm = ri.ImportTypeMapping (type, WebServiceAttribute.DefaultNamespace);
431                         }
432                         else {
433                                 SoapReflectionImporter ri = new SoapReflectionImporter ();
434                                 tm = ri.ImportTypeMapping (type, WebServiceAttribute.DefaultNamespace);
435                         }
436                         
437                         int sid = RegisterSerializer (tm);
438
439                         serializers [type] = sid;
440                         header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
441                 }
442
443                 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
444                 {
445                         Hashtable table = header_serializers [(int)use];
446                         if (table == null) return null;
447                                 
448                         return GetSerializer ((int) table [type]);
449                 }
450         
451                 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
452                 {
453                         Hashtable table = header_serializers_byname [(int)use];
454                         if (table == null) return null;
455                                 
456                         return GetSerializer ((int) table [qname]);
457                 }               
458         }
459 }