Implementando IProbeMessage con Documentos XML

En muchas ocasiones tenemos inconvenientes o problemas cuando recibimos mensajes sobre puertos previamente configurados que no corresponden a ninguna especificación de documento desplegado en la plataforma de BizTalk. Cuando esto sucede, simplemente la plataforma lanza un error sobre el registro de eventos de Windows (Eventlog.exe) de la siguiente forma:

There was a failure executing the receive pipeline: "Scorpio.BizTalk.CustomPipeline.Pipelines.RcvPipeline"
Source: "XML disassembler"
Receive Location: "D:\biztalk\CustomPipeline\in\*.*"
Reason: Finding document specification by message type "https://ValSchemas.XmlError#InfoError" failed. Verify that the schema is deployed properly.

Y el mensaje queda en estado Suspende imposibilitándose la ejecución de nuestro proceso de integración y sin poder dar algún estado de alerta o notificación acerca del error en la lógica de integración a los otros procesos relacionados. Una forma de solucionar este problema es implementado un desensamblado (Disassembler) que pueda ser utilizado desde una tubería (Pipeline) el cual pueda validar el mensaje y en caso de ocurrir algún error generar un nuevo mensaje de error que será entregado al proceso en ves del mensaje original.

Para poder implementar lo anteriormente descrito debemos de crear una clase que derive de las interfaces IProbeMessage y IDisassemblerComponent, al implementar estas clases es necesario redefinir 3 métodos: GetNext(), Disassemble() y Probe().

  • GetNext() : este método retorna cada uno de los mensajes en el documento original.
  • Disassemble() : este método desensambla el mensaje original.
  • Probe() : este método se ejecuta como prueba para realizar alguna validación.

En orden secuencial el primer método que se ejecuta es Probe(), luego Disassemble() y por último GetNext(), todo el proceso de validación la realizaremos en el método Probe(). Inicialmente este método acepta dos parámetros los cuales contienen el contexto actual de la ejecución de la tubería (Pipeline) y el segundo que tiene todo el contenido del mensaje, el tipo de lo parámetros son IPipelineContext y IBaseMessage respectivamente.

El primer proceso dentro del método es recuperar la especificación del documento, esto se realiza concatenando el nombre de espacio (targetNamespace) con "#" y con el nombre del nodo raiz del contenido Xml que se encuentra en un objeto Stream en la propiedad IBaseMessage.BodyPart.Data. Ya teniendo la especificación del documento podemos obtener los esquemas relacionados al mismo y posteriormente con el objeto XmlValidatingReader validaremos los elementos del esquema.

XmlTextReader t = new XmlTextReader(pInMsg.BodyPart.Data);
string messageType;

if

( XmlNodeType.Element == t.MoveToContent() )
{
   messageType = String.Format("{0}#{1}", t.NamespaceURI, t.Name.Replace(t.Prefix + ":", ""));
   IDocumentSpec docSpec = pContext.GetDocumentSpecByType(messageType);
   XmlValidatingReader xvr = new XmlValidatingReader(pInMsg.BodyPart.Data, XmlNodeType.Element, null);
   xvr.Schemas.Add(docSpec.GetSchemaCollection());
   while(xvr.Read());
}

Si en este proceso de validación pudiese ocurrir, simplemente ponemos todo el codigo en un try{}catch{} el cual caputara el error y lo incluira en un nuevo mensaje que reemplazara el mensaje actual. Para ello es necesario remover el IBaseMessagePart deseado y reemplazarlo por una definición Xml con el formato de error, en nuestro caso el correspondiente al body. Algo como:

pInMsg.RemovePart("body");
string err = "<ns0:Error xmlns:ns0=\"https://Scorpio.BizTalk.CustomPipeline.Schemas.XmlError\"> <Date>Hoy</Date><Desc>" + e.Message + "</Desc></ns0:Error>";
byte[] data = System.Text.Encoding.UTF8.GetBytes(err);
IBaseMessagePart part = pContext.GetMessageFactory().CreateMessagePart();
part.Data = new MemoryStream(data, 0, data.Length);
pInMsg.AddPart("body", part, true);
pInMsg.BodyPart.Data.Position = 0;
IDocumentSpec docSpec = pContext.GetDocumentSpecByType(https://Scorpio.BizTalk.CustomPipeline.Schemas.XmlError#Error);
pInMsg.Context.Write("DocumentSpecName", "https://schemas.microsoft.com/BizTalk/2003/xmlnorm-properties", docSpec.DocSpecStrongName);

Para los otros dos métodos delegaremos las operaciones al desensamblado de Xml, para ello utilizaremos el código:

public

IBaseMessage GetNext(IPipelineContext pContext)
{
   return dasm.GetNext(pContext);
}

public

void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg)
{
   dasm.Disassemble(pContext, pInMsg);
}

Donde la variable dasm es de tipo XmlDasmComp.

Nota: igual que en estos métodos también se hubiese podido delegar la operación Probe() al desensamblado XML, pero este método no valida toda la estructura real del mensaje referente a un esquema.

El anterior ejemplo solo valida la estructura de los elementos del mensaje ya que asi se especifico en el objeto XmlValidatingReader, pero se puede ampliar y enriquecer aun mas para validaciones mas especificas. También, en el ejemplo solo se trabajo con la parte body del mensaje original, pero en un mensaje pueden venir muchas partes (IBaseMessagePart) las cuales serán necesarias de tratar una a una.

Este es un pequeño ejemplo de cómo podemos implementar nuestro propio desensamblado que genere automáticamente un mensaje de error. En forma adicional Martinjs (MVP) ha trabajado en un proyecto llamado XmlCompleteValidator, pueden echarle un vistazo ya que esta bastante bueno y consiste en validar mensajes Xml.

Aquí esta el código fuente y el esquema de error.

Autor: Carlos Medina

Este mensaje se proporciona "como está" sin garantías de ninguna clase, y no otorga ningún derecho