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 HeaderInfo = System.Web.Services.Protocols.SoapHeaderMapping;
34 using System.Reflection;
35 using System.Collections;
37 using System.Xml.Serialization;
38 using System.Web.Services;
39 using System.Web.Services.Description;
41 namespace System.Web.Services.Protocols {
44 // This class represents all the information we extract from a MethodInfo
45 // in the SoapHttpClientProtocol derivative stub class
47 internal class SoapMethodStubInfo : MethodStubInfo
49 internal readonly string Action;
50 internal readonly string Binding;
52 // The name/namespace of the request
53 internal readonly string RequestName;
54 internal readonly string RequestNamespace;
56 // The name/namespace of the response.
57 internal readonly string ResponseName;
58 internal readonly string ResponseNamespace;
60 internal readonly bool OneWay;
61 internal readonly SoapParameterStyle ParameterStyle;
62 internal readonly SoapBindingStyle SoapBindingStyle;
63 internal readonly SoapBindingUse Use;
65 internal readonly HeaderInfo [] Headers;
66 internal readonly HeaderInfo [] InHeaders;
67 internal readonly HeaderInfo [] OutHeaders;
68 internal readonly HeaderInfo [] FaultHeaders;
69 internal readonly SoapExtensionRuntimeConfig [] SoapExtensions;
71 internal readonly XmlMembersMapping InputMembersMapping;
72 internal readonly XmlMembersMapping OutputMembersMapping;
73 internal readonly XmlMembersMapping InputHeaderMembersMapping;
74 internal readonly XmlMembersMapping OutputHeaderMembersMapping;
75 internal readonly XmlMembersMapping FaultHeaderMembersMapping;
77 private readonly int requestSerializerId;
78 private readonly int responseSerializerId;
79 private readonly int requestHeadersSerializerId = -1;
80 private readonly int responseHeadersSerializerId = -1;
81 private readonly int faultHeadersSerializerId = -1;
83 internal XmlSerializer RequestSerializer
85 get { return TypeStub.GetSerializer (requestSerializerId); }
88 internal XmlSerializer ResponseSerializer
90 get { return TypeStub.GetSerializer (responseSerializerId); }
93 internal XmlSerializer RequestHeadersSerializer
95 get { return requestHeadersSerializerId != -1 ? TypeStub.GetSerializer (requestHeadersSerializerId) : null; }
98 internal XmlSerializer ResponseHeadersSerializer
100 get { return responseHeadersSerializerId != -1 ? TypeStub.GetSerializer (responseHeadersSerializerId) : null; }
103 internal XmlSerializer FaultHeadersSerializer
105 get { return faultHeadersSerializerId != -1 ? TypeStub.GetSerializer (faultHeadersSerializerId) : null; }
112 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
113 : base (typeStub, source)
115 SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
116 XmlElementAttribute optional_ns = null;
119 Use = parent.LogicalType.BindingUse;
121 RequestNamespace = "";
123 ResponseNamespace = "";
124 ParameterStyle = parent.ParameterStyle;
125 SoapBindingStyle = parent.SoapBindingStyle;
127 // disabled (see bug #332150)
129 // if (parent.Type != source.DeclaringType)
130 // Binding = source.DeclaringType.Name + parent.ProtocolName;
133 else if (kind is SoapDocumentMethodAttribute){
134 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
137 if (Use == SoapBindingUse.Default) {
138 if (parent.SoapBindingStyle == SoapBindingStyle.Document)
139 Use = parent.LogicalType.BindingUse;
141 Use = SoapBindingUse.Literal;
145 Binding = dma.Binding;
146 RequestName = dma.RequestElementName;
147 RequestNamespace = dma.RequestNamespace;
148 ResponseName = dma.ResponseElementName;
149 ResponseNamespace = dma.ResponseNamespace;
150 ParameterStyle = dma.ParameterStyle;
151 if (ParameterStyle == SoapParameterStyle.Default)
152 ParameterStyle = parent.ParameterStyle;
154 SoapBindingStyle = SoapBindingStyle.Document;
156 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
157 Use = SoapBindingUse.Encoded; // RPC always use encoded
160 if (Action != null && Action.Length == 0)
162 Binding = rma.Binding;
164 // When using RPC, MS.NET seems to ignore RequestElementName and
165 // MessageName, and it always uses the method name
166 RequestName = source.Name;
167 ResponseName = source.Name + "Response";
168 // RequestName = rma.RequestElementName;
169 // ResponseName = rma.ResponseElementName;
170 RequestNamespace = rma.RequestNamespace;
171 ResponseNamespace = rma.ResponseNamespace;
172 ParameterStyle = SoapParameterStyle.Wrapped;
174 SoapBindingStyle = SoapBindingStyle.Rpc;
176 // For RPC calls, make all arguments be part of the empty namespace
177 optional_ns = new XmlElementAttribute ();
178 optional_ns.Namespace = "";
182 if (source.ReturnType != typeof (void))
183 throw new Exception ("OneWay methods should not have a return value.");
184 if (source.OutParameters.Length != 0)
185 throw new Exception ("OneWay methods should not have out/ref parameters.");
188 BindingInfo binfo = parent.GetBinding (Binding);
189 if (binfo == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'.");
191 string serviceNamespace = binfo.Namespace;
193 if (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
194 if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
195 if (RequestName == "") RequestName = Name;
196 if (ResponseName == "") ResponseName = Name + "Response";
198 Action = serviceNamespace.EndsWith("/") ? (serviceNamespace + Name) : (serviceNamespace + "/" + Name);
200 bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
201 bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
203 XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
204 XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
206 if (Use == SoapBindingUse.Literal) {
207 xmlImporter.IncludeTypes (source.CustomAttributeProvider);
208 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
209 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
212 soapImporter.IncludeTypes (source.CustomAttributeProvider);
213 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, writeAccessors);
214 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, writeAccessors);
217 InputMembersMapping.SetKey(RequestName);
218 OutputMembersMapping.SetKey(ResponseName);
220 requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
221 responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
223 object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
224 ArrayList allHeaderList = new ArrayList (o.Length);
225 ArrayList inHeaderList = new ArrayList (o.Length);
226 ArrayList outHeaderList = new ArrayList (o.Length);
227 ArrayList faultHeaderList = new ArrayList ();
229 SoapHeaderDirection unknownHeaderDirections = (SoapHeaderDirection)0;
231 for (int i = 0; i < o.Length; i++) {
232 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
233 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
234 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName + ".");
236 HeaderInfo header = new HeaderInfo (mems[0], att);
237 allHeaderList.Add (header);
238 if (!header.Custom) {
239 if ((header.Direction & SoapHeaderDirection.In) != 0)
240 inHeaderList.Add (header);
241 if ((header.Direction & SoapHeaderDirection.Out) != 0)
242 outHeaderList.Add (header);
243 if ((header.Direction & SoapHeaderDirection.Fault) != 0)
244 faultHeaderList.Add (header);
246 unknownHeaderDirections |= header.Direction;
249 Headers = (HeaderInfo[]) allHeaderList.ToArray (typeof(HeaderInfo));
251 if (inHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.In) != 0) {
252 InHeaders = (HeaderInfo[]) inHeaderList.ToArray (typeof(HeaderInfo));
253 XmlReflectionMember[] members = BuildHeadersReflectionMembers (InHeaders);
255 if (Use == SoapBindingUse.Literal)
256 InputHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
258 InputHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
260 InputHeaderMembersMapping.SetKey(RequestName + ":InHeaders");
262 requestHeadersSerializerId = parent.RegisterSerializer (InputHeaderMembersMapping);
265 if (outHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.Out) != 0) {
266 OutHeaders = (HeaderInfo[]) outHeaderList.ToArray (typeof(HeaderInfo));
267 XmlReflectionMember[] members = BuildHeadersReflectionMembers (OutHeaders);
269 if (Use == SoapBindingUse.Literal)
270 OutputHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
272 OutputHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
274 OutputHeaderMembersMapping.SetKey(ResponseName + ":OutHeaders");
276 responseHeadersSerializerId = parent.RegisterSerializer (OutputHeaderMembersMapping);
279 if (faultHeaderList.Count > 0 || (unknownHeaderDirections & SoapHeaderDirection.Fault) != 0) {
280 FaultHeaders = (HeaderInfo[]) faultHeaderList.ToArray (typeof(HeaderInfo));
281 XmlReflectionMember[] members = BuildHeadersReflectionMembers (FaultHeaders);
283 if (Use == SoapBindingUse.Literal)
284 FaultHeaderMembersMapping = xmlImporter.ImportMembersMapping ("", RequestNamespace, members, false);
286 FaultHeaderMembersMapping = soapImporter.ImportMembersMapping ("", RequestNamespace, members, false, false);
288 faultHeadersSerializerId = parent.RegisterSerializer (FaultHeaderMembersMapping);
291 SoapExtensions = SoapExtension.GetMethodExtensions (source);
294 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
296 ParameterInfo [] input = MethodInfo.InParameters;
297 XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
299 for (int i = 0; i < input.Length; i++)
301 XmlReflectionMember m = new XmlReflectionMember ();
302 m.IsReturnValue = false;
303 m.MemberName = input [i].Name;
304 m.MemberType = input [i].ParameterType;
306 m.XmlAttributes = new XmlAttributes (input[i]);
307 m.SoapAttributes = new SoapAttributes (input[i]);
309 if (m.MemberType.IsByRef)
310 m.MemberType = m.MemberType.GetElementType ();
311 if (optional_ns != null)
312 m.XmlAttributes.XmlElements.Add (optional_ns);
318 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
320 ParameterInfo [] output = MethodInfo.OutParameters;
321 bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
322 XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
323 XmlReflectionMember m;
326 if (has_return_value)
328 m = new XmlReflectionMember ();
329 m.IsReturnValue = true;
330 m.MemberName = Name + "Result";
331 m.MemberType = MethodInfo.ReturnType;
333 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
334 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
336 if (optional_ns != null)
337 m.XmlAttributes.XmlElements.Add (optional_ns);
342 for (int i = 0; i < output.Length; i++)
344 m = new XmlReflectionMember ();
345 m.IsReturnValue = false;
346 m.MemberName = output [i].Name;
347 m.MemberType = output [i].ParameterType;
348 m.XmlAttributes = new XmlAttributes (output[i]);
349 m.SoapAttributes = new SoapAttributes (output[i]);
351 if (m.MemberType.IsByRef)
352 m.MemberType = m.MemberType.GetElementType ();
353 if (optional_ns != null)
354 m.XmlAttributes.XmlElements.Add (optional_ns);
355 out_members [i + idx] = m;
360 XmlReflectionMember [] BuildHeadersReflectionMembers (HeaderInfo[] headers)
362 XmlReflectionMember [] mems = new XmlReflectionMember [headers.Length];
364 for (int n=0; n<headers.Length; n++)
366 HeaderInfo header = headers [n];
368 XmlReflectionMember m = new XmlReflectionMember ();
369 m.IsReturnValue = false;
370 m.MemberName = header.HeaderType.Name;
371 m.MemberType = header.HeaderType;
373 // MS.NET reflects header classes in a weird way. The root element
374 // name is the CLR class name unless it is specified in an XmlRootAttribute.
375 // The usual is to use the xml type name by default, but not in this case.
377 XmlAttributes ats = new XmlAttributes (header.HeaderType);
378 if (ats.XmlRoot != null) {
379 XmlElementAttribute xe = new XmlElementAttribute ();
380 xe.ElementName = ats.XmlRoot.ElementName;
381 xe.Namespace = ats.XmlRoot.Namespace;
382 m.XmlAttributes = new XmlAttributes ();
383 m.XmlAttributes.XmlElements.Add (xe);
391 public HeaderInfo GetHeaderInfo (Type headerType)
393 foreach (HeaderInfo headerInfo in Headers)
394 if (headerInfo.HeaderType == headerType) return headerInfo;
398 public XmlSerializer GetBodySerializer (SoapHeaderDirection dir, bool soap12)
401 case SoapHeaderDirection.In: return RequestSerializer;
402 case SoapHeaderDirection.Out: return ResponseSerializer;
403 case SoapHeaderDirection.Fault: return soap12 ? Soap12Fault.Serializer : Fault.Serializer;
404 default: return null;
408 public XmlSerializer GetHeaderSerializer (SoapHeaderDirection dir)
411 case SoapHeaderDirection.In: return RequestHeadersSerializer;
412 case SoapHeaderDirection.Out: return ResponseHeadersSerializer;
413 case SoapHeaderDirection.Fault: return FaultHeadersSerializer;
414 default: return null;
418 HeaderInfo[] GetHeaders (SoapHeaderDirection dir)
421 case SoapHeaderDirection.In: return InHeaders;
422 case SoapHeaderDirection.Out: return OutHeaders;
423 case SoapHeaderDirection.Fault: return FaultHeaders;
424 default: return null;
428 public object[] GetHeaderValueArray (SoapHeaderDirection dir, SoapHeaderCollection headers)
430 HeaderInfo[] headerInfos = GetHeaders (dir);
431 if (headerInfos == null) return null;
433 object[] hs = new object [headerInfos.Length];
435 for (int n=0; n<headers.Count; n++) {
436 SoapHeader h = headers[n];
437 Type t = h.GetType();
438 for (int i=0; i<headerInfos.Length; i++)
439 if (headerInfos [i].HeaderType == t)
447 // Holds the metadata loaded from the type stub, as well as
448 // the metadata for all the methods in the type
450 internal class SoapTypeStubInfo : TypeStubInfo
452 Hashtable methods_byaction = new Hashtable ();
455 internal SoapParameterStyle ParameterStyle;
456 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
457 internal SoapBindingStyle SoapBindingStyle;
458 internal XmlReflectionImporter xmlImporter;
459 internal SoapReflectionImporter soapImporter;
461 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
462 : base (logicalTypeInfo)
464 xmlImporter = new XmlReflectionImporter ();
465 soapImporter = new SoapReflectionImporter ();
467 if (typeof (SoapHttpClientProtocol).IsAssignableFrom (Type))
469 if (Bindings.Count == 0 || ((BindingInfo)Bindings[0]).WebServiceBindingAttribute == null)
470 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type + "'.");
471 if (Bindings.Count > 1)
472 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type + "'.");
475 object [] o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
477 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
479 ParameterStyle = a.ParameterStyle;
480 SoapBindingStyle = SoapBindingStyle.Document;
482 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
484 ParameterStyle = SoapParameterStyle.Wrapped;
485 SoapBindingStyle = SoapBindingStyle.Rpc;
487 ParameterStyle = SoapParameterStyle.Wrapped;
488 SoapBindingStyle = SoapBindingStyle.Document;
492 if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
494 xmlImporter.IncludeTypes (Type);
495 soapImporter.IncludeTypes (Type);
497 #if MOBILE || XAMMAC_4_5
498 SoapExtensions = new SoapExtensionRuntimeConfig [2][];
500 SoapExtensions = SoapExtension.GetTypeExtensions (Type);
504 internal SoapServiceRoutingStyle RoutingStyle {
505 get { return LogicalType.RoutingStyle; }
508 public override XmlReflectionImporter XmlImporter
510 get { return xmlImporter; }
513 public override SoapReflectionImporter SoapImporter
515 get { return soapImporter; }
518 public override string ProtocolName
520 get { return "Soap"; }
523 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
525 SoapMethodStubInfo res = null;
526 object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
527 if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
529 if (ats.Length == 0 && isClientProxy)
531 else if (ats.Length == 0)
532 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
534 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
536 methods_byaction [res.Action] = res;
540 public SoapMethodStubInfo GetMethodForSoapAction (string name)
542 return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];
546 internal class Soap12TypeStubInfo : SoapTypeStubInfo
548 public Soap12TypeStubInfo (LogicalTypeInfo logicalTypeInfo)
549 : base (logicalTypeInfo)
553 public override string ProtocolName
555 get { return "Soap12"; }