Sending a Message

To send a message, you use the MessageQueue.Send method. This method accepts the object you want to send and the label (title) of the message. Listing 8-8 shows an example.

Listing 8-8 Sending a simple text message in XML
 Dim Queue As MessageQueue If MessageQueue.Exists(".\Private$\TestQueue") Then     Queue = New MessageQueue(".\Private$\TestQueue") Else     Queue = MessageQueue.Create(".\Private$\TestQueue") End If Queue.Send("Inventory Request Order Item #222", "Test Message") 

Figure 8-5 shows the retrieved message.

Figure 8-5. Receiving a message

graphics/f08dp05.jpg

By default, the message is serialized to an XML format. Figure 8-6 shows the message body in the Computer Management tool.

Figure 8-6. The XML-formatted message body

graphics/f08dp06.jpg

Alternatively, you can set the MessageQueue.Formatter property to an instance of the BinaryMessageFormatter, which ensures a more compact message (and thereby lessens network traffic). (See Listing 8-9.)

Listing 8-9 Sending a simple text message in binary
 Dim Queue As MessageQueue If MessageQueue.Exists(".\Private$\TestQueue") Then     Queue = New MessageQueue(".\Private$\TestQueue") Else     Queue = MessageQueue.Create(".\Private$\TestQueue") End If Queue.Formatter = New BinaryMessageFormatter() Queue.Send("Inventory Request Order Item #222", "Test Message") 

Figure 8-7 shows the same message in a binary format. The size has dwindled from 73 to 57 bytes.

Figure 8-7. The binary-formatted message body

graphics/f08dp07.jpg

Sending Object Messages

Of course, message queues would be fairly awkward if they accepted only text messages. Instead, you can submit any type of object as long as it satisfies three criteria:

  • The class must have a public constructor with no arguments. .NET uses this constructor to re-create the object when the message is received.

  • The class must be marked as serializable.

  • All class properties must be readable and writable. Read-only properties won't be serialized because .NET won't be able to restore the property values when re-creating the object.

Listing 8-10 shows an ideal Message class. It encapsulates information about an order, which the message recipient can then process.

Listing 8-10 A serializable message class
 <Serializable()> _ Public Class Order     Public OrderCode As String     Public ClientName As String     Public Items As ArrayList     Public Sub New()         ' No actions are required in the default constructor.     End Sub     Public Sub New(ByVal code As String, ByVal client As String)         OrderCode = code         ClientName = client     End Sub End Class 

The code to send this custom object is trivially easy (as shown in Listing 8-11).

Listing 8-11 Sending an object message
 Dim Ord As New Order("283c74c3-721d-4f34-80e5-57657b6cbc27", _   "Smith & Smith") Queue.Send(Ord, "Test Order") 

Note

When you use objects in a message queue, be sure to clearly identify the object type before creating the queue. Trying to determine what type of object has been received can be a nightmare for the server-side application. It's far easier if a queue is restricted to one type of object. However, the server application will always need to check the object type when retrieving a message, or at least use error-handling code for its casting operation. This is because there is no way to enforce queue type safety and restrict clients from submitting incorrect objects in a message. You will also run into problems if clients send the same type of object but encode it with the wrong formatter.


Advanced Message Configuration

None of these examples shows you how to send a message with encryption, a high priority, or a customized timeout. All these options, and more, are available through the Message class. To use them, just create a Message instance and pass the custom object that you want to send in the Message constructor. Then send the message object using the MessageQueue.Send method. This two-step process looks like Listing 8-12.

Listing 8-12 Sending a configured message with the message class
 Dim Ord As New Order("283c74c3-721d-4f34-80e5-57657b6cbc27", _   "Smith & Smith") ' Create the message wrapper. Dim OrderMessage As New Message(Ord) ' Enable encryption (requires access to Active Directory). OrderMessage.UseEncryption = True OrderMessage.EncryptionAlgorithm = Messaging.EncryptionAlgorithm.Rc2 ' Store the outgoing message in a file before sending it. OrderMessage.Recoverable = True ' Make it high priority. OrderMessage.Priority = Messaging.MessagePriority.VeryHigh ' Give it an hour to make it to the destination queue. OrderMessage.TimeToBeReceived = TimeSpan.FromHours(1) ' Store a copy of the outgoing message on this computer. OrderMessage.UseJournalQueue = True Queue.Send(OrderMessage, "Encrypted Order") 

The Message class doesn't provide any methods, but it does contain a slew of properties you can set. The reasons why you might create a Message wrapper include the following:

  • To enable Recoverable mode if you're working in Offline mode. Otherwise, outgoing messages are lost when the computer is rebooted.

  • To set a specialized priority. When receiving messages from a nontransactional queue, the application receives the highest (priority level 6) messages first, according to its receipt date, followed by the next highest priority, and so on.

  • To specifically use the journal queue to store a copy of the outgoing message.

  • To configure a nonstandard timeout value.

  • To enable encryption. Encrypted messages use a private/public key system supported by Active Directory. Note, however, that the encryption lasts only while the message is in transit, after which the message is decrypted by the recipient and stored in the destination queue in plain text. This might not be secure enough for some applications, in which case you might want to control the encryption and decryption programmatically, as discussed in Chapter 13.

  • To enable the dead-letter queue so you will have a record if the message could not be sent successfully to the target queue within the specified amount of time.

  • To enable message acknowledgement. You do this by setting the AcknowledgeType and specifying the administration queue that should receive the acknowledgement in the AdministrationQueue property.

  • To read the Message.Id property after sending the message, which will indicate the unique GUID that was generated to identify this message. You can use this ID later to confirm that a message was received.

Table 8-4 lists some key properties and the defaults that are used if you don't modify the Message class.

Table 8-4. Message Property Defaults 

Property

Default

AcknowledgeType

AcknowledgeType.None

AdministrationQueue

A null reference (Nothing)

AttachSenderId

True

AuthenticationProviderName

Microsoft Base Cryptographic Provider version 1.0

AuthenticationProviderType

CryptoProviderType.RSA_FULL

CorrelationId

An empty string ("")

EncryptionAlgorithm

EncryptionAlgorithm.RC2

Extension

A zero-length array of bytes

Formatter

XmlMessageFormatter

HashAlgorithm

HashAlgorithm.MD5

Label

An empty string ("")

Priority

MessagePriority.Normal

Recoverable

False

ResponseQueue

A null reference (Nothing)

SenderCertificate

A zero-length array of bytes

TimeToBeReceived

Message.InfiniteTimeout

TimeToReachQueue

Message.InfiniteTimeout

TransactionStatusQueue

A null reference (Nothing)

UseAuthentication

False

UseDeadLetterQueue

False

UseEncryption

False

UseJournalQueue

False

UseTracing

False

Receiving a Message

Typically, a server-side application uses the Receive method to read a single message from the queue. For a nontransactional queue, this is the message with the highest priority. If multiple messages exist with the same priority, the oldest one is read first. For a transactional queue, the message received first is simply the message that was sent first.

Before you can receive a message, you need to create a formatter that is configured to recognize specific .NET types. You identify the allowed types using an array of Type objects. In the following example, the code is searching for Order types in the message body:

 ' Set the allowed types. Dim AllowedTypes() As Type = {GetType(Order)} Queue.Formatter = New XmlMessageFormatter(AllowedTypes) 

Note, however, that this code won't prevent you from attempting to download a message of another type, at which point an exception will be generated.

A server-side application might use this type of logic to receive all the messages in the queue (as shown in Listing 8-13).

Listing 8-13 Receiving all messages
 Dim Queue As New MessageQueue(".\Private$\TestQueue") Dim ReceivedMessage As Message ' Set the allowed types. Dim AllowedTypes() As Type = {GetType(Order)} Queue.Formatter = New XmlMessageFormatter(AllowedTypes) Do     ReceivedMessage = Queue.Receive()     ' Check if it is the expected type.     If TypeOf ReceivedMessage.Body Is Order Then         ' Process message here.         Dim ReceivedOrder As Order = CType(ReceivedMessage.Body, Order)         Console.WriteLine(ReceivedOrder.OrderCode)     End If Loop 

If there is no message in the queue when you call Queue.Receive, your code just stops, waiting indefinitely for the next message to be received. Alternatively, you can use an overloaded version of the Receive method that accepts a TimeSpan. If no message is received after the specified amount of time, the code continues. This enables you to create a component that can intelligently check whether it should shut down (perhaps in response to a user interface action or a signal set by another component). Without using a TimeSpan, the code could block perpetually and remain unresponsive (as shown in Listing 8-14).

Listing 8-14 Polling for messages
 Do     ReceivedMessage = Queue.Receive(TimeSpan.FromSeconds(60))     ' Check if a message was received.     If ReceivedMessage = Nothing Then         ' Yield the processor for some other work, unless         ' a stop is requested.         If Not Me.ShouldStop Then             Thread.Sleep(TimeSpan.FromSeconds(60))         End If     Else         ' (Process message here.)     End If     ' Check for a requested shutdown.     If Me.ShouldStop Then Exit Loop Loop 

The MessageQueue class also supports asynchronous reads through its BeginReceive and EndReceive methods. These work the same as the asynchronous methods examined in Chapter 6 and are useful when receiving large data objects.

Sometimes Polling Isn't a Problem

When you create a server-side message processor, you will probably create it as a stand-alone Windows service that periodically checks a queue for messages. For experienced developers, this type of design might set off warning bells because it uses polling, which typically reduces scalability in any system.

The problem with polling is that as more users poll, what begins as minor overhead can become a significant performance hit. If you have hundreds of clients polling a database, for example, connections will become harder to obtain and queries will execute much more slowly. (With thousands of clients, the system will probably stop working altogether.) With Message Queuing, however, polling isn't a problem because with a server-side queue, generally multiple clients send messages but only one application retrieves messages. Therefore, only one application is polling the queue at a time. Similarly, client queues are local to the client computer. Even if every client is polling continuously for a response message, they will all be polling separate queues. Furthermore, each queue is located on a local workstation, which means that polling it has no effect on server resources.

Browsing Messages

When a message is received, it is removed from the queue, even if your application does not use it. To retrieve a copy of the first message but leave the message in the queue, you can use the MessageQueue.Peek method. However, this technique is inconvenient because if called multiple times, you will always receive the same message. A better approach is the For Each syntax, which enables you to walk through all the messages without removing them.

Listing 8-15 shows an example that iterates through all the messages and displays the title of each one in a list control.

Listing 8-15 Reading messages without receiving them
 Dim Queue As New MessageQueue(".\Private$\TestQueue") Dim Message As Message ' Configure the list to show the message title. lstMessages.DisplayMember = "Label" For Each Message In Queue     lstMessages.Items.Add(Message) Next 

Note that the whole object is stored in the list control. This enables you to retrieve more message information after the user makes a selection. Or, the user can choose a message, and you can use the MessageQueue.ReceiveById method to download and remove the selected message, regardless of where it is positioned in the queue.

Response and Acknowledgement Messages

Conceptually, there are two types of message acknowledgements: automatic and manual. Automatic acknowledgements are sent to a dedicated administration queue. The Message Queuing service sends the acknowledgement message to indicate that a message was received or processed, without requiring the receiving application to take any action (as shown in Listing 8-16).

Listing 8-16 Requesting an acknowledgement message
 Dim Queue As New MessageQueue(".\Private$\TestQueue") Dim Ord As New Order("283c74c3-721d-4f34-80e5-57657b6cbc27", _   "Smith & Smith") Dim OrderMessage As New Message(Ord) OrderMessage.AdministrationQueue = New _   MessageQueue(".\Private$\TestAcknowledgement") OrderMessage.AcknowledgementType = AcknowledgementTypes.PositiveArrival Queue.Send(OrderMessage, "Order") ' Hold on to the correlation ID. ' It's required in order to check for an acknowledgement. Dim CorrelationID As String = OrderMessage.Id 

Here's how it works: Before you send the message, you set the Message.AdministrationQueue property to the administration queue that will receive the message. Then you indicate the desired type of acknowledgement by setting Message.AcknowledgementType to one of the values listed in Table 8-5.

Table 8-5. Values from the AcknowledgementTypes Enumeration

Value

Description

None

No acknowledgement message is sent. This is the default.

FullReachQueue

The acknowledgement is sent if the message reached the queue successfully, timed out, or was denied access.

PositiveArrival

The acknowledgement is sent if the message reached the queue successfully. However, that doesn't mean the application has processed this message.

NotAcknowledgeReachQueue

This is an "anti-acknowledgment" message. It's sent only if the message can't reach the queue, either because it timed out or access was denied.

FullReceive

The acknowledgement is sent if the message was received by an application or if a timeout occurred.

NegativeReceive

The acknowledgement is sent only if the message could not be read from the queue successfully.

PositiveReceive

The acknowledgement is sent if the message was received (although there is no way to know who has read it from the queue).

NotAcknowledgeReceive

This is an "anti-acknowledgment" message. It's sent only if the message can't be retrieved from the queue, usually because its TTBR timeout has expired.

After sending the message, you should examine and record the Message.Id property (perhaps using a local XML file or text file). You can then retrieve the corresponding acknowledgement message from the administration queue, when it arrives, by using the MessageQueue.ReceiveByCorrelationId method and specifying the ID of the message you sent (as shown in Listing 8-17). Acknowledgement messages don't have any interesting content. However, you can examine the Message.Acknowledgement property of an acknowledgement message to determine whether the message was received, timed out, and so on.

Listing 8-17 Retrieving an acknowledgement message
 Dim AcknowledgementMessage As Message AcknowledgementMessage = Queue.ReceiveByCorrelationId(CorrelationId) If AcknowledgementMessage.Acknowledgement = Acknowledgement.ReachQueue     ' (The order reached the queue. It may or may not have been     ' processed.) End If 

Response queues operate under a similar principle, but they require the receiving application to take some action. They can also contain additional information. For example, a response message to an order might be a custom structure with information about the order date and shipping charges. To use a response queue, the sending application creates a MessageQueue object that identifies the response queue (which is an ordinary queue) and sets it in the Message.ResponseQueue property (as shown in Listing 8-18).

Listing 8-18 Identifying a response message
 Dim Queue As New MessageQueue(".\Private$\TestQueue") Dim Ord As New Order("283c74c3-721d-4f34-80e5-57657b6cbc27", _   "Smith & Smith") Dim OrderMessage As New Message(Ord) ' Specify a response queue. Dim ResponseQueue As New MessageQueue(".\Private$\TestQueueResponse") OrderMessage.ResponseQueue = ResponseQueue Queue.Send(OrderMessage, "Order") 

The receiving application must then examine this property, create a new message, and set the Message.CorrelationId property of the response message to match the Message.Id of the received message. This allows the client to determine what the message is responding to. Unlike with acknowledgement messages, it's the recipient's responsibility to perform these tasks. The Message Queuing infrastructure doesn't send a response message automatically.



Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net