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