2 // Methods.cs: Information about a method and its mapping to a SOAP web service.
6 // Lluis Sanchez Gual (lluis@ximian.com)
8 // (C) 2003 Ximian, Inc.
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
32 using System.Reflection;
33 using System.Collections;
35 using System.Xml.Serialization;
36 using System.Web.Services;
37 using System.Web.Services.Description;
39 namespace System.Web.Services.Protocols {
42 // This class represents all the information we extract from a MethodInfo
43 // in the SoapHttpClientProtocol derivative stub class
45 internal class SoapMethodStubInfo : MethodStubInfo
47 internal string Action;
48 internal string Binding;
50 // The name/namespace of the request
51 internal string RequestName;
52 internal string RequestNamespace;
54 // The name/namespace of the response.
55 internal string ResponseName;
56 internal string ResponseNamespace;
59 internal SoapParameterStyle ParameterStyle;
60 internal SoapBindingStyle SoapBindingStyle;
61 internal SoapBindingUse Use;
63 internal HeaderInfo[] Headers;
64 internal HeaderInfo[] InHeaders;
65 internal HeaderInfo[] OutHeaders;
66 internal HeaderInfo[] FaultHeaders;
67 internal SoapExtensionRuntimeConfig[] SoapExtensions;
69 internal XmlMembersMapping InputMembersMapping;
70 internal XmlMembersMapping OutputMembersMapping;
71 internal XmlMembersMapping InputHeaderMembersMapping;
72 internal XmlMembersMapping OutputHeaderMembersMapping;
73 internal XmlMembersMapping FaultHeaderMembersMapping;
75 private int requestSerializerId;
76 private int responseSerializerId;
77 private int requestHeadersSerializerId = -1;
78 private int responseHeadersSerializerId = -1;
79 private int faultHeadersSerializerId = -1;
81 internal XmlSerializer RequestSerializer
83 get { return TypeStub.GetSerializer (requestSerializerId); }
86 internal XmlSerializer ResponseSerializer
88 get { return TypeStub.GetSerializer (responseSerializerId); }
91 internal XmlSerializer RequestHeadersSerializer
93 get { return requestHeadersSerializerId != -1 ? TypeStub.GetSerializer (requestHeadersSerializerId) : null; }
96 internal XmlSerializer ResponseHeadersSerializer
98 get { return responseHeadersSerializerId != -1 ? TypeStub.GetSerializer (responseHeadersSerializerId) : null; }
101 internal XmlSerializer FaultHeadersSerializer
103 get { return faultHeadersSerializerId != -1 ? TypeStub.GetSerializer (faultHeadersSerializerId) : null; }
110 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
111 : base (typeStub, source)
113 SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
114 XmlElementAttribute optional_ns = null;
119 RequestNamespace = "";
121 ResponseNamespace = "";
122 ParameterStyle = parent.ParameterStyle;
123 SoapBindingStyle = parent.SoapBindingStyle;
126 else if (kind is SoapDocumentMethodAttribute){
127 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
130 if (Use == SoapBindingUse.Default) {
131 if (parent.SoapBindingStyle == SoapBindingStyle.Document)
134 Use = SoapBindingUse.Literal;
138 Binding = dma.Binding;
139 RequestName = dma.RequestElementName;
140 RequestNamespace = dma.RequestNamespace;
141 ResponseName = dma.ResponseElementName;
142 ResponseNamespace = dma.ResponseNamespace;
143 ParameterStyle = dma.ParameterStyle;
144 if (ParameterStyle == SoapParameterStyle.Default)
145 ParameterStyle = parent.ParameterStyle;
147 SoapBindingStyle = SoapBindingStyle.Document;
149 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
150 Use = SoapBindingUse.Encoded; // RPC always use encoded
153 Binding = rma.Binding;
155 // When using RPC, MS.NET seems to ignore RequestElementName and
156 // MessageName, and it always uses the method name
157 RequestName = source.Name;
158 ResponseName = source.Name + "Response";
159 // RequestName = rma.RequestElementName;
160 // ResponseName = rma.ResponseElementName;
161 RequestNamespace = rma.RequestNamespace;
162 ResponseNamespace = rma.ResponseNamespace;
163 ParameterStyle = SoapParameterStyle.Wrapped;
165 SoapBindingStyle = SoapBindingStyle.Rpc;
167 // For RPC calls, make all arguments be part of the empty namespace
168 optional_ns = new XmlElementAttribute ();
169 optional_ns.Namespace = "";
173 if (source.ReturnType != typeof (void))
174 throw new Exception ("OneWay methods should not have a return value.");
175 if (source.OutParameters.Length != 0)
176 throw new Exception ("OneWay methods should not have out/ref parameters.");
179 BindingInfo binfo = parent.GetBinding (Binding);
180 if (binfo == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'.");
182 string serviceNamespace = binfo.Namespace;
184 if (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
185 if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
186 if (RequestName == "") RequestName = Name;
187 if (ResponseName == "") ResponseName = Name + "Response";
188 if (Action == null || Action == "")
189 Action = serviceNamespace.EndsWith("/") ? (serviceNamespace + Name) : (serviceNamespace + "/" + Name);
191 bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
192 bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
194 XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
195 XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
197 if (Use == SoapBindingUse.Literal) {
198 xmlImporter.IncludeTypes (source.CustomAttributeProvider);
199 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
200 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
203 soapImporter.IncludeTypes (source.CustomAttributeProvider);
204 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, writeAccessors);
205 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, writeAccessors);
208 requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
209 responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
211 object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
212 ArrayList allHeaderList = new ArrayList (o.Length);
213 ArrayList inHeaderList = new ArrayList (o.Length);
214 ArrayList outHeaderList = new ArrayList (o.Length);
215 ArrayList faultHeaderList = new ArrayList ();
217 SoapHeaderDirection unknownHeaderDirections = (SoapHeaderDirection)0;
219 for (int i = 0; i < o.Length; i++) {
220 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
221 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
222 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName + ".");
224 HeaderInfo header = new HeaderInfo (mems[0], att);
225 allHeaderList.Add (header);
226 if (!header.IsUnknownHeader) {
227 if ((header.Direction & SoapHeaderDirection.In) != 0)
228 inHeaderList.Add (header);
229 if ((header.Direction & SoapHeaderDirection.Out) != 0)
230 outHeaderList.Add (header);
231 if ((header.Direction & SoapHeaderDirection.Fault) != 0)
232 faultHeaderList.Add (header);
234 unknownHeaderDirections |= header.Direction;
237 Headers = (HeaderInfo[]) allHeaderList.ToArray (typeof(HeaderInfo));
239 if (inHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.In) != 0) {
240 InHeaders = (HeaderInfo[]) inHeaderList.ToArray (typeof(HeaderInfo));
241 XmlReflectionMember[] members = BuildHeadersReflectionMembers (InHeaders);
243 if (Use == SoapBindingUse.Literal)
244 InputHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
246 InputHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
248 requestHeadersSerializerId = parent.RegisterSerializer (InputHeaderMembersMapping);
251 if (outHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.Out) != 0) {
252 OutHeaders = (HeaderInfo[]) outHeaderList.ToArray (typeof(HeaderInfo));
253 XmlReflectionMember[] members = BuildHeadersReflectionMembers (OutHeaders);
255 if (Use == SoapBindingUse.Literal)
256 OutputHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
258 OutputHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
260 responseHeadersSerializerId = parent.RegisterSerializer (OutputHeaderMembersMapping);
263 if (faultHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.Fault) != 0) {
264 FaultHeaders = (HeaderInfo[]) faultHeaderList.ToArray (typeof(HeaderInfo));
265 XmlReflectionMember[] members = BuildHeadersReflectionMembers (FaultHeaders);
267 if (Use == SoapBindingUse.Literal)
268 FaultHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
270 FaultHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
272 faultHeadersSerializerId = parent.RegisterSerializer (FaultHeaderMembersMapping);
275 SoapExtensions = SoapExtension.GetMethodExtensions (source);
278 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
280 ParameterInfo [] input = MethodInfo.InParameters;
281 XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
283 for (int i = 0; i < input.Length; i++)
285 XmlReflectionMember m = new XmlReflectionMember ();
286 m.IsReturnValue = false;
287 m.MemberName = input [i].Name;
288 m.MemberType = input [i].ParameterType;
290 m.XmlAttributes = new XmlAttributes (input[i]);
291 m.SoapAttributes = new SoapAttributes (input[i]);
293 if (m.MemberType.IsByRef)
294 m.MemberType = m.MemberType.GetElementType ();
295 if (optional_ns != null)
296 m.XmlAttributes.XmlElements.Add (optional_ns);
302 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
304 ParameterInfo [] output = MethodInfo.OutParameters;
305 bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
306 XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
307 XmlReflectionMember m;
310 if (has_return_value)
312 m = new XmlReflectionMember ();
313 m.IsReturnValue = true;
314 m.MemberName = RequestName + "Result";
315 m.MemberType = MethodInfo.ReturnType;
317 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
318 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
320 if (optional_ns != null)
321 m.XmlAttributes.XmlElements.Add (optional_ns);
326 for (int i = 0; i < output.Length; i++)
328 m = new XmlReflectionMember ();
329 m.IsReturnValue = false;
330 m.MemberName = output [i].Name;
331 m.MemberType = output [i].ParameterType;
332 m.XmlAttributes = new XmlAttributes (output[i]);
333 m.SoapAttributes = new SoapAttributes (output[i]);
335 if (m.MemberType.IsByRef)
336 m.MemberType = m.MemberType.GetElementType ();
337 if (optional_ns != null)
338 m.XmlAttributes.XmlElements.Add (optional_ns);
339 out_members [i + idx] = m;
344 XmlReflectionMember [] BuildHeadersReflectionMembers (HeaderInfo[] headers)
346 XmlReflectionMember [] mems = new XmlReflectionMember [headers.Length];
348 for (int n=0; n<headers.Length; n++)
350 HeaderInfo header = headers [n];
352 XmlReflectionMember m = new XmlReflectionMember ();
353 m.IsReturnValue = false;
354 m.MemberName = header.HeaderType.Name;
355 m.MemberType = header.HeaderType;
357 // MS.NET reflects header classes in a weird way. The root element
358 // name is the CLR class name unless it is specified in an XmlRootAttribute.
359 // The usual is to use the xml type name by default, but not in this case.
361 XmlAttributes ats = new XmlAttributes (header.HeaderType);
362 if (ats.XmlRoot != null) {
363 XmlElementAttribute xe = new XmlElementAttribute ();
364 xe.ElementName = ats.XmlRoot.ElementName;
365 xe.Namespace = ats.XmlRoot.Namespace;
366 m.XmlAttributes = new XmlAttributes ();
367 m.XmlAttributes.XmlElements.Add (xe);
375 public HeaderInfo GetHeaderInfo (Type headerType)
377 foreach (HeaderInfo headerInfo in Headers)
378 if (headerInfo.HeaderType == headerType) return headerInfo;
382 public XmlSerializer GetBodySerializer (SoapHeaderDirection dir)
385 case SoapHeaderDirection.In: return RequestSerializer;
386 case SoapHeaderDirection.Out: return ResponseSerializer;
387 case SoapHeaderDirection.Fault: return Fault.Serializer;
388 default: return null;
392 public XmlSerializer GetHeaderSerializer (SoapHeaderDirection dir)
395 case SoapHeaderDirection.In: return RequestHeadersSerializer;
396 case SoapHeaderDirection.Out: return ResponseHeadersSerializer;
397 case SoapHeaderDirection.Fault: return FaultHeadersSerializer;
398 default: return null;
402 HeaderInfo[] GetHeaders (SoapHeaderDirection dir)
405 case SoapHeaderDirection.In: return InHeaders;
406 case SoapHeaderDirection.Out: return OutHeaders;
407 case SoapHeaderDirection.Fault: return FaultHeaders;
408 default: return null;
412 public object[] GetHeaderValueArray (SoapHeaderDirection dir, SoapHeaderCollection headers)
414 HeaderInfo[] headerInfos = GetHeaders (dir);
415 if (headerInfos == null) return null;
417 object[] hs = new object [headerInfos.Length];
419 for (int n=0; n<headers.Count; n++) {
420 SoapHeader h = headers[n];
421 Type t = h.GetType();
422 for (int i=0; i<headerInfos.Length; i++)
423 if (headerInfos [i].HeaderType == t)
430 internal class HeaderInfo
432 internal MemberInfo Member;
433 internal SoapHeaderAttribute AttributeInfo;
434 internal Type HeaderType;
435 internal bool IsUnknownHeader;
437 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
440 AttributeInfo = attributeInfo;
441 if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
442 else HeaderType = ((FieldInfo)Member).FieldType;
444 if (HeaderType == typeof(SoapHeader) || HeaderType == typeof(SoapUnknownHeader) ||
445 HeaderType == typeof(SoapHeader[]) || HeaderType == typeof(SoapUnknownHeader[]))
447 IsUnknownHeader = true;
449 else if (!typeof(SoapHeader).IsAssignableFrom (HeaderType))
450 throw new InvalidOperationException (string.Format ("Header members type must be a SoapHeader subclass"));
453 public object GetHeaderValue (object ob)
455 if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
456 else return ((FieldInfo)Member).GetValue (ob);
459 public void SetHeaderValue (object ob, SoapHeader header)
461 object value = header;
462 if (IsUnknownHeader && HeaderType.IsArray)
464 SoapUnknownHeader uheader = header as SoapUnknownHeader;
465 SoapUnknownHeader[] array = (SoapUnknownHeader[]) GetHeaderValue (ob);
466 if (array == null || array.Length == 0) {
467 value = new SoapUnknownHeader[] { uheader };
470 SoapUnknownHeader[] newArray = new SoapUnknownHeader [array.Length+1];
471 Array.Copy (array, newArray, array.Length);
472 newArray [array.Length] = uheader;
477 if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
478 else ((FieldInfo)Member).SetValue (ob, value);
481 public SoapHeaderDirection Direction
483 get { return AttributeInfo.Direction; }
489 // Holds the metadata loaded from the type stub, as well as
490 // the metadata for all the methods in the type
492 internal class SoapTypeStubInfo : TypeStubInfo
494 Hashtable[] header_serializers = new Hashtable [3];
495 Hashtable[] header_serializers_byname = new Hashtable [3];
496 Hashtable methods_byaction = new Hashtable ();
499 internal SoapParameterStyle ParameterStyle;
500 internal SoapServiceRoutingStyle RoutingStyle;
501 internal SoapBindingUse Use;
502 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
503 internal SoapBindingStyle SoapBindingStyle;
504 internal XmlReflectionImporter xmlImporter;
505 internal SoapReflectionImporter soapImporter;
507 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
508 : base (logicalTypeInfo)
510 xmlImporter = new XmlReflectionImporter ();
511 soapImporter = new SoapReflectionImporter ();
515 o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
517 if (typeof (SoapHttpClientProtocol).IsAssignableFrom (Type))
520 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type + "'.");
522 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type + "'.");
524 // Remove the default binding, it is not needed since there is always
525 // a binding attribute.
529 foreach (WebServiceBindingAttribute at in o)
530 AddBinding (new BindingInfo (at, LogicalType.WebServiceNamespace));
532 o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
534 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
536 ParameterStyle = a.ParameterStyle;
537 RoutingStyle = a.RoutingStyle;
539 SoapBindingStyle = SoapBindingStyle.Document;
541 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
543 SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
545 ParameterStyle = SoapParameterStyle.Wrapped;
546 RoutingStyle = srs.RoutingStyle;
547 Use = SoapBindingUse.Encoded;
548 SoapBindingStyle = SoapBindingStyle.Rpc;
550 ParameterStyle = SoapParameterStyle.Wrapped;
551 RoutingStyle = SoapServiceRoutingStyle.SoapAction;
552 Use = SoapBindingUse.Literal;
553 SoapBindingStyle = SoapBindingStyle.Document;
557 if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
558 if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
560 xmlImporter.IncludeTypes (Type);
561 soapImporter.IncludeTypes (Type);
563 SoapExtensions = SoapExtension.GetTypeExtensions (Type);
566 public override XmlReflectionImporter XmlImporter
568 get { return xmlImporter; }
571 public override SoapReflectionImporter SoapImporter
573 get { return soapImporter; }
576 public override string ProtocolName
578 get { return "Soap"; }
581 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
583 SoapMethodStubInfo res = null;
584 object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
585 if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
587 if (ats.Length == 0 && isClientProxy)
589 else if (ats.Length == 0)
590 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
592 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
594 methods_byaction [res.Action] = res;
598 public SoapMethodStubInfo GetMethodForSoapAction (string name)
600 return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];