MessageFault.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. //
  2. // MessageFault.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2005-2006 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. namespace System.ServiceModel.Channels
  34. {
  35. public abstract class MessageFault
  36. {
  37. // type members
  38. public static MessageFault CreateFault (Message message, int maxBufferSize)
  39. {
  40. if (message.Version.Envelope == EnvelopeVersion.Soap11)
  41. return CreateFault11 (message, maxBufferSize);
  42. else if (message.Version.Envelope == EnvelopeVersion.Soap12)
  43. return CreateFault12 (message, maxBufferSize);
  44. throw new InvalidOperationException ("The input message is not a SOAP envelope.");
  45. }
  46. static MessageFault CreateFault11 (Message message, int maxBufferSize)
  47. {
  48. FaultCode fc = null;
  49. FaultReason fr = null;
  50. object details = null;
  51. XmlDictionaryReader r = message.GetReaderAtBodyContents ();
  52. r.ReadStartElement ("Fault", message.Version.Envelope.Namespace);
  53. r.MoveToContent ();
  54. while (r.NodeType != XmlNodeType.EndElement) {
  55. switch (r.LocalName) {
  56. case "faultcode":
  57. fc = ReadFaultCode11 (r);
  58. break;
  59. case "faultstring":
  60. fr = new FaultReason (r.ReadElementContentAsString());
  61. break;
  62. case "detail":
  63. //BUGBUG: Handle children of type other than ExceptionDetail, in order to comply with
  64. // FaultContractAttribute.
  65. r.ReadStartElement ();
  66. r.MoveToContent();
  67. details = new DataContractSerializer (typeof (ExceptionDetail)).ReadObject (r);
  68. break;
  69. case "faultactor":
  70. default:
  71. throw new NotImplementedException ();
  72. }
  73. r.MoveToContent ();
  74. }
  75. r.ReadEndElement ();
  76. if (details == null)
  77. return CreateFault (fc, fr);
  78. return CreateFault (fc, fr, details);
  79. }
  80. static MessageFault CreateFault12 (Message message, int maxBufferSize)
  81. {
  82. FaultCode fc = null;
  83. FaultReason fr = null;
  84. XmlDictionaryReader r = message.GetReaderAtBodyContents ();
  85. r.ReadStartElement ("Fault", message.Version.Envelope.Namespace);
  86. r.MoveToContent ();
  87. while (r.NodeType != XmlNodeType.EndElement) {
  88. switch (r.LocalName) {
  89. case "Code":
  90. fc = ReadFaultCode12 (r, message.Version.Envelope.Namespace);
  91. break;
  92. case "Reason":
  93. fr = ReadFaultReason12 (r, message.Version.Envelope.Namespace);
  94. break;
  95. default:
  96. throw new NotImplementedException ();
  97. }
  98. }
  99. r.ReadEndElement ();
  100. return CreateFault (fc, fr);
  101. }
  102. static FaultCode ReadFaultCode11 (XmlDictionaryReader r)
  103. {
  104. FaultCode subcode = null;
  105. XmlQualifiedName value = XmlQualifiedName.Empty;
  106. if (r.IsEmptyElement)
  107. throw new ArgumentException ("Fault Code is mandatory in SOAP fault message.");
  108. r.ReadStartElement ("faultcode");
  109. r.MoveToContent ();
  110. while (r.NodeType != XmlNodeType.EndElement) {
  111. if (r.NodeType == XmlNodeType.Element)
  112. subcode = ReadFaultCode11 (r);
  113. else
  114. value = (XmlQualifiedName) r.ReadContentAs (typeof (XmlQualifiedName), r as IXmlNamespaceResolver);
  115. r.MoveToContent ();
  116. }
  117. r.ReadEndElement ();
  118. return new FaultCode (value.Name, value.Namespace, subcode);
  119. }
  120. static FaultCode ReadFaultCode12 (XmlDictionaryReader r, string ns)
  121. {
  122. FaultCode subcode = null;
  123. XmlQualifiedName value = XmlQualifiedName.Empty;
  124. if (r.IsEmptyElement)
  125. throw new ArgumentException ("Value element is mandatory in SOAP fault code.");
  126. r.ReadStartElement ("Code", ns);
  127. r.MoveToContent ();
  128. while (r.NodeType != XmlNodeType.EndElement) {
  129. switch (r.LocalName) {
  130. case "Subcode":
  131. subcode = ReadFaultCode12 (r, ns);
  132. break;
  133. case "Value":
  134. value = (XmlQualifiedName) r.ReadElementContentAs (typeof (XmlQualifiedName), r as IXmlNamespaceResolver, "Value", ns);
  135. break;
  136. default:
  137. throw new ArgumentException ();
  138. }
  139. r.MoveToContent ();
  140. }
  141. r.ReadEndElement ();
  142. return new FaultCode (value.Name, value.Namespace, subcode);
  143. }
  144. static FaultReason ReadFaultReason12 (XmlDictionaryReader r, string ns)
  145. {
  146. List<FaultReasonText> l = new List<FaultReasonText> ();
  147. if (r.IsEmptyElement)
  148. throw new ArgumentException ("One or more Text element is mandatory in SOAP fault reason text.");
  149. r.ReadStartElement ("Code", ns);
  150. for (r.MoveToContent ();
  151. r.NodeType != XmlNodeType.EndElement;
  152. r.MoveToContent ()) {
  153. string lang = r.GetAttribute ("lang", "http://www.w3.org/XML/1998/namespace");
  154. l.Add (new FaultReasonText (r.ReadElementContentAsString ("Text", ns), lang));
  155. }
  156. return new FaultReason (l);
  157. }
  158. public static MessageFault CreateFault (FaultCode code,
  159. string reason)
  160. {
  161. return CreateFault (code, new FaultReason (reason));
  162. }
  163. public static MessageFault CreateFault (FaultCode code,
  164. FaultReason reason)
  165. {
  166. return new SimpleMessageFault (code, reason,
  167. false, null, null, null, null);
  168. }
  169. public static MessageFault CreateFault (FaultCode code,
  170. FaultReason reason, object detail)
  171. {
  172. return new SimpleMessageFault (code, reason,
  173. true, detail, new DataContractSerializer (detail.GetType ()), null, null);
  174. }
  175. public static MessageFault CreateFault (FaultCode code,
  176. FaultReason reason, object detail,
  177. XmlObjectSerializer formatter)
  178. {
  179. return new SimpleMessageFault (code, reason, true,
  180. detail, formatter, String.Empty, String.Empty);
  181. }
  182. public static MessageFault CreateFault (FaultCode code,
  183. FaultReason reason, object detail,
  184. XmlObjectSerializer formatter, string actor)
  185. {
  186. return new SimpleMessageFault (code, reason,
  187. true, detail, formatter, actor, String.Empty);
  188. }
  189. public static MessageFault CreateFault (FaultCode code,
  190. FaultReason reason, object detail,
  191. XmlObjectSerializer formatter, string actor, string node)
  192. {
  193. return new SimpleMessageFault (code, reason,
  194. true, detail, formatter, actor, node);
  195. }
  196. // pretty simple implementation class
  197. internal class SimpleMessageFault : MessageFault
  198. {
  199. bool has_detail;
  200. string actor, node;
  201. FaultCode code;
  202. FaultReason reason;
  203. object detail;
  204. XmlObjectSerializer formatter;
  205. public SimpleMessageFault (FaultCode code,
  206. FaultReason reason, bool has_detail,
  207. object detail, XmlObjectSerializer formatter,
  208. string actor, string node)
  209. : this (code, reason, detail, formatter, actor, node)
  210. {
  211. this.has_detail = has_detail;
  212. }
  213. public SimpleMessageFault (FaultCode code,
  214. FaultReason reason,
  215. object detail, XmlObjectSerializer formatter,
  216. string actor, string node)
  217. {
  218. if (code == null)
  219. throw new ArgumentNullException ("code");
  220. if (reason == null)
  221. throw new ArgumentNullException ("reason");
  222. this.code = code;
  223. this.reason = reason;
  224. this.detail = detail;
  225. this.formatter = formatter;
  226. this.actor = actor;
  227. this.node = node;
  228. }
  229. public override string Actor {
  230. get { return actor; }
  231. }
  232. public override FaultCode Code {
  233. get { return code; }
  234. }
  235. public override bool HasDetail {
  236. // it is not simply "detail != null" since
  237. // null detail could become <ms:anyType xsi:nil="true" />
  238. get { return has_detail; }
  239. }
  240. public override string Node {
  241. get { return node; }
  242. }
  243. public override FaultReason Reason {
  244. get { return reason; }
  245. }
  246. protected override XmlDictionaryReader OnGetReaderAtDetailContents ()
  247. {
  248. // FIXME: use XmlObjectSerializer
  249. return base.OnGetReaderAtDetailContents ();
  250. }
  251. protected override void OnWriteDetailContents (XmlDictionaryWriter writer)
  252. {
  253. formatter.WriteObject (writer, detail);
  254. }
  255. public object Detail {
  256. get { return detail; }
  257. }
  258. }
  259. // instance members
  260. protected MessageFault ()
  261. {
  262. }
  263. [MonoTODO ("is this true?")]
  264. public virtual string Actor {
  265. get { return String.Empty; }
  266. }
  267. public abstract FaultCode Code { get; }
  268. public abstract bool HasDetail { get; }
  269. [MonoTODO ("is this true?")]
  270. public virtual string Node {
  271. get { return String.Empty; }
  272. }
  273. public abstract FaultReason Reason { get; }
  274. public T GetDetail<T> ()
  275. {
  276. return GetDetail<T> (new DataContractSerializer (typeof (T)));
  277. }
  278. public T GetDetail<T> (XmlObjectSerializer formatter)
  279. {
  280. if (!HasDetail)
  281. throw new InvalidOperationException ("This message does not have details.");
  282. return (T) formatter.ReadObject (GetReaderAtDetailContents ());
  283. }
  284. public XmlDictionaryReader GetReaderAtDetailContents ()
  285. {
  286. return OnGetReaderAtDetailContents ();
  287. }
  288. public void WriteTo (XmlDictionaryWriter writer,
  289. EnvelopeVersion version)
  290. {
  291. writer.WriteStartElement ("Fault", version.Namespace);
  292. WriteFaultCode (writer, version, Code);
  293. WriteReason (writer, version);
  294. if (HasDetail)
  295. OnWriteDetail (writer, version);
  296. writer.WriteEndElement ();
  297. }
  298. private void WriteFaultCode (XmlDictionaryWriter writer,
  299. EnvelopeVersion version, FaultCode code)
  300. {
  301. if (version == EnvelopeVersion.Soap11) {
  302. writer.WriteStartElement ("", "faultcode", version.Namespace);
  303. if (code.Namespace.Length > 0)
  304. writer.WriteXmlnsAttribute ("a", code.Namespace);
  305. writer.WriteQualifiedName (code.Name, code.Namespace);
  306. writer.WriteEndElement ();
  307. } else { // Soap12
  308. writer.WriteStartElement ("Code", version.Namespace);
  309. writer.WriteStartElement ("Value", version.Namespace);
  310. if (code.Namespace.Length > 0)
  311. writer.WriteXmlnsAttribute ("a", code.Namespace);
  312. writer.WriteQualifiedName (code.Name, code.Namespace);
  313. if (code.SubCode != null)
  314. WriteFaultCode (writer, version, code.SubCode);
  315. writer.WriteEndElement ();
  316. writer.WriteEndElement ();
  317. }
  318. }
  319. private void WriteReason (XmlDictionaryWriter writer,
  320. EnvelopeVersion version)
  321. {
  322. if (version == EnvelopeVersion.Soap11) {
  323. foreach (FaultReasonText t in Reason.Translations) {
  324. writer.WriteStartElement ("", "faultstring", version.Namespace);
  325. if (t.XmlLang != null)
  326. writer.WriteAttributeString ("xml", "lang", null, t.XmlLang);
  327. writer.WriteString (t.Text);
  328. writer.WriteEndElement ();
  329. }
  330. } else { // Soap12
  331. writer.WriteStartElement ("Reason", version.Namespace);
  332. foreach (FaultReasonText t in Reason.Translations) {
  333. writer.WriteStartElement ("Text", version.Namespace);
  334. if (t.XmlLang != null)
  335. writer.WriteAttributeString ("xml", "lang", null, t.XmlLang);
  336. writer.WriteString (t.Text);
  337. writer.WriteEndElement ();
  338. }
  339. writer.WriteEndElement ();
  340. }
  341. }
  342. public void WriteTo (XmlWriter writer, EnvelopeVersion version)
  343. {
  344. WriteTo (XmlDictionaryWriter.CreateDictionaryWriter (
  345. writer), version);
  346. }
  347. protected virtual XmlDictionaryReader OnGetReaderAtDetailContents ()
  348. {
  349. MemoryStream ms = new MemoryStream ();
  350. using (XmlDictionaryWriter dw =
  351. XmlDictionaryWriter.CreateDictionaryWriter (
  352. XmlWriter.Create (ms))) {
  353. OnWriteDetailContents (dw);
  354. }
  355. ms.Seek (0, SeekOrigin.Begin);
  356. return XmlDictionaryReader.CreateDictionaryReader (
  357. XmlReader.Create (ms));
  358. }
  359. protected virtual void OnWriteDetail (XmlDictionaryWriter writer, EnvelopeVersion version)
  360. {
  361. OnWriteStartDetail (writer, version);
  362. OnWriteDetailContents (writer);
  363. writer.WriteEndElement ();
  364. }
  365. protected virtual void OnWriteStartDetail (XmlDictionaryWriter writer, EnvelopeVersion version)
  366. {
  367. if (version == EnvelopeVersion.Soap11)
  368. writer.WriteStartElement ("detail", String.Empty);
  369. else // Soap12
  370. writer.WriteStartElement ("Detail", version.Namespace);
  371. }
  372. protected abstract void OnWriteDetailContents (XmlDictionaryWriter writer);
  373. }
  374. }