Click here to Skip to main content
15,884,629 members
Articles / Productivity Apps and Services / Biztalk

BizTalk ESB Exception Handling – Consuming WCF Services – Part III

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
3 Nov 2012CPOL6 min read 19.5K   151   6  
Managing exceptions when consuming WCF services via the BizTalk ESB Toolkit - Part III
using System;
using System.Diagnostics;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Text;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.XPath;

using XLANG = Microsoft.XLANGs.RuntimeTypes;
using System.Globalization;
using System.Xml.Xsl;

namespace ESB.ExceptionHandling.ServiceModel.Dispatcher
{
    /// <summary>
    /// This class can be customized to create a message inspector.
    /// </summary>
    public class ErrorHandler : IErrorHandler, IServiceBehavior
    {
        #region Public Constructors
        public ErrorHandler()
        {
        }
        #endregion

        #region Properties
        public string Map { get; set; }
        #endregion

        #region IServiceBehavior Members
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
            Trace.WriteLine("[ESB Dispatcher.ErrorHandler.AddBindingParameters]");
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            Trace.WriteLine("[ESB Dispatcher.ErrorHandler.ApplyDispatchBehavior]");
            Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.ApplyDispatchBehavior] Map: {0}", Map));

            ErrorHandler errorHandler = new ErrorHandler();

            errorHandler.Map = Map;

            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;

                if (channelDispatcher != null)
                {
                    channelDispatcher.ErrorHandlers.Add(errorHandler);
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            Trace.WriteLine("[ESB Dispatcher.ErrorHandler.Validate]");
        }
        #endregion

        #region IErrorHandler Members
        public bool HandleError(Exception error)
        {
            //Trace the error for debugging purposes
            Trace.WriteLine("[ESB Dispatcher.ErrorHandler.HandleError]");

            Exception exception = error;

            while (exception != null)
            {
                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.HandleError]   Exception type:    {0}", exception.GetType().FullName));
                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.HandleError]   Exception message: {0}", exception.Message));

                exception = exception.InnerException;
            }

            //WCF must NOT abort the session
            return true;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            Trace.WriteLine("[ESB Dispatcher.ErrorHandler.ProvideFault]");

            try
            {
                if (error != null)
                {
                    //Trace the error for debugging purposes
                    Exception exception = error;

                    while (exception != null)
                    {
                        Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.ProvideFault]   Exception type:    {0}", exception.GetType().FullName));
                        Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.ProvideFault]   Exception message: {0}", exception.Message));

                        exception = exception.InnerException;
                    }
                }

                //Create our custom fault message
                fault = CreateErrorMessage(fault, "An unexpected error occured", error);
            }
            catch (Exception exception)
            {
                Trace.WriteLine("[ESB Dispatcher.ErrorHandler.ProvideFault] ================ Major error ==============");
                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.ProvideFault] " + exception.GetType().FullName));
                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.ProvideFault] " + exception.Message));
                Trace.WriteLine("[ESB Dispatcher.ErrorHandler.ProvideFault] ===========================================");

                //Create our custom fault message
                fault = CreateErrorMessage(fault, "An unexpected error occured", error);
            }
            finally
            {
                Trace.WriteLine("[ESB Dispatcher.ErrorHandler.ProvideFault]");
            }
        }
        #endregion

        #region Private Methods
        private Message CreateErrorMessage(Message message, string errorMessage, Exception exception)
        {
            Trace.WriteLine("[ESB Dispatcher.ErrorHandler.CreateErrorMessage]");

            string action = null;

            if (message != null &&
                message.Headers != null)
                action = message.Headers.Action;

            string messageType = "http://schemas.xmlsoap.org/soap/envelope/#Fault";

            //Prepare to use our body writer to create a message
            StringBuilder stringBuilder = new StringBuilder();

            //Create the SOAP Fault body
            using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder))
            {
                using (XmlDictionaryWriter xmlDictionaryWriter = XmlDictionaryWriter.CreateDictionaryWriter(xmlWriter))
                {
                    //Create an instance of our body writer
                    BodyWriter bodyWriter = new ErrorBodyWriter("soap:Receiver",
                                                                exception.Message,
                                                                action,
                                                                errorMessage,
                                                                exception);

                    Trace.WriteLine("[ESB Dispatcher.ErrorHandler.CreateErrorMessage] Creating SOAP Fault");

                    //Execute the body writer
                    bodyWriter.WriteBodyContents(xmlDictionaryWriter);

                    //Flush the stream
                    xmlDictionaryWriter.Flush();
                }
            }

            Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.CreateErrorMessage] SOAP Fault message body: {0}", stringBuilder.ToString()));

            if (!string.IsNullOrEmpty(Map))
            {
                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.CreateErrorMessage] Executing Map: {0}", Map));

                //A map has been configured so let's execute it.
                //  Note: if executing the map fails in any way, we return the 
                //        SOAP Fault created above
                using (MemoryStream streamIn = new MemoryStream(Encoding.Unicode.GetBytes(stringBuilder.ToString())))
                {
                    //Perform the transform
                    Stream streamOut = TransformStream(streamIn, Map, ref messageType);

                    Trace.WriteLine("[ESB Dispatcher.ErrorHandler.CreateErrorMessage] Getting the new message body");

                    using (StreamReader streamReader = new StreamReader(streamOut))
                    {
                        //Get the contents of the transform stream
                        stringBuilder = new StringBuilder(streamReader.ReadToEnd());
                    }
                }
            }
            else
                Trace.WriteLine("[ESB Dispatcher.ErrorHandler.CreateErrorMessage] No map to execute");

            Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.CreateErrorMessage] Response message body: {0}", stringBuilder.ToString()));

            TextReader textReader = new StringReader(stringBuilder.ToString());

            Trace.WriteLine("[ESB Dispatcher.ErrorHandler.CreateErrorMessage] Creating the response message");

            //Convert the raw message (stringBuilder) to a Message
            Message replyMessage = Message.CreateMessage(MessageVersion.Default,
                                                         messageType,
                                                         XmlReader.Create(textReader));

            //Return the message
            return replyMessage;
        }

        private Stream TransformStream(Stream streamIn, string mapName, ref string messageType)
        {
            try
            {
                Trace.WriteLine("[ESB Dispatcher.ErrorHandler.TransformStream]");

                //Get the data type of the map
                Type type = Type.GetType(mapName);

                if (type == null)
                    throw new Exception(string.Format("The type for {0} was not a valid map type.", mapName));

                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.TransformStream] Map type: {0}", type.FullName));

                //Load the map
                XLANG.TransformMetaData transformMetaData = XLANG.TransformMetaData.For(type);
                //Get the schema information of the source message from the map
                XLANG.SchemaMetadata schemaMetadataSource = transformMetaData.SourceSchemas[0];
                //Get the schema type of the soruce message in the map
                string schemaName = schemaMetadataSource.SchemaName;

                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.TransformStream] Map source schema: {0}", schemaName));

                //Check that the message type of the input stream matches the message type of the
                //  source schema in the map
                if (!string.Equals(messageType, schemaName, StringComparison.InvariantCultureIgnoreCase))
                {
                    Trace.WriteLine("[ESB Dispatcher.ErrorHandler.TransformStream] Map source schema not the same as the given message type");

                    //The message type is not the same as the map source schema so don't
                    //  run the map
                    return streamIn;
                }

                //Get the schema information of the target message from the map
                XLANG.SchemaMetadata schemaMetadataTarget = transformMetaData.TargetSchemas[0];
                //Get the message type of the target of the map
                messageType = schemaMetadataTarget.SchemaName;

                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.TransformStream] Map target schema: {0}", messageType));

                //Prepare the input stream
                XPathDocument input = new XPathDocument(streamIn);
                XslTransform transform = transformMetaData.Transform;

                //Create the output stream
                Stream streamOut = new MemoryStream();

                Trace.WriteLine("[ESB Dispatcher.ErrorHandler.TransformStream] About to map");

                //Perform the transform
                transform.Transform(input, transformMetaData.ArgumentList, streamOut);

                //Finalise the output stream
                streamOut.Flush();
                streamOut.Seek(0L, SeekOrigin.Begin);

                Trace.WriteLine("[ESB Dispatcher.ErrorHandler.TransformStream] Mapping done");

                return streamOut;
            }
            catch (Exception exception)
            {
                Trace.WriteLine(string.Format("[ESB Dispatcher.ErrorHandler.TransformStream] An error occured executing the map: {0}", exception.Message));

                return streamIn;
            }
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Digging Dog Ltd
New Zealand New Zealand
I have almost 20 years commercial software development using a number of languages from C++ to C#/VB. My main focus has been in the Microsoft space and, since the early 2000's, most of this has been in the web or integration arena. Over the past few years I have been focusing on building business solutions using a variety of products such as BizTalk and Share Point.
In terms of SDLC's, I have experience with various methodologies such as Agile, RUP, Iterative and Waterfall.
Lastly, the roles I have had in the last few years have given me the opportunity to gain a lot of experience as a consultant. Since, over the last 18 years, I have worked with many customers this has further given me the chance to enjoy many and varying challenges that have helped me grow in my career.
Today, I spend a lot of time talking, designing, documenting and mentoring team members. I also spend quite a bit of time authoring software and find the combination a perfect fit for me.

Specialties
Consultancy, Solution architecture, Solution design, Project costing and tracking, Team lead, Software development, C#, VB, ASP.NET, HTML, DHTML, JavaScript, BizTalk, SharePoint

Comments and Discussions