Custom Disassembler
From: Sam Loveridge (sloveridge_at_adelaidebank.com.au)
Date: 01/03/05
- Previous message: Jon Flanders: "Re: Visual Studio 2003 Biztalk projects"
- Next in thread: WenJun Zhang[msft]: "RE: Custom Disassembler"
- Reply: WenJun Zhang[msft]: "RE: Custom Disassembler"
- Messages sorted by: [ date ] [ thread ]
Date: Tue, 4 Jan 2005 07:30:51 +1030
Hi. I'm trying unsuccessfully to implement a custom disassembler. The
inbound message, and desired message formats are as follows:
Inbound:
FieldName:Value
FieldName:Value
FieldName:Value
Required Disassembled Format:
<message>
<fieldName>Value</fieldName>
<fieldName>Value</fieldName>
<fieldName>Value</fieldName>
</message>
When the inbound message is placed in the receive location it is picked up
and then can be seen in HAT as suspended. When saving the message from HAT
it can be seen in the original format, so I suspect the disassembler is
either not functioning correctly, or not fired at all. Looking in the event
viewer the following error can be seen:
"The data at the root level is invalid. Line 1, position 1."
I have read that this may be due to seekable streams and have tried various
suggestions that I've read on various posts but with no success, so I've
decided to post the original code to my disassembler below. Any help is
greatly appreciated. The code I am using in my disassembler is as follows
(apologies for the large post, please cut code below from replies to reduce
bloated messages in thread):
<code>
Imports Microsoft.Biztalk.Component.Interop
Imports Microsoft.Biztalk.Message.Interop
Imports System
Imports System.Collections
Imports System.Diagnostics
Imports System.IO
Imports System.Text
Imports System.Xml
<ComponentCategory(CategoryTypes.CATID_PipelineComponent), _
ComponentCategory(CategoryTypes.CATID_DisassemblingParser), _
System.Runtime.InteropServices.Guid("B60F6801-C07E-45f7-A9C7-E8797671D710")>
_
Public Class MyDisassembler
Implements IBaseComponent, _
IComponent, _
IComponentUI, _
IPersistPropertyBag, _
IDisassemblerComponent
' Constant pipeline component values:
Private Const ComponentName As String = "MyDisassembler"
Private Const ComponentDescription As String = "My Custom Receive Pipeline
Disassembler."
Private Const ComponentVersion As String = "1.0.0.0"
' Private Constant Declarations
Private Const NameValueSeparator As String = ":"
Private Const RootMessageElementName As String = "message"
' Private Variable Declarations
Private _nameValues As Hashtable
Private _originalMsgContext As IBaseMessageContext ' Original's message
context.
Private _outputFile As FileStream
Public Sub New()
' Create a trace listener for the event log.
Dim myTraceListener As New EventLogTraceListener("MyDisassemblerTrace")
' Add the event log trace listener to the collection.
Trace.Listeners.Add(myTraceListener)
End Sub
#Region "Microsoft.Biztalk.Component.Interop.IBaseComponent Implementation"
Public ReadOnly Property Description() As String _
Implements Microsoft.Biztalk.Component.Interop.IBaseComponent.Description
Get
Return ComponentDescription
End Get
End Property
Public ReadOnly Property Name() As String _
Implements Microsoft.Biztalk.Component.Interop.IBaseComponent.Name
Get
Return ComponentName
End Get
End Property
Public ReadOnly Property Version() As String _
Implements Microsoft.Biztalk.Component.Interop.IBaseComponent.Version
Get
Return ComponentVersion
End Get
End Property
#End Region
#Region "Microsoft.Biztalk.Component.Interop.IComponent Implementation"
Public Function Execute( _
ByVal pContext As
Microsoft.Biztalk.Component.Interop.IPipelineContext, _
ByVal pInMsg As
Microsoft.Biztalk.Message.Interop.IBaseMessage) As
Microsoft.Biztalk.Message.Interop.IBaseMessage _
Implements Microsoft.Biztalk.Component.Interop.IComponent.Execute
'
End Function
#End Region
#Region "Microsoft.Biztalk.Component.Interop.IComponentUI Implementation"
Public ReadOnly Property Icon() As System.IntPtr _
Implements Microsoft.Biztalk.Component.Interop.IComponentUI.Icon
Get
' No icon associated with this pipeline component:
Return IntPtr.Zero
End Get
End Property
Public Function Validate(ByVal projectSystem As Object) As
System.Collections.IEnumerator _
Implements Microsoft.Biztalk.Component.Interop.IComponentUI.Validate
Dim validationResult As System.Collections.Specialized.StringCollection
= New System.Collections.Specialized.StringCollection
Return DirectCast(validationResult.GetEnumerator(), IEnumerator)
End Function
#End Region
#Region "Microsoft.Biztalk.Component.Interop.IPersistPropertyBag
Implementation"
Public Sub GetClassID(ByRef classID As System.Guid) _
Implements
Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.GetClassID
classID = Me.GetType().GUID
End Sub
Public Sub InitNew() _
Implements Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.InitNew
'
End Sub
Public Sub Load(ByVal propertyBag As
Microsoft.Biztalk.Component.Interop.IPropertyBag, ByVal errorLog As Integer)
_
Implements Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.Load
'
End Sub
Public Sub Save(ByVal propertyBag As
Microsoft.Biztalk.Component.Interop.IPropertyBag, ByVal clearDirty As
Boolean, ByVal saveAllProperties As Boolean) _
Implements Microsoft.Biztalk.Component.Interop.IPersistPropertyBag.Save
'
End Sub
#End Region
#Region "Microsoft.Biztalk.Component.Interop.IDisassemblerComponent
Implementation"
Public Sub Disassemble(ByVal pContext As
Microsoft.Biztalk.Component.Interop.IPipelineContext, ByVal pInMsg As
Microsoft.Biztalk.Message.Interop.IBaseMessage) _
Implements
Microsoft.Biztalk.Component.Interop.IDisassemblerComponent.Disassemble
Trace.WriteLine(String.Format("{0} : Entering Disassemble",
DateTime.Now().ToLongTimeString()))
Dim outputMessage As XmlDocument = Nothing
' Initialise the field name value collection
_nameValues = New Hashtable
Trace.WriteLine(String.Format("{0} : pInMsg.PartCount = {1}",
DateTime.Now().ToLongTimeString(), pInMsg.PartCount))
' Consider this document only if it has parts
If (pInMsg.PartCount >= 1) Then
Dim bodyPart As IBaseMessagePart = pInMsg.BodyPart
Trace.WriteLine(String.Format("{0} : bodyPart Is Nothing? {1}",
DateTime.Now().ToLongTimeString(), (bodyPart Is Nothing).ToString()))
' Clone the message context so we can associate it with the new
message later
_originalMsgContext = PipelineUtil.CloneMessageContext(pInMsg.Context)
' Consider this only if this is not null
If (Not bodyPart Is Nothing) Then
Trace.WriteLine(String.Format("{0} : About to reference bodyPart
Stream ...", DateTime.Now().ToLongTimeString()))
Dim docStream As Stream = bodyPart.GetOriginalDataStream()
Dim lineRead As String = Nothing
Dim lineNum As Integer = 1
' We got the original data stream (not a copy) so rewind the stream
docStream.Seek(0, SeekOrigin.Begin)
Trace.WriteLine(String.Format("{0} : Creating StreamReader from
bodyPart stream to read each line in input data",
DateTime.Now().ToLongTimeString()))
' Use a stream reader to facilitate reading line by line
Dim streamReader As New streamReader(docStream)
' Parse all tables and all values in betwen them
lineRead = streamReader.ReadLine()
While (Not lineRead Is Nothing)
Trace.WriteLine(String.Format("{0} : lineRead = '{1}'",
DateTime.Now().ToLongTimeString(), lineRead))
' TODO - Do we need to associate schema here?
' If so then we can determine which schema by HMT:150, HMV:001
' Make sure we don't process an empty line
If lineRead.Trim().Length > 0 Then
' Process the field definition and value
' Make sure the name/value separator exists in line
If (lineRead.IndexOf(NameValueSeparator) > 0) Then
Try
' Read name & value details from line
Dim nameValue() As String =
lineRead.Split(NameValueSeparator.ToCharArray())
Dim fieldName As String = nameValue(0).Trim().ToLower()
Dim value As String = nameValue(1).Trim()
Trace.WriteLine(String.Format("{0} : Adding data to
collection with :: fieldName='{1}' and value='{2}'",
DateTime.Now().ToLongTimeString(), fieldName, value))
' Add the field to our collection
_nameValues.Add(fieldName, value)
Catch ex As Exception
' Invalid line - ignore
' (Invalid messages will be picked up in the validate
' stage of the receive pipeline)
End Try
End If
End If
' Get the next line
lineRead = streamReader.ReadLine()
lineNum += 1
End While
End If
End If
Trace.WriteLine(String.Format("{0} : Exiting Disassemble",
DateTime.Now().ToLongTimeString()))
End Sub
Public Function GetNext(ByVal pContext As
Microsoft.Biztalk.Component.Interop.IPipelineContext) As
Microsoft.Biztalk.Message.Interop.IBaseMessage _
Implements
Microsoft.Biztalk.Component.Interop.IDisassemblerComponent.GetNext
Trace.WriteLine(String.Format("{0} : Entering GetNext",
DateTime.Now().ToLongTimeString()))
Dim newMsg As IBaseMessage = Nothing
' We have something to return
If ((Not _nameValues Is Nothing) AndAlso (_nameValues.Count > 0)) Then
' Creates a new message
newMsg = pContext.GetMessageFactory().CreateMessage()
' Prepare the message: We combine all tables into one XML document
' like the following:
' <message>
' <name>value</name>
' [...]
' </message>
' Get a new stream so we can prepare the XML and connect it to an
XMLTextWriter
Dim outputStream As New MemoryStream
Dim xmlWriter As New XmlTextWriter(outputStream, Encoding.UTF8)
' Apply the appropriate formatting and start the XML document
xmlWriter.Formatting = Formatting.Indented
xmlWriter.WriteStartDocument(True)
' Write the root element
xmlWriter.WriteStartElement(String.Empty, RootMessageElementName,
String.Empty)
' Emit all name/value pairs
For Each key As Object In _nameValues.Keys
xmlWriter.WriteElementString(key.ToString(),
_nameValues(key).ToString())
Next
' Close the document
xmlWriter.WriteEndElement()
xmlWriter.WriteEndDocument()
xmlWriter.Flush()
' Rewind the stream so it is ready for use by the messaging engine
outputStream.Seek(0, SeekOrigin.Begin)
' Add one part to it. This part will be the "body"
Dim newMsgPart As IBaseMessagePart =
pContext.GetMessageFactory().CreateMessagePart()
newMsgPart.Charset = "UTF-8"
newMsgPart.ContentType = "text/xml"
newMsgPart.Data = outputStream
newMsg.AddPart("body", newMsgPart, True)
' Copy the original message context to the newly created message
For iProp As Integer = 0 To
(Convert.ToInt32(_originalMsgContext.CountProperties) - 1)
Dim strName As String
Dim strNSpace As String
Dim val As Object = _originalMsgContext.ReadAt(iProp, strName,
strNSpace)
' If the property has been promoted, respect the settings
If (_originalMsgContext.IsPromoted(strName, strNSpace)) Then
newMsg.Context.Promote(strName, strNSpace, val)
Else
newMsg.Context.Write(strName, strNSpace, val)
End If
Next
' There is a new stream so let's make it visible to the
resourceTracker
pContext.ResourceTracker.AddResource(outputStream)
' We returned this message so drop our internal table representation
_nameValues = Nothing
End If
Trace.WriteLine(String.Format("{0} : newMsg Is Nothing? {1}",
DateTime.Now().ToLongTimeString(), (newMsg Is Nothing).ToString()))
Trace.WriteLine(String.Format("{0} : Exiting GetNext",
DateTime.Now().ToLongTimeString()))
Return newMsg
End Function
#End Region
End Class
</code>
- Previous message: Jon Flanders: "Re: Visual Studio 2003 Biztalk projects"
- Next in thread: WenJun Zhang[msft]: "RE: Custom Disassembler"
- Reply: WenJun Zhang[msft]: "RE: Custom Disassembler"
- Messages sorted by: [ date ] [ thread ]
Relevant Pages
|