Merge branch 'master' into import-wcf
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / Message.cs
1 //
2 // Message.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2005-2006,2010 Novell, Inc.  http://www.novell.com
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Runtime.Serialization;
32 using System.Xml;
33 using System.Xml.Schema;
34
35 namespace System.ServiceModel.Channels
36 {
37         public abstract class Message : IDisposable
38         {
39                 bool disposed;
40                 string body_id;
41                 Message copied_message;
42                 string string_cache;
43
44                 protected Message () {
45                         State = MessageState.Created;
46                 }
47
48                 public abstract MessageHeaders Headers { get; }
49
50                 public virtual bool IsEmpty {
51                         get { return false; }
52                 }
53
54                 public virtual bool IsFault {
55                         get { return false; }
56                 }
57
58                 public abstract MessageProperties Properties { get; }
59
60                 public MessageState State { get; private set; }
61
62                 public abstract MessageVersion Version { get; }
63
64                 protected bool IsDisposed {
65                         get { return disposed; }
66                 }
67
68                 public void Close ()
69                 {
70                         if (!disposed)
71                                 OnClose ();
72                         State = MessageState.Closed;
73                         disposed = true;
74                 }
75
76                 public MessageBuffer CreateBufferedCopy (int maxBufferSize)
77                 {
78                         if (State != MessageState.Created)
79                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
80
81                         if (copied_message != null)
82                                 return copied_message.CreateBufferedCopy (maxBufferSize);
83
84                         try {
85                                 return OnCreateBufferedCopy (maxBufferSize);
86                         } finally {
87                                 State = MessageState.Copied;
88                         }
89                 }
90
91                 void IDisposable.Dispose ()
92                 {
93                         Close ();
94                 }
95
96                 public T GetBody<T> ()
97                 {
98                         return OnGetBody<T> (GetReaderAtBodyContents ());
99                 }
100
101                 public T GetBody<T> (XmlObjectSerializer xmlFormatter)
102                 {
103                         // FIXME: Somehow use OnGetBody() here as well?
104                         return (T)xmlFormatter.ReadObject (GetReaderAtBodyContents ());
105                 }
106
107                 protected virtual T OnGetBody<T> (XmlDictionaryReader reader)
108                 {
109                         var xmlFormatter = new DataContractSerializer (typeof (T));
110                         return (T)xmlFormatter.ReadObject (reader);
111                 }
112
113                 public string GetBodyAttribute (string localName, string ns)
114                 {
115                         return OnGetBodyAttribute (localName, ns);
116                 }
117
118                 public XmlDictionaryReader GetReaderAtBodyContents ()
119                 {
120                         if (copied_message != null)
121                                 return copied_message.GetReaderAtBodyContents ();
122
123                         return OnGetReaderAtBodyContents ();
124                 }
125
126                 public override string ToString ()
127                 {
128                         if (string_cache != null)
129                                 return string_cache;
130
131                         StringWriter sw = new StringWriter ();
132                         XmlWriterSettings settings = new XmlWriterSettings ();
133                         settings.Indent = true;
134                         settings.OmitXmlDeclaration = true;
135
136                         using (XmlWriter w = XmlWriter.Create (sw, settings)) {
137                                 OnBodyToString (XmlDictionaryWriter.CreateDictionaryWriter (w));
138                         }
139                         string_cache = sw.ToString ();
140                         return string_cache;
141                 }
142
143                 void WriteXsiNil (XmlDictionaryWriter writer)
144                 {
145                         var dic = Constants.SoapDictionary;
146                         writer.WriteStartElement ("z", dic.Add ("anyType"), dic.Add (Constants.MSSerialization));
147                         writer.WriteAttributeString ("i", dic.Add ("nil"), dic.Add ("http://www.w3.org/2001/XMLSchema-instance"), "true");
148                         writer.WriteEndElement ();
149                 }
150
151                 public void WriteBody (XmlDictionaryWriter writer)
152                 {
153                         if (Version.Envelope != EnvelopeVersion.None)
154                                 WriteStartBody (writer);
155                         WriteBodyContents (writer);
156                         if (Version.Envelope != EnvelopeVersion.None)
157                                 writer.WriteEndElement ();
158                 }
159
160                 public void WriteBody (XmlWriter writer)
161                 {
162                         WriteBody (XmlDictionaryWriter.CreateDictionaryWriter (writer));
163                 }
164
165                 public void WriteBodyContents (XmlDictionaryWriter writer)
166                 {
167                         if (!IsEmpty) {
168                                 if (copied_message != null)
169                                         copied_message.WriteBodyContents (writer);
170                                 else
171                                         OnWriteBodyContents (writer);
172                         }
173                         else if (Version.Envelope == EnvelopeVersion.None)
174                                 WriteXsiNil (writer);
175                         State = MessageState.Written;
176                 }
177
178                 public void WriteMessage (XmlDictionaryWriter writer)
179                 {
180                         if (State != MessageState.Created)
181                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
182
183                         OnWriteMessage (writer);
184                 }
185
186                 public void WriteMessage (XmlWriter writer)
187                 {
188                         WriteMessage (XmlDictionaryWriter.CreateDictionaryWriter (writer));
189                 }
190
191                 public void WriteStartBody (XmlDictionaryWriter writer)
192                 {
193                         if (State != MessageState.Created)
194                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
195
196                         OnWriteStartBody (writer);
197                 }
198
199                 public void WriteStartBody (XmlWriter writer)
200                 {
201                         WriteStartBody (
202                                 XmlDictionaryWriter.CreateDictionaryWriter (writer));
203                 }
204
205                 public void WriteStartEnvelope (XmlDictionaryWriter writer)
206                 {
207                         if (State != MessageState.Created)
208                                 throw new InvalidOperationException (String.Format ("The message is already at {0} state", State));
209
210                         OnWriteStartEnvelope (writer);
211                 }
212
213                 protected virtual void OnBodyToString (
214                         XmlDictionaryWriter writer)
215                 {
216                         MessageState tempState = State;
217                         try {
218                                 var mb = CreateBufferedCopy (int.MaxValue);
219                                 copied_message = mb.CreateMessage ();
220                                 var msg = mb.CreateMessage ();
221                                 msg.WriteMessage (writer);
222                         }
223                         finally {
224                                 State = tempState;
225                         }
226                 }
227
228                 protected virtual void OnClose ()
229                 {
230                 }
231
232                 protected virtual MessageBuffer OnCreateBufferedCopy (
233                         int maxBufferSize)
234                 {
235                         var s = new XmlWriterSettings ();
236                         s.OmitXmlDeclaration = true;
237                         s.ConformanceLevel = ConformanceLevel.Auto;
238                         StringWriter sw = new StringWriter ();
239                         using (XmlDictionaryWriter w = XmlDictionaryWriter.CreateDictionaryWriter (XmlWriter.Create (sw, s)))
240                                 WriteBodyContents (w);
241                         var headers = new MessageHeaders (Headers);
242                         var props = new MessageProperties (Properties);
243                         return new DefaultMessageBuffer (maxBufferSize, headers, props, new XmlReaderBodyWriter (sw.ToString (), maxBufferSize, null), false, new AttributeCollection ());
244                 }
245
246                 protected virtual string OnGetBodyAttribute (
247                         string localName, string ns)
248                 {
249                         return null;
250                 }
251
252                 protected virtual XmlDictionaryReader OnGetReaderAtBodyContents ()
253                 {
254                         var ws = new XmlWriterSettings ();
255                         ws.ConformanceLevel = ConformanceLevel.Auto;
256                         StringWriter sw = new StringWriter ();
257                         using (XmlDictionaryWriter body = XmlDictionaryWriter.CreateDictionaryWriter (XmlWriter.Create (sw, ws))) {
258                                 WriteBodyContents (body);
259                         }
260
261                         var nt = new NameTable ();
262                         var nsmgr = new XmlNamespaceManager (nt);
263                         nsmgr.AddNamespace ("s", Version.Envelope.Namespace);
264                         nsmgr.AddNamespace ("a", Version.Addressing.Namespace);
265                         var pc = new XmlParserContext (nt, nsmgr, null, XmlSpace.None);
266                         
267                         var rs = new XmlReaderSettings ();
268                         rs.ConformanceLevel = ConformanceLevel.Auto;
269                         
270                         return XmlDictionaryReader.CreateDictionaryReader (XmlReader.Create (new StringReader (sw.ToString ()), rs, pc));
271                 }
272
273                 protected abstract void OnWriteBodyContents (
274                         XmlDictionaryWriter writer);
275
276                 protected virtual void OnWriteMessage (
277                         XmlDictionaryWriter writer)
278                 {
279                         if (Version.Envelope != EnvelopeVersion.None) {
280                                 WriteStartEnvelope (writer);
281                                 if (Headers.Count > 0) {
282                                         OnWriteStartHeaders (writer);
283                                         for (int i = 0, count = Headers.Count; i < count; i++)
284                                                 Headers.WriteHeader (i, writer);
285                                         writer.WriteEndElement ();
286                                 }
287                         }
288                         WriteBody (writer);
289                         if (Version.Envelope != EnvelopeVersion.None)
290                                 writer.WriteEndElement ();
291                 }
292
293                 protected virtual void OnWriteStartBody (
294                         XmlDictionaryWriter writer)
295                 {
296                         var dic = Constants.SoapDictionary;
297                         writer.WriteStartElement ("s", dic.Add ("Body"), dic.Add (Version.Envelope.Namespace));
298                 }
299
300                 protected virtual void OnWriteStartEnvelope (
301                         XmlDictionaryWriter writer)
302                 {
303                         var dic = Constants.SoapDictionary;
304                         writer.WriteStartElement ("s", dic.Add ("Envelope"), dic.Add (Version.Envelope.Namespace));
305                         if (Headers.Action != null && Version.Addressing.Namespace != MessageVersion.None.Addressing.Namespace)
306                                 writer.WriteXmlnsAttribute ("a", dic.Add (Version.Addressing.Namespace));
307                         foreach (MessageHeaderInfo h in Headers)
308                                 if (h.Id != null && writer.LookupPrefix (Constants.WsuNamespace) != "u") {
309                                         writer.WriteXmlnsAttribute ("u", dic.Add (Constants.WsuNamespace));
310                                         break;
311                                 }
312                 }
313
314                 protected virtual void OnWriteStartHeaders (
315                         XmlDictionaryWriter writer)
316                 {
317                         var dic = Constants.SoapDictionary;
318                         writer.WriteStartElement ("s", dic.Add ("Header"), dic.Add (Version.Envelope.Namespace));
319                 }
320
321                 #region factory methods
322
323                 // 1) version, code, reason, action -> 3
324                 // 2) version, code, reason, detail, action -> 3
325                 // 3) version, fault, action -> SimpleMessage
326                 // 4) version, action, body -> 10 or 5
327                 // 5) version, action, body, formatter -> 10 or 9
328                 // 6) version, action, xmlReader -> 7
329                 // 7) version, action, reader -> 9
330                 // 8) xmlReader, maxSizeOfHeaders, version -> 11
331                 // 9) version, action, body -> SimpleMessage
332                 // 10) version, action -> EmptyMessage
333                 // 11) reader, maxSizeOfHeaders, version -> XmlReaderMessage
334
335                 // 1)
336                 public static Message CreateMessage (MessageVersion version,
337                         FaultCode code, string reason, string action)
338                 {
339                         MessageFault fault = MessageFault.CreateFault (code, reason);
340                         return CreateMessage (version, fault, action);
341                 }
342
343                 // 2)
344                 public static Message CreateMessage (MessageVersion version,
345                         FaultCode code, string reason, object detail,
346                         string action)
347                 {
348                         MessageFault fault = MessageFault.CreateFault (
349                                 code, new FaultReason (reason), detail);
350                         return CreateMessage (version, fault, action);
351                 }
352
353                 // 3)
354                 public static Message CreateMessage (MessageVersion version,
355                         MessageFault fault, string action)
356                 {
357                         return new SimpleMessage (version, action,
358                                 new MessageFaultBodyWriter (fault, version), true, empty_attributes);
359                 }
360
361                 // 4)
362                 public static Message CreateMessage (MessageVersion version,
363                         string action, object body)
364                 {
365                         return body == null ?
366                                 CreateMessage (version, action) :
367                                 CreateMessage (version, action, body, new DataContractSerializer (body.GetType ()));
368                 }
369
370                 // 5)
371                 public static Message CreateMessage (MessageVersion version,
372                         string action, object body, XmlObjectSerializer xmlFormatter)
373                 {
374                         return body == null ?
375                                 CreateMessage (version, action) :
376                                 CreateMessage (
377                                         version, action,
378                                         new XmlObjectSerializerBodyWriter (body, xmlFormatter));
379                 }
380
381                 // 6)
382                 public static Message CreateMessage (MessageVersion version,
383                         string action, XmlReader body)
384                 {
385                         return CreateMessage (version, action,
386                                 XmlDictionaryReader.CreateDictionaryReader (body));
387                 }
388
389                 // 7)
390                 public static Message CreateMessage (MessageVersion version,
391                         string action, XmlDictionaryReader body)
392                 {
393                         return CreateMessage (version, action,
394                                 new XmlReaderBodyWriter (body));
395                 }
396
397                 // 8)
398                 public static Message CreateMessage (XmlReader envelopeReader,
399                         int maxSizeOfHeaders, MessageVersion version)
400                 {
401                         return CreateMessage (
402                                 XmlDictionaryReader.CreateDictionaryReader (envelopeReader),
403                                 maxSizeOfHeaders,
404                                 version);
405                 }
406
407                 // Core implementations of CreateMessage.
408
409                 static readonly AttributeCollection empty_attributes = new AttributeCollection ();
410
411                 // 9)
412                 public static Message CreateMessage (MessageVersion version,
413                         string action, BodyWriter body)
414                 {
415                         if (version == null)
416                                 throw new ArgumentNullException ("version");
417                         if (body == null)
418                                 throw new ArgumentNullException ("body");
419                         return new SimpleMessage (version, action, body, false, empty_attributes);
420                 }
421
422                 // 10)
423                 public static Message CreateMessage (MessageVersion version,
424                         string action)
425                 {
426                         if (version == null)
427                                 throw new ArgumentNullException ("version");
428                         return new EmptyMessage (version, action);
429                 }
430
431                 // 11)
432                 public static Message CreateMessage (
433                         XmlDictionaryReader envelopeReader,
434                         int maxSizeOfHeaders,
435                         MessageVersion version)
436                 {
437                         if (envelopeReader == null)
438                                 throw new ArgumentNullException ("envelopeReader");
439                         if (version == null)
440                                 throw new ArgumentNullException ("version");
441                         return new XmlReaderMessage (version,
442                                 envelopeReader, maxSizeOfHeaders);
443                 }
444
445                 #endregion
446         }
447 }