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
;
130 // When using RPC, MS.NET seems to ignore RequestElementName and
131 // MessageName, and it always uses the method name
132 RequestName
= source
.Name
;
133 ResponseName
= source
.Name
+ "Response";
134 // RequestName = rma.RequestElementName;
135 // ResponseName = rma.ResponseElementName;
136 RequestNamespace
= rma
.RequestNamespace
;
137 ResponseNamespace
= rma
.ResponseNamespace
;
138 ParameterStyle
= SoapParameterStyle
.Wrapped
;
140 SoapBindingStyle
= SoapBindingStyle
.Rpc
;
142 // For RPC calls, make all arguments be part of the empty namespace
143 optional_ns
= new XmlElementAttribute ();
144 optional_ns
.Namespace
= "";
148 if (source
.ReturnType
!= typeof (void))
149 throw new Exception ("OneWay methods should not have a return value.");
150 if (source
.OutParameters
.Length
!= 0)
151 throw new Exception ("OneWay methods should not have out/ref parameters.");
154 BindingInfo binfo
= parent
.GetBinding (Binding
);
155 if (binfo
== null) throw new InvalidOperationException ("Type '" + parent
.Type
+ "' is missing WebServiceBinding attribute that defines a binding named '" + Binding
+ "'.");
157 string serviceNamespace
= binfo
.Namespace
;
159 if (RequestNamespace
== "") RequestNamespace
= parent
.LogicalType
.GetWebServiceNamespace (serviceNamespace
, Use
);
160 if (ResponseNamespace
== "") ResponseNamespace
= parent
.LogicalType
.GetWebServiceNamespace (serviceNamespace
, Use
);
161 if (RequestName
== "") RequestName
= Name
;
162 if (ResponseName
== "") ResponseName
= Name
+ "Response";
163 if (Action
== null || Action
== "")
164 Action
= serviceNamespace
.EndsWith("/") ? (serviceNamespace
+ Name
) : (serviceNamespace
+ "/" + Name
);
166 bool hasWrappingElem
= (ParameterStyle
== SoapParameterStyle
.Wrapped
);
167 bool writeAccessors
= (SoapBindingStyle
== SoapBindingStyle
.Rpc
);
169 XmlReflectionMember
[] in_members
= BuildRequestReflectionMembers (optional_ns
);
170 XmlReflectionMember
[] out_members
= BuildResponseReflectionMembers (optional_ns
);
172 if (Use
== SoapBindingUse
.Literal
) {
173 xmlImporter
.IncludeTypes (source
.CustomAttributeProvider
);
174 InputMembersMapping
= xmlImporter
.ImportMembersMapping (RequestName
, RequestNamespace
, in_members
, hasWrappingElem
);
175 OutputMembersMapping
= xmlImporter
.ImportMembersMapping (ResponseName
, ResponseNamespace
, out_members
, hasWrappingElem
);
178 soapImporter
.IncludeTypes (source
.CustomAttributeProvider
);
179 InputMembersMapping
= soapImporter
.ImportMembersMapping (RequestName
, RequestNamespace
, in_members
, hasWrappingElem
, writeAccessors
);
180 OutputMembersMapping
= soapImporter
.ImportMembersMapping (ResponseName
, ResponseNamespace
, out_members
, hasWrappingElem
, writeAccessors
);
183 requestSerializerId
= parent
.RegisterSerializer (InputMembersMapping
);
184 responseSerializerId
= parent
.RegisterSerializer (OutputMembersMapping
);
186 object[] o
= source
.GetCustomAttributes (typeof (SoapHeaderAttribute
));
187 ArrayList headerList
= new ArrayList (o
.Length
);
189 for (int i
= 0; i
< o
.Length
; i
++) {
190 SoapHeaderAttribute att
= (SoapHeaderAttribute
) o
[i
];
191 MemberInfo
[] mems
= source
.DeclaringType
.GetMember (att
.MemberName
);
192 if (mems
.Length
== 0) throw new InvalidOperationException ("Member " + att
.MemberName
+ " not found in class " + source
.DeclaringType
.FullName
+ ".");
194 HeaderInfo header
= new HeaderInfo (mems
[0], att
);
195 headerList
.Add (header
);
197 if (!header
.IsUnknownHeader
)
198 parent
.RegisterHeaderType (header
.HeaderType
, serviceNamespace
, Use
);
200 Headers
= (HeaderInfo
[]) headerList
.ToArray (typeof(HeaderInfo
));
202 SoapExtensions
= SoapExtension
.GetMethodExtensions (source
);
205 XmlReflectionMember
[] BuildRequestReflectionMembers (XmlElementAttribute optional_ns
)
207 ParameterInfo
[] input
= MethodInfo
.InParameters
;
208 XmlReflectionMember
[] in_members
= new XmlReflectionMember
[input
.Length
];
210 for (int i
= 0; i
< input
.Length
; i
++)
212 XmlReflectionMember m
= new XmlReflectionMember ();
213 m
.IsReturnValue
= false;
214 m
.MemberName
= input
[i
].Name
;
215 m
.MemberType
= input
[i
].ParameterType
;
217 m
.XmlAttributes
= new XmlAttributes (input
[i
]);
218 m
.SoapAttributes
= new SoapAttributes (input
[i
]);
220 if (m
.MemberType
.IsByRef
)
221 m
.MemberType
= m
.MemberType
.GetElementType ();
222 if (optional_ns
!= null)
223 m
.XmlAttributes
.XmlElements
.Add (optional_ns
);
229 XmlReflectionMember
[] BuildResponseReflectionMembers (XmlElementAttribute optional_ns
)
231 ParameterInfo
[] output
= MethodInfo
.OutParameters
;
232 bool has_return_value
= !(OneWay
|| MethodInfo
.ReturnType
== typeof (void));
233 XmlReflectionMember
[] out_members
= new XmlReflectionMember
[(has_return_value
? 1 : 0) + output
.Length
];
234 XmlReflectionMember m
;
237 if (has_return_value
)
239 m
= new XmlReflectionMember ();
240 m
.IsReturnValue
= true;
241 m
.MemberName
= RequestName
+ "Result";
242 m
.MemberType
= MethodInfo
.ReturnType
;
244 m
.XmlAttributes
= new XmlAttributes (MethodInfo
.ReturnTypeCustomAttributeProvider
);
245 m
.SoapAttributes
= new SoapAttributes (MethodInfo
.ReturnTypeCustomAttributeProvider
);
247 if (optional_ns
!= null)
248 m
.XmlAttributes
.XmlElements
.Add (optional_ns
);
253 for (int i
= 0; i
< output
.Length
; i
++)
255 m
= new XmlReflectionMember ();
256 m
.IsReturnValue
= false;
257 m
.MemberName
= output
[i
].Name
;
258 m
.MemberType
= output
[i
].ParameterType
;
259 m
.XmlAttributes
= new XmlAttributes (output
[i
]);
260 m
.SoapAttributes
= new SoapAttributes (output
[i
]);
262 if (m
.MemberType
.IsByRef
)
263 m
.MemberType
= m
.MemberType
.GetElementType ();
264 if (optional_ns
!= null)
265 m
.XmlAttributes
.XmlElements
.Add (optional_ns
);
266 out_members
[i
+ idx
] = m
;
271 public HeaderInfo
GetHeaderInfo (Type headerType
)
273 foreach (HeaderInfo headerInfo
in Headers
)
274 if (headerInfo
.HeaderType
== headerType
) return headerInfo
;
279 internal class HeaderInfo
281 internal MemberInfo Member
;
282 internal SoapHeaderAttribute AttributeInfo
;
283 internal Type HeaderType
;
284 internal bool IsUnknownHeader
;
286 public HeaderInfo (MemberInfo member
, SoapHeaderAttribute attributeInfo
)
289 AttributeInfo
= attributeInfo
;
290 if (Member
is PropertyInfo
) HeaderType
= ((PropertyInfo
)Member
).PropertyType
;
291 else HeaderType
= ((FieldInfo
)Member
).FieldType
;
293 if (HeaderType
== typeof(SoapHeader
) || HeaderType
== typeof(SoapUnknownHeader
) ||
294 HeaderType
== typeof(SoapHeader
[]) || HeaderType
== typeof(SoapUnknownHeader
[]))
296 IsUnknownHeader
= true;
298 else if (!typeof(SoapHeader
).IsAssignableFrom (HeaderType
))
299 throw new InvalidOperationException (string.Format ("Header members type must be a SoapHeader subclass"));
302 public object GetHeaderValue (object ob
)
304 if (Member
is PropertyInfo
) return ((PropertyInfo
)Member
).GetValue (ob
, null);
305 else return ((FieldInfo
)Member
).GetValue (ob
);
308 public void SetHeaderValue (object ob
, SoapHeader header
)
310 object value = header
;
311 if (IsUnknownHeader
&& HeaderType
.IsArray
)
313 SoapUnknownHeader uheader
= header
as SoapUnknownHeader
;
314 SoapUnknownHeader
[] array
= (SoapUnknownHeader
[]) GetHeaderValue (ob
);
315 if (array
== null || array
.Length
== 0) {
316 value = new SoapUnknownHeader
[] { uheader }
;
319 SoapUnknownHeader
[] newArray
= new SoapUnknownHeader
[array
.Length
+1];
320 Array
.Copy (array
, newArray
, array
.Length
);
321 newArray
[array
.Length
] = uheader
;
326 if (Member
is PropertyInfo
) ((PropertyInfo
)Member
).SetValue (ob
, value, null);
327 else ((FieldInfo
)Member
).SetValue (ob
, value);
330 public SoapHeaderDirection Direction
332 get { return AttributeInfo.Direction; }
338 // Holds the metadata loaded from the type stub, as well as
339 // the metadata for all the methods in the type
341 internal class SoapTypeStubInfo
: TypeStubInfo
343 Hashtable
[] header_serializers
= new Hashtable
[3];
344 Hashtable
[] header_serializers_byname
= new Hashtable
[3];
345 Hashtable methods_byaction
= new Hashtable ();
348 internal SoapParameterStyle ParameterStyle
;
349 internal SoapServiceRoutingStyle RoutingStyle
;
350 internal SoapBindingUse Use
;
351 internal SoapExtensionRuntimeConfig
[][] SoapExtensions
;
352 internal SoapBindingStyle SoapBindingStyle
;
353 internal XmlReflectionImporter xmlImporter
;
354 internal SoapReflectionImporter soapImporter
;
356 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo
)
357 : base (logicalTypeInfo
)
359 xmlImporter
= new XmlReflectionImporter ();
360 soapImporter
= new SoapReflectionImporter ();
364 o
= Type
.GetCustomAttributes (typeof (WebServiceBindingAttribute
), false);
366 if (typeof (SoapHttpClientProtocol
).IsAssignableFrom (Type
))
369 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type
+ "'.");
371 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type
+ "'.");
373 // Remove the default binding, it is not needed since there is always
374 // a binding attribute.
378 foreach (WebServiceBindingAttribute at
in o
)
379 AddBinding (new BindingInfo (at
, LogicalType
.WebServiceNamespace
));
381 o
= Type
.GetCustomAttributes (typeof (SoapDocumentServiceAttribute
), false);
383 SoapDocumentServiceAttribute a
= (SoapDocumentServiceAttribute
) o
[0];
385 ParameterStyle
= a
.ParameterStyle
;
386 RoutingStyle
= a
.RoutingStyle
;
388 SoapBindingStyle
= SoapBindingStyle
.Document
;
390 o
= Type
.GetCustomAttributes (typeof (SoapRpcServiceAttribute
), false);
392 SoapRpcServiceAttribute srs
= (SoapRpcServiceAttribute
) o
[0];
394 ParameterStyle
= SoapParameterStyle
.Wrapped
;
395 RoutingStyle
= srs
.RoutingStyle
;
396 Use
= SoapBindingUse
.Encoded
;
397 SoapBindingStyle
= SoapBindingStyle
.Rpc
;
399 ParameterStyle
= SoapParameterStyle
.Wrapped
;
400 RoutingStyle
= SoapServiceRoutingStyle
.SoapAction
;
401 Use
= SoapBindingUse
.Literal
;
402 SoapBindingStyle
= SoapBindingStyle
.Document
;
406 if (ParameterStyle
== SoapParameterStyle
.Default
) ParameterStyle
= SoapParameterStyle
.Wrapped
;
407 if (Use
== SoapBindingUse
.Default
) Use
= SoapBindingUse
.Literal
;
409 xmlImporter
.IncludeTypes (Type
);
410 soapImporter
.IncludeTypes (Type
);
412 SoapExtensions
= SoapExtension
.GetTypeExtensions (Type
);
415 public override XmlReflectionImporter XmlImporter
417 get { return xmlImporter; }
420 public override SoapReflectionImporter SoapImporter
422 get { return soapImporter; }
425 public override string ProtocolName
427 get { return "Soap"; }
430 protected override MethodStubInfo
CreateMethodStubInfo (TypeStubInfo parent
, LogicalMethodInfo lmi
, bool isClientProxy
)
432 SoapMethodStubInfo res
= null;
433 object [] ats
= lmi
.GetCustomAttributes (typeof (SoapDocumentMethodAttribute
));
434 if (ats
.Length
== 0) ats
= lmi
.GetCustomAttributes (typeof (SoapRpcMethodAttribute
));
436 if (ats
.Length
== 0 && isClientProxy
)
438 else if (ats
.Length
== 0)
439 res
= new SoapMethodStubInfo (parent
, lmi
, null, xmlImporter
, soapImporter
);
441 res
= new SoapMethodStubInfo (parent
, lmi
, ats
[0], xmlImporter
, soapImporter
);
443 methods_byaction
[res
.Action
] = res
;
447 internal void RegisterHeaderType (Type type
, string serviceNamespace
, SoapBindingUse use
)
449 Hashtable serializers
= header_serializers
[(int)use
];
450 if (serializers
== null) {
451 serializers
= new Hashtable ();
452 header_serializers
[(int)use
] = serializers
;
453 header_serializers_byname
[(int)use
] = new Hashtable ();
456 if (serializers
.ContainsKey (type
))
460 if (use
== SoapBindingUse
.Literal
) {
461 XmlReflectionImporter ri
= new XmlReflectionImporter ();
463 // MS.NET reflects header classes in a weird way. The root element
464 // name is the CLR class name unless it is specified in an XmlRootAttribute.
465 // The usual is to use the xml type name by default, but not in this case.
467 XmlRootAttribute root
;
468 XmlAttributes ats
= new XmlAttributes (type
);
469 if (ats
.XmlRoot
!= null) root
= ats
.XmlRoot
;
470 else root
= new XmlRootAttribute (type
.Name
);
472 if (root
.Namespace
== null) root
.Namespace
= LogicalType
.GetWebServiceLiteralNamespace (serviceNamespace
);
473 if (root
.ElementName
== null) root
.ElementName
= type
.Name
;
475 tm
= ri
.ImportTypeMapping (type
, root
);
478 SoapReflectionImporter ri
= new SoapReflectionImporter ();
479 tm
= ri
.ImportTypeMapping (type
, LogicalType
.GetWebServiceEncodedNamespace (serviceNamespace
));
482 int sid
= RegisterSerializer (tm
);
484 serializers
[type
] = sid
;
485 header_serializers_byname
[(int)use
] [new XmlQualifiedName (tm
.ElementName
, tm
.Namespace
)] = sid
;
488 internal XmlSerializer
GetHeaderSerializer (Type type
, SoapBindingUse use
)
490 Hashtable table
= header_serializers
[(int)use
];
491 if (table
== null) return null;
493 return GetSerializer ((int) table
[type
]);
496 internal XmlSerializer
GetHeaderSerializer (XmlQualifiedName qname
, SoapBindingUse use
)
498 Hashtable table
= header_serializers_byname
[(int)use
];
499 if (table
== null) return null;
501 object serId
= table
[qname
];
502 if (serId
== null) return null;
504 return GetSerializer ((int) serId
);
507 public SoapMethodStubInfo
GetMethodForSoapAction (string name
)
509 return (SoapMethodStubInfo
) methods_byaction
[name
.Trim ('"',' ')];