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 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 + ".");
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);
193 SoapExtensions = SoapExtension.GetMethodExtensions (source);
196 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
198 ParameterInfo [] input = MethodInfo.InParameters;
199 XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
201 for (int i = 0; i < input.Length; i++)
203 XmlReflectionMember m = new XmlReflectionMember ();
204 m.IsReturnValue = false;
205 m.MemberName = input [i].Name;
206 m.MemberType = input [i].ParameterType;
208 m.XmlAttributes = new XmlAttributes (input[i]);
209 m.SoapAttributes = new SoapAttributes (input[i]);
211 if (m.MemberType.IsByRef)
212 m.MemberType = m.MemberType.GetElementType ();
213 if (optional_ns != null)
214 m.XmlAttributes.XmlElements.Add (optional_ns);
220 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
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;
228 if (has_return_value)
230 m = new XmlReflectionMember ();
231 m.IsReturnValue = true;
232 m.MemberName = RequestName + "Result";
233 m.MemberType = MethodInfo.ReturnType;
235 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
236 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
238 if (optional_ns != null)
239 m.XmlAttributes.XmlElements.Add (optional_ns);
244 for (int i = 0; i < output.Length; i++)
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]);
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;
262 public HeaderInfo GetHeaderInfo (Type headerType)
264 foreach (HeaderInfo headerInfo in Headers)
265 if (headerInfo.HeaderType == headerType) return headerInfo;
270 internal class HeaderInfo
272 internal MemberInfo Member;
273 internal SoapHeaderAttribute AttributeInfo;
274 internal Type HeaderType;
276 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
279 AttributeInfo = attributeInfo;
280 if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
281 else HeaderType = ((FieldInfo)Member).FieldType;
284 public object GetHeaderValue (object ob)
286 if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
287 else return ((FieldInfo)Member).GetValue (ob);
290 public void SetHeaderValue (object ob, object value)
292 if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
293 else ((FieldInfo)Member).SetValue (ob, value);
296 public SoapHeaderDirection Direction
298 get { return AttributeInfo.Direction; }
304 // Holds the metadata loaded from the type stub, as well as
305 // the metadata for all the methods in the type
307 internal class SoapTypeStubInfo : TypeStubInfo
309 Hashtable[] header_serializers = new Hashtable [3];
310 Hashtable[] header_serializers_byname = new Hashtable [3];
311 Hashtable methods_byaction = new Hashtable ();
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;
322 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
323 : base (logicalTypeInfo)
325 xmlImporter = new XmlReflectionImporter ();
326 soapImporter = new SoapReflectionImporter ();
330 o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
332 if (typeof (SoapHttpClientProtocol).IsAssignableFrom (Type))
335 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type + "'.");
337 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type + "'.");
339 // Remove the default binding, it is not needed since there is always
340 // a binding attribute.
344 foreach (WebServiceBindingAttribute at in o)
345 AddBinding (new BindingInfo (at, LogicalType.WebServiceNamespace));
347 o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
349 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
351 ParameterStyle = a.ParameterStyle;
352 RoutingStyle = a.RoutingStyle;
354 SoapBindingStyle = SoapBindingStyle.Document;
356 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
358 SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
360 ParameterStyle = SoapParameterStyle.Wrapped;
361 RoutingStyle = srs.RoutingStyle;
362 Use = SoapBindingUse.Encoded;
363 SoapBindingStyle = SoapBindingStyle.Rpc;
365 ParameterStyle = SoapParameterStyle.Wrapped;
366 RoutingStyle = SoapServiceRoutingStyle.SoapAction;
367 Use = SoapBindingUse.Literal;
368 SoapBindingStyle = SoapBindingStyle.Document;
372 if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
373 if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
375 xmlImporter.IncludeTypes (Type);
376 soapImporter.IncludeTypes (Type);
378 SoapExtensions = SoapExtension.GetTypeExtensions (Type);
381 public override XmlReflectionImporter XmlImporter
383 get { return xmlImporter; }
386 public override SoapReflectionImporter SoapImporter
388 get { return soapImporter; }
391 public override string ProtocolName
393 get { return "Soap"; }
396 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
398 SoapMethodStubInfo res = null;
399 object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
400 if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
402 if (ats.Length == 0 && isClientProxy)
404 else if (ats.Length == 0)
405 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
407 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
409 methods_byaction [res.Action] = res;
413 internal void RegisterHeaderType (Type type, string serviceNamespace, SoapBindingUse use)
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 ();
422 if (serializers.ContainsKey (type))
426 if (use == SoapBindingUse.Literal) {
427 XmlReflectionImporter ri = new XmlReflectionImporter ();
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.
433 XmlRootAttribute root;
434 XmlAttributes ats = new XmlAttributes (type);
435 if (ats.XmlRoot != null) root = ats.XmlRoot;
436 else root = new XmlRootAttribute (type.Name);
438 if (root.Namespace == null) root.Namespace = LogicalType.GetWebServiceLiteralNamespace (serviceNamespace);
439 if (root.ElementName == null) root.ElementName = type.Name;
441 tm = ri.ImportTypeMapping (type, root);
444 SoapReflectionImporter ri = new SoapReflectionImporter ();
445 tm = ri.ImportTypeMapping (type, LogicalType.GetWebServiceEncodedNamespace (serviceNamespace));
448 int sid = RegisterSerializer (tm);
450 serializers [type] = sid;
451 header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
454 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
456 Hashtable table = header_serializers [(int)use];
457 if (table == null) return null;
459 return GetSerializer ((int) table [type]);
462 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
464 Hashtable table = header_serializers_byname [(int)use];
465 if (table == null) return null;
467 return GetSerializer ((int) table [qname]);
470 public SoapMethodStubInfo GetMethodForSoapAction (string name)
472 return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];