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 SoapExtensionRuntimeConfig[] SoapExtensions;
66 internal XmlMembersMapping InputMembersMapping;
67 internal XmlMembersMapping OutputMembersMapping;
69 private int requestSerializerId;
70 private int responseSerializerId;
72 internal XmlSerializer RequestSerializer
74 get { return TypeStub.GetSerializer (requestSerializerId); }
77 internal XmlSerializer ResponseSerializer
79 get { return TypeStub.GetSerializer (responseSerializerId); }
85 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
86 : base (typeStub, source)
88 SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
89 XmlElementAttribute optional_ns = null;
94 RequestNamespace = "";
96 ResponseNamespace = "";
97 ParameterStyle = parent.ParameterStyle;
98 SoapBindingStyle = parent.SoapBindingStyle;
101 else if (kind is SoapDocumentMethodAttribute){
102 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
105 if (Use == SoapBindingUse.Default) {
106 if (parent.SoapBindingStyle == SoapBindingStyle.Document)
109 Use = SoapBindingUse.Literal;
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;
122 SoapBindingStyle = SoapBindingStyle.Document;
124 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
125 Use = SoapBindingUse.Encoded; // RPC always use encoded
128 Binding = rma.Binding;
129 RequestName = rma.RequestElementName;
130 RequestNamespace = rma.RequestNamespace;
131 ResponseNamespace = rma.ResponseNamespace;
132 ResponseName = rma.ResponseElementName;
133 ParameterStyle = SoapParameterStyle.Wrapped;
135 SoapBindingStyle = SoapBindingStyle.Rpc;
137 // For RPC calls, make all arguments be part of the empty namespace
138 optional_ns = new XmlElementAttribute ();
139 optional_ns.Namespace = "";
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.");
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 + "'.");
152 string serviceNamespace = binfo.Namespace;
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);
161 bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
162 bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
164 XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
165 XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
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);
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);
178 requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
179 responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
181 object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
182 ArrayList headerList = new ArrayList (o.Length);
184 for (int i = 0; i < o.Length; i++) {
185 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
186 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
187 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName + ".");
189 HeaderInfo header = new HeaderInfo (mems[0], att);
190 headerList.Add (header);
192 if (!header.IsUnknownHeader)
193 parent.RegisterHeaderType (header.HeaderType, serviceNamespace, Use);
195 Headers = (HeaderInfo[]) headerList.ToArray (typeof(HeaderInfo));
197 SoapExtensions = SoapExtension.GetMethodExtensions (source);
200 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
202 ParameterInfo [] input = MethodInfo.InParameters;
203 XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
205 for (int i = 0; i < input.Length; i++)
207 XmlReflectionMember m = new XmlReflectionMember ();
208 m.IsReturnValue = false;
209 m.MemberName = input [i].Name;
210 m.MemberType = input [i].ParameterType;
212 m.XmlAttributes = new XmlAttributes (input[i]);
213 m.SoapAttributes = new SoapAttributes (input[i]);
215 if (m.MemberType.IsByRef)
216 m.MemberType = m.MemberType.GetElementType ();
217 if (optional_ns != null)
218 m.XmlAttributes.XmlElements.Add (optional_ns);
224 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
226 ParameterInfo [] output = MethodInfo.OutParameters;
227 bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
228 XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
229 XmlReflectionMember m;
232 if (has_return_value)
234 m = new XmlReflectionMember ();
235 m.IsReturnValue = true;
236 m.MemberName = RequestName + "Result";
237 m.MemberType = MethodInfo.ReturnType;
239 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
240 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
242 if (optional_ns != null)
243 m.XmlAttributes.XmlElements.Add (optional_ns);
248 for (int i = 0; i < output.Length; i++)
250 m = new XmlReflectionMember ();
251 m.IsReturnValue = false;
252 m.MemberName = output [i].Name;
253 m.MemberType = output [i].ParameterType;
254 m.XmlAttributes = new XmlAttributes (output[i]);
255 m.SoapAttributes = new SoapAttributes (output[i]);
257 if (m.MemberType.IsByRef)
258 m.MemberType = m.MemberType.GetElementType ();
259 if (optional_ns != null)
260 m.XmlAttributes.XmlElements.Add (optional_ns);
261 out_members [i + idx] = m;
266 public HeaderInfo GetHeaderInfo (Type headerType)
268 foreach (HeaderInfo headerInfo in Headers)
269 if (headerInfo.HeaderType == headerType) return headerInfo;
274 internal class HeaderInfo
276 internal MemberInfo Member;
277 internal SoapHeaderAttribute AttributeInfo;
278 internal Type HeaderType;
279 internal bool IsUnknownHeader;
281 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
284 AttributeInfo = attributeInfo;
285 if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
286 else HeaderType = ((FieldInfo)Member).FieldType;
288 if (HeaderType == typeof(SoapHeader) || HeaderType == typeof(SoapUnknownHeader) ||
289 HeaderType == typeof(SoapHeader[]) || HeaderType == typeof(SoapUnknownHeader[]))
291 IsUnknownHeader = true;
293 else if (!typeof(SoapHeader).IsAssignableFrom (HeaderType))
294 throw new InvalidOperationException (string.Format ("Header members type must be a SoapHeader subclass"));
297 public object GetHeaderValue (object ob)
299 if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
300 else return ((FieldInfo)Member).GetValue (ob);
303 public void SetHeaderValue (object ob, SoapHeader header)
305 object value = header;
306 if (IsUnknownHeader && HeaderType.IsArray)
308 SoapUnknownHeader uheader = header as SoapUnknownHeader;
309 SoapUnknownHeader[] array = (SoapUnknownHeader[]) GetHeaderValue (ob);
310 if (array == null || array.Length == 0) {
311 value = new SoapUnknownHeader[] { uheader };
314 SoapUnknownHeader[] newArray = new SoapUnknownHeader [array.Length+1];
315 Array.Copy (array, newArray, array.Length);
316 newArray [array.Length] = uheader;
321 if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
322 else ((FieldInfo)Member).SetValue (ob, value);
325 public SoapHeaderDirection Direction
327 get { return AttributeInfo.Direction; }
333 // Holds the metadata loaded from the type stub, as well as
334 // the metadata for all the methods in the type
336 internal class SoapTypeStubInfo : TypeStubInfo
338 Hashtable[] header_serializers = new Hashtable [3];
339 Hashtable[] header_serializers_byname = new Hashtable [3];
340 Hashtable methods_byaction = new Hashtable ();
343 internal SoapParameterStyle ParameterStyle;
344 internal SoapServiceRoutingStyle RoutingStyle;
345 internal SoapBindingUse Use;
346 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
347 internal SoapBindingStyle SoapBindingStyle;
348 internal XmlReflectionImporter xmlImporter;
349 internal SoapReflectionImporter soapImporter;
351 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
352 : base (logicalTypeInfo)
354 xmlImporter = new XmlReflectionImporter ();
355 soapImporter = new SoapReflectionImporter ();
359 o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
361 if (typeof (SoapHttpClientProtocol).IsAssignableFrom (Type))
364 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type + "'.");
366 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type + "'.");
368 // Remove the default binding, it is not needed since there is always
369 // a binding attribute.
373 foreach (WebServiceBindingAttribute at in o)
374 AddBinding (new BindingInfo (at, LogicalType.WebServiceNamespace));
376 o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
378 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
380 ParameterStyle = a.ParameterStyle;
381 RoutingStyle = a.RoutingStyle;
383 SoapBindingStyle = SoapBindingStyle.Document;
385 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
387 SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
389 ParameterStyle = SoapParameterStyle.Wrapped;
390 RoutingStyle = srs.RoutingStyle;
391 Use = SoapBindingUse.Encoded;
392 SoapBindingStyle = SoapBindingStyle.Rpc;
394 ParameterStyle = SoapParameterStyle.Wrapped;
395 RoutingStyle = SoapServiceRoutingStyle.SoapAction;
396 Use = SoapBindingUse.Literal;
397 SoapBindingStyle = SoapBindingStyle.Document;
401 if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
402 if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
404 xmlImporter.IncludeTypes (Type);
405 soapImporter.IncludeTypes (Type);
407 SoapExtensions = SoapExtension.GetTypeExtensions (Type);
410 public override XmlReflectionImporter XmlImporter
412 get { return xmlImporter; }
415 public override SoapReflectionImporter SoapImporter
417 get { return soapImporter; }
420 public override string ProtocolName
422 get { return "Soap"; }
425 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
427 SoapMethodStubInfo res = null;
428 object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
429 if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
431 if (ats.Length == 0 && isClientProxy)
433 else if (ats.Length == 0)
434 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
436 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
438 methods_byaction [res.Action] = res;
442 internal void RegisterHeaderType (Type type, string serviceNamespace, SoapBindingUse use)
444 Hashtable serializers = header_serializers [(int)use];
445 if (serializers == null) {
446 serializers = new Hashtable ();
447 header_serializers [(int)use] = serializers;
448 header_serializers_byname [(int)use] = new Hashtable ();
451 if (serializers.ContainsKey (type))
455 if (use == SoapBindingUse.Literal) {
456 XmlReflectionImporter ri = new XmlReflectionImporter ();
458 // MS.NET reflects header classes in a weird way. The root element
459 // name is the CLR class name unless it is specified in an XmlRootAttribute.
460 // The usual is to use the xml type name by default, but not in this case.
462 XmlRootAttribute root;
463 XmlAttributes ats = new XmlAttributes (type);
464 if (ats.XmlRoot != null) root = ats.XmlRoot;
465 else root = new XmlRootAttribute (type.Name);
467 if (root.Namespace == null) root.Namespace = LogicalType.GetWebServiceLiteralNamespace (serviceNamespace);
468 if (root.ElementName == null) root.ElementName = type.Name;
470 tm = ri.ImportTypeMapping (type, root);
473 SoapReflectionImporter ri = new SoapReflectionImporter ();
474 tm = ri.ImportTypeMapping (type, LogicalType.GetWebServiceEncodedNamespace (serviceNamespace));
477 int sid = RegisterSerializer (tm);
479 serializers [type] = sid;
480 header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
483 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
485 Hashtable table = header_serializers [(int)use];
486 if (table == null) return null;
488 return GetSerializer ((int) table [type]);
491 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
493 Hashtable table = header_serializers_byname [(int)use];
494 if (table == null) return null;
496 object serId = table [qname];
497 if (serId == null) return null;
499 return GetSerializer ((int) serId);
502 public SoapMethodStubInfo GetMethodForSoapAction (string name)
504 return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];