BizTalk 2009 Archive Incoming Messages

At one of our customers we want to be able to archive all messages that come in to BizTalk 2009. I created a pipeline component that does this, where archiving can be turned on or off from the BizTalk Administrator Console. Here is the code to do this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
using System;
using System.IO;
using System.Text;
using System.ComponentModel;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
 
namespace Company.BizTalk.PipelineComponents
{
    /// <summary>
    /// Archive an incoming message.
    /// </summary>
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [System.Runtime.InteropServices.Guid("CFBF459C-01FC-49B8-903B-2DE65A000FF6")]
    [ComponentCategory(CategoryTypes.CATID_Decoder)]
    public class ArchivingComponentIncoming : 
        Microsoft.BizTalk.Component.Interop.IComponent, IBaseComponent, 
        IPersistPropertyBag, IComponentUI
    {
 
        #region Properties
 
        /// <summary>
        /// Boolean indicating if we want to archive the messages coming through this 
        /// pipeline.
        /// </summary>
        private bool _doArchiving;
 
        /// <summary>
        /// Boolean indicating if we want to archive the messages coming through this 
        /// pipeline.
        /// </summary>
        public bool DoArchiving
        {
            get
            {
                return _doArchiving;
            }
            set
            {
                _doArchiving = value;
            }
        }
 
        /// <summary>
        /// Location where we want to archive the messages.
        /// </summary>
        private string _archivePath;
 
        /// <summary>
        /// Location where we want to archive the messages.
        /// </summary>
        public string ArchivePath
        {
            get
            {
                return _archivePath;
            }
            set
            {
                _archivePath = value;
            }
        }
 
        #endregion
 
 
        #region IBaseComponent members
 
        /// <summary>
        /// Name of the component
        /// </summary>
        [Browsable(false)]
        public string Name
        {
            get
            {
                return "ArchivingComponentIncoming";
            }
        }
 
        /// <summary>
        /// Version of the component
        /// </summary>
        [Browsable(false)]
        public string Version
        {
            get
            {
                return "1.0.0.0";
            }
        }
 
        /// <summary>
        /// Description of the component
        /// </summary>
        [Browsable(false)]
        public string Description
        {
            get
            {
                return "Component used to archive incoming messages.";
            }
        }
 
        #endregion
 
        #region IPersistPropertyBag members
 
        /// <summary>
        /// Gets class ID of component for usage from unmanaged code.
        /// </summary>
        /// <param name="classid">
        /// Class ID of the component
        /// </param>
        public void GetClassID(out System.Guid classid)
        {
            classid = new System.Guid("CFBF459C-01FC-49B8-903B-2DE65A000FF6");
        }
 
        /// <summary>
        /// Not needed.
        /// </summary>
        public void InitNew()
        {
        }
 
        /// <summary>
        /// Loads configuration properties for the component.
        /// </summary>
        /// <param name="pb">Configuration property bag.</param>
        /// <param name="errlog">Error status.</param>
        public virtual void Load(Microsoft.BizTalk.Component.Interop.IPropertyBag pb,
            int errlog)
        {
            object val = null;
 
            // Get configuration property for indication if we want to archive
            val = this.ReadPropertyBag(pb, "DoArchiving");
 
            if (val != null)
            {
                this._doArchiving = ((bool)(val));
            }
 
            // Get configuration property for path where we want to archive
            val = this.ReadPropertyBag(pb, "ArchivePath");
 
            if (val != null)
            {
                this._archivePath = ((string)(val));
            }
        }
 
        /// <summary>
        /// Saves the current component configuration into the property bag.
        /// </summary>
        /// <param name="pb">Configuration property bag.</param>
        /// <param name="fClearDirty">Not used.</param>
        /// <param name="fSaveAllProperties">Not used.</param>
        public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb,
            bool fClearDirty, bool fSaveAllProperties)
        {
            this.WritePropertyBag(pb, "DoArchiving", this.DoArchiving);
 
            this.WritePropertyBag(pb, "ArchivePath", this.ArchivePath);
        }
 
        #region utility functionality
 
        /// <summary>
        /// Reads property value from property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <returns>Value of the property.</returns>
        private object ReadPropertyBag(
            Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
        {
            object val = null;
 
            try
            {
                pb.Read(propName, out val, 0);
            }
            catch (System.ArgumentException)
            {
                return val;
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
 
            return val;
        }
 
        /// <summary>
        /// Writes property values into a property bag.
        /// </summary>
        /// <param name="pb">Property bag.</param>
        /// <param name="propName">Name of property.</param>
        /// <param name="val">Value of property.</param>
        private void WritePropertyBag(
            Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, 
            object val)
        {
            try
            {
                pb.Write(propName, ref val);
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
        }
 
        #endregion
        #endregion
 
        #region IComponentUI members
        /// <summary>
        /// Component icon to use in BizTalk Editor.
        /// </summary>
        [Browsable(false)]
        public IntPtr Icon
        {
            get
            {
                return IntPtr.Zero;
            }
        }
 
        /// <summary>
        /// The Validate method is called by the BizTalk Editor during the build of 
        /// a BizTalk project.
        /// </summary>
        /// <param name="obj">An Object containing the configuration properties.
        /// </param>
        /// <returns>The IEnumerator enables the caller to enumerate through a 
        /// collection of strings containing error messages. 
        /// These error messages appear as compiler error messages. 
        /// To report successful property validation, the method should return 
        /// an empty enumerator.</returns>
        public System.Collections.IEnumerator Validate(object obj)
        {
            return null;
        }
 
        #endregion
 
        #region IComponent members
 
        /// <summary>
        /// Implements IComponent.Execute method.
        /// </summary>
        /// <param name="pc">Pipeline context.</param>
        /// <param name="inmsg">Input message.</param>
        /// <returns>Original input message.</returns>
        /// <remarks>
        /// IComponent.Execute method is used to initiate the processing of the 
        /// message in this pipeline component.
        /// </remarks>
        public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(
            Microsoft.BizTalk.Component.Interop.IPipelineContext pc, 
            Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
        {
            // Check if we want to archive the message
            if (_doArchiving)
            {
                // Get the message that was received
                IBaseMessage passedMessage = inmsg;
 
                // The name under which we want to archive the message
                string archiveFileName = null;
 
                // Get the interchange id from the message
                string interchangeID = (string)passedMessage.Context.Read(
                    "InterchangeID", 
                    "http://schemas.microsoft.com/BizTalk/2003/system-properties");
 
                // If the transport type if file or ftp, get the incoming filename 
                // to use as part of the archive filename (for easier identification)
                string filePath = null;
 
                // Get the type of adapter that was used for receiving the file
                string adapterType = (string)passedMessage.Context.Read(
                    "InboundTransportType", 
                    "http://schemas.microsoft.com/BizTalk/2003/system-properties");
 
                // Check if we received over a file adapter
                if (adapterType == "FILE")
                {
                    filePath = (string)passedMessage.Context.Read("ReceivedFileName",
                        "http://schemas.microsoft.com/BizTalk/2003/file-properties");
                }
 
                // Check if we received over a ftp adapter
                else if (adapterType == "FTP")
                {
                    filePath = (string)passedMessage.Context.Read("ReceivedFileName",
                        "http://schemas.microsoft.com/BizTalk/2003/ftp-properties");
                }
 
                // Set the name under which we want to archive the message
                archiveFileName = interchangeID + ".out";
 
                // Check if filePath was set
                if (filePath != null)
                {
                    // We ran into problems where the pipeline could not be executed,
                    // and the file just kept being archived
                    // To make sure this does not happen, we decided to never 
                    // archive the same message more then 100 times (still allowing 
                    // for multiple tests with the same message)
                    if (Directory.GetFiles(this._archivePath, String.Format("{0}*", 
                        Path.GetFileName(filePath))).Length > 100)
                    {
                        goto Finished;
                    }
 
                    // If filepath was set, add the incoming file name to the file 
                    // name under which we want to archive
                    archiveFileName = String.Format("{0}_{1}", 
                        Path.GetFileName(filePath), archiveFileName);
                }
 
                // Write the archive file
                WriteToFile(passedMessage, Path.Combine(this._archivePath, 
                    archiveFileName));
            }
 
            Finished:
 
            // Continue processing the message
            return inmsg;
        }
 
        /// <summary>
        /// This method is used to copy the contents from a memory stream to a
        /// filestream, saving the contents to a file.
        /// </summary>
        /// <param name="input">The memory stream.</param>
        /// <param name="output">The file stream.</param>
        protected void CopyStream(Stream input, Stream output)
        {
            // Set the buffer size for reading the stream
            int BUFFER_SIZE = 4096;
 
            // Create a buffer
            byte[] buffer = new byte[BUFFER_SIZE];
 
            // Integer indicating how many bytes we have read
            int bytesRead;
 
            try
            {
                // Read from the input stream
                bytesRead = input.Read(buffer, 0, BUFFER_SIZE);
 
                // Read all the bytes in the input stream
                while (bytesRead > 0)
                {
                    // Write the contents to the output stream
                    output.Write(buffer, 0, bytesRead);
 
                    // Go to the next byte
                    bytesRead = input.Read(buffer, 0, BUFFER_SIZE);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                // Rewind input stream
                if (input.CanSeek)
                {
                    input.Position = 0;
                }
            }
        }
 
        /// <summary>
        /// Write the contents of the message to an archive file.
        /// </summary>
        /// <param name="message">The message that should be archived.</param>
        /// <param name="fileName">The filename for the archive message.</param>
        protected void WriteToFile(IBaseMessage message, string fileName)
        {
            // Get the body of the message
            Stream msgStream = message.BodyPart.GetOriginalDataStream();
 
            // Create a new file stream
            FileStream fileStream = null;
 
            try
            {
                // Open the archive file for writing
                fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
 
                // Copy the data from the message to the archive file
                this.CopyStream(msgStream, fileStream);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                // Close the file stream
                if (fileStream != null)
                {
                    fileStream.Close();
                }
 
                // Close the message
                if (msgStream.CanSeek)
                {
                    msgStream.Position = 0;
                }
            }
        }
 
        #endregion
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *