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 requestSerializerId
= parent
.RegisterSerializer (InputMembersMapping
);
218 responseSerializerId
= parent
.RegisterSerializer (OutputMembersMapping
);
220 object[] o
= source
.GetCustomAttributes (typeof (SoapHeaderAttribute
));
221 ArrayList allHeaderList
= new ArrayList (o
.Length
);
222 ArrayList inHeaderList
= new ArrayList (o
.Length
);
223 ArrayList outHeaderList
= new ArrayList (o
.Length
);
224 ArrayList faultHeaderList
= new ArrayList ();
226 SoapHeaderDirection unknownHeaderDirections
= (SoapHeaderDirection
)0;
228 for (int i
= 0; i
< o
.Length
; i
++) {
229 SoapHeaderAttribute att
= (SoapHeaderAttribute
) o
[i
];
230 MemberInfo
[] mems
= source
.DeclaringType
.GetMember (att
.MemberName
);
231 if (mems
.Length
== 0) throw new InvalidOperationException ("Member " + att
.MemberName
+ " not found in class " + source
.DeclaringType
.FullName
+ ".");
233 HeaderInfo header
= new HeaderInfo (mems
[0], att
);
234 allHeaderList
.Add (header
);
235 if (!header
.Custom
) {
236 if ((header
.Direction
& SoapHeaderDirection
.In
) != 0)
237 inHeaderList
.Add (header
);
238 if ((header
.Direction
& SoapHeaderDirection
.Out
) != 0)
239 outHeaderList
.Add (header
);
240 if ((header
.Direction
& SoapHeaderDirection
.Fault
) != 0)
241 faultHeaderList
.Add (header
);
243 unknownHeaderDirections
|= header
.Direction
;
246 Headers
= (HeaderInfo
[]) allHeaderList
.ToArray (typeof(HeaderInfo
));
248 if (inHeaderList
.Count
> 0 || (unknownHeaderDirections
& SoapHeaderDirection
.In
) != 0) {
249 InHeaders
= (HeaderInfo
[]) inHeaderList
.ToArray (typeof(HeaderInfo
));
250 XmlReflectionMember
[] members
= BuildHeadersReflectionMembers (InHeaders
);
252 if (Use
== SoapBindingUse
.Literal
)
253 InputHeaderMembersMapping
= xmlImporter
.ImportMembersMapping ("", RequestNamespace
, members
, false);
255 InputHeaderMembersMapping
= soapImporter
.ImportMembersMapping ("", RequestNamespace
, members
, false, false);
257 requestHeadersSerializerId
= parent
.RegisterSerializer (InputHeaderMembersMapping
);
260 if (outHeaderList
.Count
> 0 || (unknownHeaderDirections
& SoapHeaderDirection
.Out
) != 0) {
261 OutHeaders
= (HeaderInfo
[]) outHeaderList
.ToArray (typeof(HeaderInfo
));
262 XmlReflectionMember
[] members
= BuildHeadersReflectionMembers (OutHeaders
);
264 if (Use
== SoapBindingUse
.Literal
)
265 OutputHeaderMembersMapping
= xmlImporter
.ImportMembersMapping ("", RequestNamespace
, members
, false);
267 OutputHeaderMembersMapping
= soapImporter
.ImportMembersMapping ("", RequestNamespace
, members
, false, false);
269 responseHeadersSerializerId
= parent
.RegisterSerializer (OutputHeaderMembersMapping
);
272 if (faultHeaderList
.Count
> 0 || (unknownHeaderDirections
& SoapHeaderDirection
.Fault
) != 0) {
273 FaultHeaders
= (HeaderInfo
[]) faultHeaderList
.ToArray (typeof(HeaderInfo
));
274 XmlReflectionMember
[] members
= BuildHeadersReflectionMembers (FaultHeaders
);
276 if (Use
== SoapBindingUse
.Literal
)
277 FaultHeaderMembersMapping
= xmlImporter
.ImportMembersMapping ("", RequestNamespace
, members
, false);
279 FaultHeaderMembersMapping
= soapImporter
.ImportMembersMapping ("", RequestNamespace
, members
, false, false);
281 faultHeadersSerializerId
= parent
.RegisterSerializer (FaultHeaderMembersMapping
);
284 SoapExtensions
= SoapExtension
.GetMethodExtensions (source
);
287 XmlReflectionMember
[] BuildRequestReflectionMembers (XmlElementAttribute optional_ns
)
289 ParameterInfo
[] input
= MethodInfo
.InParameters
;
290 XmlReflectionMember
[] in_members
= new XmlReflectionMember
[input
.Length
];
292 for (int i
= 0; i
< input
.Length
; i
++)
294 XmlReflectionMember m
= new XmlReflectionMember ();
295 m
.IsReturnValue
= false;
296 m
.MemberName
= input
[i
].Name
;
297 m
.MemberType
= input
[i
].ParameterType
;
299 m
.XmlAttributes
= new XmlAttributes (input
[i
]);
300 m
.SoapAttributes
= new SoapAttributes (input
[i
]);
302 if (m
.MemberType
.IsByRef
)
303 m
.MemberType
= m
.MemberType
.GetElementType ();
304 if (optional_ns
!= null)
305 m
.XmlAttributes
.XmlElements
.Add (optional_ns
);
311 XmlReflectionMember
[] BuildResponseReflectionMembers (XmlElementAttribute optional_ns
)
313 ParameterInfo
[] output
= MethodInfo
.OutParameters
;
314 bool has_return_value
= !(OneWay
|| MethodInfo
.ReturnType
== typeof (void));
315 XmlReflectionMember
[] out_members
= new XmlReflectionMember
[(has_return_value
? 1 : 0) + output
.Length
];
316 XmlReflectionMember m
;
319 if (has_return_value
)
321 m
= new XmlReflectionMember ();
322 m
.IsReturnValue
= true;
323 m
.MemberName
= Name
+ "Result";
324 m
.MemberType
= MethodInfo
.ReturnType
;
326 m
.XmlAttributes
= new XmlAttributes (MethodInfo
.ReturnTypeCustomAttributeProvider
);
327 m
.SoapAttributes
= new SoapAttributes (MethodInfo
.ReturnTypeCustomAttributeProvider
);
329 if (optional_ns
!= null)
330 m
.XmlAttributes
.XmlElements
.Add (optional_ns
);
335 for (int i
= 0; i
< output
.Length
; i
++)
337 m
= new XmlReflectionMember ();
338 m
.IsReturnValue
= false;
339 m
.MemberName
= output
[i
].Name
;
340 m
.MemberType
= output
[i
].ParameterType
;
341 m
.XmlAttributes
= new XmlAttributes (output
[i
]);
342 m
.SoapAttributes
= new SoapAttributes (output
[i
]);
344 if (m
.MemberType
.IsByRef
)
345 m
.MemberType
= m
.MemberType
.GetElementType ();
346 if (optional_ns
!= null)
347 m
.XmlAttributes
.XmlElements
.Add (optional_ns
);
348 out_members
[i
+ idx
] = m
;
353 XmlReflectionMember
[] BuildHeadersReflectionMembers (HeaderInfo
[] headers
)
355 XmlReflectionMember
[] mems
= new XmlReflectionMember
[headers
.Length
];
357 for (int n
=0; n
<headers
.Length
; n
++)
359 HeaderInfo header
= headers
[n
];
361 XmlReflectionMember m
= new XmlReflectionMember ();
362 m
.IsReturnValue
= false;
363 m
.MemberName
= header
.HeaderType
.Name
;
364 m
.MemberType
= header
.HeaderType
;
366 // MS.NET reflects header classes in a weird way. The root element
367 // name is the CLR class name unless it is specified in an XmlRootAttribute.
368 // The usual is to use the xml type name by default, but not in this case.
370 XmlAttributes ats
= new XmlAttributes (header
.HeaderType
);
371 if (ats
.XmlRoot
!= null) {
372 XmlElementAttribute xe
= new XmlElementAttribute ();
373 xe
.ElementName
= ats
.XmlRoot
.ElementName
;
374 xe
.Namespace
= ats
.XmlRoot
.Namespace
;
375 m
.XmlAttributes
= new XmlAttributes ();
376 m
.XmlAttributes
.XmlElements
.Add (xe
);
384 public HeaderInfo
GetHeaderInfo (Type headerType
)
386 foreach (HeaderInfo headerInfo
in Headers
)
387 if (headerInfo
.HeaderType
== headerType
) return headerInfo
;
391 public XmlSerializer
GetBodySerializer (SoapHeaderDirection dir
, bool soap12
)
394 case SoapHeaderDirection
.In
: return RequestSerializer
;
395 case SoapHeaderDirection
.Out
: return ResponseSerializer
;
396 case SoapHeaderDirection
.Fault
: return soap12
? Soap12Fault
.Serializer
: Fault
.Serializer
;
397 default: return null;
401 public XmlSerializer
GetHeaderSerializer (SoapHeaderDirection dir
)
404 case SoapHeaderDirection
.In
: return RequestHeadersSerializer
;
405 case SoapHeaderDirection
.Out
: return ResponseHeadersSerializer
;
406 case SoapHeaderDirection
.Fault
: return FaultHeadersSerializer
;
407 default: return null;
411 HeaderInfo
[] GetHeaders (SoapHeaderDirection dir
)
414 case SoapHeaderDirection
.In
: return InHeaders
;
415 case SoapHeaderDirection
.Out
: return OutHeaders
;
416 case SoapHeaderDirection
.Fault
: return FaultHeaders
;
417 default: return null;
421 public object[] GetHeaderValueArray (SoapHeaderDirection dir
, SoapHeaderCollection headers
)
423 HeaderInfo
[] headerInfos
= GetHeaders (dir
);
424 if (headerInfos
== null) return null;
426 object[] hs
= new object [headerInfos
.Length
];
428 for (int n
=0; n
<headers
.Count
; n
++) {
429 SoapHeader h
= headers
[n
];
430 Type t
= h
.GetType();
431 for (int i
=0; i
<headerInfos
.Length
; i
++)
432 if (headerInfos
[i
].HeaderType
== t
)
440 // Holds the metadata loaded from the type stub, as well as
441 // the metadata for all the methods in the type
443 internal class SoapTypeStubInfo
: TypeStubInfo
445 Hashtable
[] header_serializers
= new Hashtable
[3];
446 Hashtable
[] header_serializers_byname
= new Hashtable
[3];
447 Hashtable methods_byaction
= new Hashtable ();
450 internal SoapParameterStyle ParameterStyle
;
451 internal SoapExtensionRuntimeConfig
[][] SoapExtensions
;
452 internal SoapBindingStyle SoapBindingStyle
;
453 internal XmlReflectionImporter xmlImporter
;
454 internal SoapReflectionImporter soapImporter
;
456 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo
)
457 : base (logicalTypeInfo
)
459 xmlImporter
= new XmlReflectionImporter ();
460 soapImporter
= new SoapReflectionImporter ();
462 if (typeof (SoapHttpClientProtocol
).IsAssignableFrom (Type
))
464 if (Bindings
.Count
== 0 || ((BindingInfo
)Bindings
[0]).WebServiceBindingAttribute
== null)
465 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type
+ "'.");
466 if (Bindings
.Count
> 1)
467 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type
+ "'.");
470 object [] o
= Type
.GetCustomAttributes (typeof (SoapDocumentServiceAttribute
), false);
472 SoapDocumentServiceAttribute a
= (SoapDocumentServiceAttribute
) o
[0];
474 ParameterStyle
= a
.ParameterStyle
;
475 SoapBindingStyle
= SoapBindingStyle
.Document
;
477 o
= Type
.GetCustomAttributes (typeof (SoapRpcServiceAttribute
), false);
479 SoapRpcServiceAttribute srs
= (SoapRpcServiceAttribute
) o
[0];
481 ParameterStyle
= SoapParameterStyle
.Wrapped
;
482 SoapBindingStyle
= SoapBindingStyle
.Rpc
;
484 ParameterStyle
= SoapParameterStyle
.Wrapped
;
485 SoapBindingStyle
= SoapBindingStyle
.Document
;
489 if (ParameterStyle
== SoapParameterStyle
.Default
) ParameterStyle
= SoapParameterStyle
.Wrapped
;
491 xmlImporter
.IncludeTypes (Type
);
492 soapImporter
.IncludeTypes (Type
);
495 SoapExtensions
= new SoapExtensionRuntimeConfig
[2][];
497 SoapExtensions
= SoapExtension
.GetTypeExtensions (Type
);
501 internal SoapServiceRoutingStyle RoutingStyle
{
502 get { return LogicalType.RoutingStyle; }
505 public override XmlReflectionImporter XmlImporter
507 get { return xmlImporter; }
510 public override SoapReflectionImporter SoapImporter
512 get { return soapImporter; }
515 public override string ProtocolName
517 get { return "Soap"; }
520 protected override MethodStubInfo
CreateMethodStubInfo (TypeStubInfo parent
, LogicalMethodInfo lmi
, bool isClientProxy
)
522 SoapMethodStubInfo res
= null;
523 object [] ats
= lmi
.GetCustomAttributes (typeof (SoapDocumentMethodAttribute
));
524 if (ats
.Length
== 0) ats
= lmi
.GetCustomAttributes (typeof (SoapRpcMethodAttribute
));
526 if (ats
.Length
== 0 && isClientProxy
)
528 else if (ats
.Length
== 0)
529 res
= new SoapMethodStubInfo (parent
, lmi
, null, xmlImporter
, soapImporter
);
531 res
= new SoapMethodStubInfo (parent
, lmi
, ats
[0], xmlImporter
, soapImporter
);
533 methods_byaction
[res
.Action
] = res
;
537 public SoapMethodStubInfo
GetMethodForSoapAction (string name
)
539 return (SoapMethodStubInfo
) methods_byaction
[name
.Trim ('"',' ')];
543 internal class Soap12TypeStubInfo
: SoapTypeStubInfo
545 public Soap12TypeStubInfo (LogicalTypeInfo logicalTypeInfo
)
546 : base (logicalTypeInfo
)
550 public override string ProtocolName
552 get { return "Soap12"; }