2 // DefaultMessageOperationFormatter.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Eyal Alaluf <eyala@mainsoft.com>
8 // Copyright (C) 2005-2010 Novell, Inc. http://www.novell.com
9 // Copyright (C) 2008 Mainsoft Co. http://www.mainsoft.com
10 // Copyright 2011 Xamarin Inc (http://www.xamarin.com).
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.Collections.Generic;
35 using System.Reflection;
36 using System.Runtime.Serialization;
37 using System.ServiceModel;
38 using System.ServiceModel.Channels;
39 using System.ServiceModel.Description;
42 using System.Xml.Serialization;
44 namespace System.ServiceModel.Dispatcher
46 // This type is introduced for moonlight compatibility.
47 internal class OperationFormatter
48 : IDispatchMessageFormatter, IClientMessageFormatter
50 BaseMessagesFormatter impl;
51 string operation_name;
53 public OperationFormatter (OperationDescription od, bool isRpc, bool isEncoded)
55 Validate (od, isRpc, isEncoded);
57 impl = BaseMessagesFormatter.Create (od);
59 operation_name = od.Name;
62 public string OperationName {
63 get { return operation_name; }
66 internal static bool IsValidReturnValue (MessagePartDescription part)
68 return part != null && part.Type != typeof (void);
71 internal static void Validate (OperationDescription od, bool isRpc, bool isEncoded)
73 bool hasParameter = false, hasVoid = false;
74 foreach (var md in od.Messages) {
75 if (md.IsTypedMessage || md.IsUntypedMessage) {
76 if (isRpc && !isEncoded)
77 throw new InvalidOperationException ("Message with action {0} is either strongly-typed or untyped, but defined as RPC and encoded.");
78 if (hasParameter && !md.IsVoid)
79 throw new InvalidOperationException (String.Format ("Operation '{0}' contains a message with parameters. Strongly-typed or untyped message can be paired only with strongly-typed, untyped or void message.", od.Name));
81 throw new InvalidOperationException (String.Format ("This operation '{0}' is defined as RPC and contains a message with void, which is not allowed.", od.Name));
83 hasParameter |= !md.IsVoid;
89 public object DeserializeReply (Message message, object [] parameters)
91 return impl.DeserializeReply (message, parameters);
94 public Message SerializeRequest (MessageVersion messageVersion, object [] parameters)
96 return impl.SerializeRequest (messageVersion, parameters);
99 public void DeserializeRequest (Message message, object [] parameters)
101 impl.DeserializeRequest (message, parameters);
104 public Message SerializeReply (MessageVersion messageVersion, object [] parameters, object result)
106 return impl.SerializeReply (messageVersion, parameters, result);
110 internal abstract class BaseMessagesFormatter
111 : IDispatchMessageFormatter, IClientMessageFormatter
113 MessageDescriptionCollection messages;
115 ParameterInfo [] requestMethodParams;
116 ParameterInfo [] replyMethodParams;
117 List<Type> operation_known_types = new List<Type> ();
119 public BaseMessagesFormatter (MessageDescriptionCollection messages)
121 this.messages = messages;
124 public BaseMessagesFormatter (OperationDescription desc)
125 : this (desc.Messages)
127 if (desc.SyncMethod != null)
130 requestMethodParams = replyMethodParams = desc.SyncMethod.GetParameters ();
134 ParameterInfo [] methodParams = desc.BeginMethod.GetParameters ();
135 requestMethodParams = new ParameterInfo [methodParams.Length - 2];
136 Array.Copy (methodParams, requestMethodParams, requestMethodParams.Length);
137 methodParams = desc.EndMethod.GetParameters ();
138 replyMethodParams = new ParameterInfo [methodParams.Length - 1];
139 Array.Copy (methodParams, replyMethodParams, replyMethodParams.Length);
140 operation_known_types.AddRange (desc.KnownTypes);
143 // FIXME: this should be refactored and eliminated.
144 // XmlSerializerFormatAttribute and DataContractFormatAttribute
145 // should be handled at ContractDescription.GetContract (to fill
146 // IOperationBehavior for each).
148 // Fixing the issue above should also fix "Formatter is already filled at initial state" issue described in EndpointDispatcher.cs and ContractDescription.cs.
149 public static BaseMessagesFormatter Create (OperationDescription desc)
151 MethodInfo attrProvider = desc.SyncMethod ?? desc.BeginMethod;
153 attrs = attrProvider.GetCustomAttributes (typeof (XmlSerializerFormatAttribute), false);
154 if (attrs != null && attrs.Length > 0)
155 return new XmlMessagesFormatter (desc, (XmlSerializerFormatAttribute) attrs [0]);
157 attrs = attrProvider.GetCustomAttributes (typeof (DataContractFormatAttribute), false);
158 DataContractFormatAttribute dataAttr = null;
159 if (attrs != null && attrs.Length > 0)
160 dataAttr = (DataContractFormatAttribute) attrs [0];
161 return new DataContractMessagesFormatter (desc, dataAttr);
164 public IEnumerable<Type> OperationKnownTypes {
165 get { return operation_known_types; }
168 protected abstract Message PartsToMessage (
169 MessageDescription md, MessageVersion version, string action, object [] parts);
170 protected abstract object [] MessageToParts (MessageDescription md, Message message);
171 protected abstract Dictionary<MessageHeaderDescription,object> MessageToHeaderObjects (MessageDescription md, Message message);
173 public Message SerializeRequest (
174 MessageVersion version, object [] parameters)
176 MessageDescription md = null;
177 foreach (MessageDescription mdi in messages)
181 object [] parts = CreatePartsArray (md.Body);
182 var headers = md.Headers.Count > 0 ? new Dictionary<MessageHeaderDescription,object> () : null;
183 if (md.MessageType != null)
184 MessageObjectToParts (md, parameters [0], headers, parts);
187 foreach (ParameterInfo pi in requestMethodParams)
189 parts [index++] = parameters [pi.Position];
191 var msg = PartsToMessage (md, version, md.Action, parts);
193 foreach (var pair in headers)
194 if (pair.Value != null)
195 msg.Headers.Add (CreateHeader (pair.Key, pair.Value));
199 public Message SerializeReply (
200 MessageVersion version, object [] parameters, object result)
202 // use_response_converter
204 MessageDescription md = null;
205 foreach (MessageDescription mdi in messages)
209 object [] parts = CreatePartsArray (md.Body);
210 var headers = md.Headers.Count > 0 ? new Dictionary<MessageHeaderDescription,object> () : null;
211 if (md.MessageType != null)
212 MessageObjectToParts (md, result, headers, parts);
214 if (HasReturnValue (md.Body))
216 int index = ParamsOffset (md.Body);
218 foreach (ParameterInfo pi in replyMethodParams)
219 if (pi.IsOut || pi.ParameterType.IsByRef)
220 parts [index++] = parameters [paramsIdx++];
222 string action = version.Addressing == AddressingVersion.None ? null : md.Action;
223 var msg = PartsToMessage (md, version, action, parts);
225 foreach (var pair in headers)
226 if (pair.Value != null)
227 msg.Headers.Add (CreateHeader (pair.Key, pair.Value));
231 MessageHeader CreateHeader (MessageHeaderDescription mh, object value)
233 return MessageHeader.CreateHeader (mh.Name, mh.Namespace, value, mh.MustUnderstand, mh.Actor, mh.Relay);
236 public void DeserializeRequest (Message message, object [] parameters)
238 string action = message.Headers.Action;
239 MessageDescription md = messages.Find (action);
241 throw new ActionNotSupportedException (String.Format ("Action '{0}' is not supported by this operation.", action));
243 var headers = MessageToHeaderObjects (md, message);
244 object [] parts = MessageToParts (md, message);
245 if (md.MessageType != null) {
247 parameters [0] = Activator.CreateInstance (md.MessageType);
249 parameters [0] = Activator.CreateInstance (md.MessageType, true);
251 PartsToMessageObject (md, headers, parts, parameters [0]);
256 foreach (ParameterInfo pi in requestMethodParams)
258 parameters [index] = parts [index];
264 public object DeserializeReply (Message message, object [] parameters)
266 MessageDescription md = null;
267 foreach (MessageDescription mdi in messages)
271 var headers = MessageToHeaderObjects (md, message);
272 object [] parts = MessageToParts (md, message);
273 if (md.MessageType != null) {
275 object msgObject = Activator.CreateInstance (md.MessageType);
277 object msgObject = Activator.CreateInstance (md.MessageType, true);
279 PartsToMessageObject (md, headers, parts, msgObject);
283 int index = ParamsOffset (md.Body);
284 foreach (ParameterInfo pi in replyMethodParams) {
285 if (pi.IsOut || pi.ParameterType.IsByRef)
286 parameters [pi.Position] = parts [index++];
288 return HasReturnValue (md.Body) ? parts [0] : null;
292 void PartsToMessageObject (MessageDescription md, Dictionary<MessageHeaderDescription,object> headers, object [] parts, object msgObject)
295 foreach (var pair in headers) {
296 var mi = pair.Key.MemberInfo;
298 ((FieldInfo) mi).SetValue (msgObject, pair.Value);
300 ((PropertyInfo) mi).SetValue (msgObject, pair.Value, null);
303 var l = new List<MessagePartDescription> (md.Body.Parts);
304 if (md.Body.ReturnValue != null)
305 l.Add (md.Body.ReturnValue);
306 foreach (MessagePartDescription partDesc in l)
307 if (partDesc.MemberInfo is FieldInfo)
308 ((FieldInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index]);
309 else if (partDesc.MemberInfo is PropertyInfo)
310 ((PropertyInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index], null);
311 // otherwise, it could be null (in case of undefined return value in MessageContract)
314 void MessageObjectToParts (MessageDescription md, object msgObject, Dictionary<MessageHeaderDescription,object> headers, object [] parts)
316 foreach (var headDesc in md.Headers) {
317 var mi = headDesc.MemberInfo;
319 headers [headDesc] = ((FieldInfo) mi).GetValue (msgObject);
321 headers [headDesc] = ((PropertyInfo) mi).GetValue (msgObject, null);
324 var l = new List<MessagePartDescription> (md.Body.Parts);
325 if (md.Body.ReturnValue != null)
326 l.Add (md.Body.ReturnValue);
327 foreach (MessagePartDescription partDesc in l) {
328 if (partDesc.MemberInfo == null)
330 if (partDesc.MemberInfo is FieldInfo)
331 parts [partDesc.Index] = ((FieldInfo) partDesc.MemberInfo).GetValue (msgObject);
333 parts [partDesc.Index] = ((PropertyInfo) partDesc.MemberInfo).GetValue (msgObject, null);
337 internal static bool HasReturnValue (MessageBodyDescription desc)
339 return desc.ReturnValue != null && desc.ReturnValue.Type != typeof (void);
342 protected static int ParamsOffset (MessageBodyDescription desc)
344 return HasReturnValue (desc) ? 1 : 0;
347 protected static object [] CreatePartsArray (MessageBodyDescription desc)
349 if (HasReturnValue (desc))
350 return new object [desc.Parts.Count + 1];
351 return new object [desc.Parts.Count];
355 class DataContractMessagesFormatter : BaseMessagesFormatter
357 DataContractFormatAttribute attr;
359 DataContractSerializerOperationBehavior serializerBehavior;
362 public DataContractMessagesFormatter (OperationDescription desc, DataContractFormatAttribute attr)
366 this.serializerBehavior = desc.Behaviors.Find<DataContractSerializerOperationBehavior>();
371 public DataContractMessagesFormatter (MessageDescriptionCollection messages, DataContractFormatAttribute attr)
377 Dictionary<MessagePartDescription, XmlObjectSerializer> serializers
378 = new Dictionary<MessagePartDescription,XmlObjectSerializer> ();
380 protected override Message PartsToMessage (
381 MessageDescription md, MessageVersion version, string action, object [] parts)
383 return Message.CreateMessage (version, action, new DataContractBodyWriter (md.Body, this, parts));
386 protected override Dictionary<MessageHeaderDescription,object> MessageToHeaderObjects (MessageDescription md, Message message)
388 if (message.IsEmpty || md.Headers.Count == 0)
391 var dic = new Dictionary<MessageHeaderDescription,object> ();
392 for (int i = 0; i < message.Headers.Count; i++) {
393 var r = message.Headers.GetReaderAtHeader (i);
394 var mh = md.Headers.FirstOrDefault (h => h.Name == r.LocalName && h.Namespace == r.NamespaceURI);
396 dic [mh] = ReadHeaderObject (mh.Type, GetSerializer (mh), r);
401 protected override object [] MessageToParts (
402 MessageDescription md, Message message)
407 int offset = ParamsOffset (md.Body);
408 object [] parts = CreatePartsArray (md.Body);
410 XmlDictionaryReader r = message.GetReaderAtBodyContents ();
411 if (md.Body.WrapperName != null)
412 r.ReadStartElement (md.Body.WrapperName, md.Body.WrapperNamespace);
414 for (r.MoveToContent (); r.NodeType == XmlNodeType.Element; r.MoveToContent ()) {
415 XmlQualifiedName key = new XmlQualifiedName (r.LocalName, r.NamespaceURI);
416 MessagePartDescription rv = md.Body.ReturnValue;
417 if (rv != null && rv.Name == key.Name && rv.Namespace == key.Namespace && rv.Type != typeof (void))
418 parts [0] = ReadMessagePart (md.Body.ReturnValue, r);
419 else if (md.Body.Parts.Contains (key)) {
420 MessagePartDescription p = md.Body.Parts [key];
421 parts [p.Index + offset] = ReadMessagePart (p, r);
423 else // Skip unknown elements
427 if (md.Body.WrapperName != null && !r.EOF)
433 object ReadMessagePart (MessagePartDescription part, XmlDictionaryReader r)
435 if (part.Type == typeof (Stream))
436 // FIXME: it seems TransferMode.Streamed* has different serialization than .Buffered. Need to differentiate serialization somewhere (not limited to here).
437 return new MemoryStream (Convert.FromBase64String (r.ReadElementContentAsString (part.Name, part.Namespace)));
439 return GetSerializer (part).ReadObject (r);
442 XmlObjectSerializer GetSerializer (MessagePartDescription partDesc)
444 if (!serializers.ContainsKey (partDesc))
446 if (serializerBehavior != null)
447 serializers [partDesc] = serializerBehavior.CreateSerializer(
448 partDesc.Type, partDesc.Name, partDesc.Namespace, OperationKnownTypes as IList<Type>);
451 serializers [partDesc] = new DataContractSerializer (
452 partDesc.Type, partDesc.Name, partDesc.Namespace, OperationKnownTypes);
453 return serializers [partDesc];
456 object ReadHeaderObject (Type type, XmlObjectSerializer serializer, XmlDictionaryReader reader)
458 // FIXME: it's a nasty workaround just to avoid UniqueId output as a string.
459 // Seealso MessageHeader.DefaultMessageHeader.OnWriteHeaderContents().
460 // Note that msg.Headers.GetHeader<UniqueId> () simply fails (on .NET too) and it is useless. The API is lame by design.
461 if (type == typeof (UniqueId))
462 return new UniqueId (reader.ReadElementContentAsString ());
464 return serializer.ReadObject (reader);
467 class DataContractBodyWriter : BodyWriter
469 MessageBodyDescription desc;
471 DataContractMessagesFormatter parent;
473 public DataContractBodyWriter (MessageBodyDescription desc, DataContractMessagesFormatter parent, object [] parts)
477 this.parent = parent;
481 protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
483 int offset = HasReturnValue (desc) ? 1 : 0;
484 if (desc.WrapperName != null)
485 writer.WriteStartElement (desc.WrapperName, desc.WrapperNamespace);
486 if (HasReturnValue (desc))
487 WriteMessagePart (writer, desc, desc.ReturnValue, parts [0]);
488 foreach (MessagePartDescription partDesc in desc.Parts)
489 WriteMessagePart (writer, desc, partDesc, parts [partDesc.Index + offset]);
490 if (desc.WrapperName != null)
491 writer.WriteEndElement ();
494 void WriteMessagePart (
495 XmlDictionaryWriter writer, MessageBodyDescription desc, MessagePartDescription partDesc, object obj)
497 // FIXME: it seems TransferMode.Streamed* has different serialization than .Buffered. Need to differentiate serialization somewhere (not limited to here).
498 if (partDesc.Type == typeof (Stream)) {
499 writer.WriteStartElement (partDesc.Name, partDesc.Namespace);
500 writer.WriteValue (new StreamProvider ((Stream) obj));
501 writer.WriteEndElement ();
504 parent.GetSerializer (partDesc).WriteObject (writer, obj);
508 class StreamProvider : IStreamProvider
513 public StreamProvider (Stream s)
518 public Stream GetStream ()
521 throw new InvalidOperationException ("Stream is already in use.");
526 public void ReleaseStream (Stream stream)
529 throw new ArgumentNullException ("stream");
530 if (this.s != stream)
531 throw new ArgumentException ("Incorrect parameter stream");