Although we’ve by no means covered all the objects, properties, methods and events available in MSMQ in the previous section, we now have enough information to start using MSMQ in our own applications. In the next chapter, you'll see how we implemented MSMQ in our Wrox Car Co sample application. In the meantime, to give you a gentle introduction, we'll add some MSMQ features to the Finance component that we started creating way back in Chapter 2.
In Chapter 2 we created a component named WCCFinance.PaymentTerms
, which could be used to calculate the number of payments required to clear a loan at a fixed monthly interest rate. In today's world of close scrutiny on financial institutions, it's usual to be required to keep records of all credit advice you give to customers. To comply with this, we want our component to be able to log all the calculations it does.
Of course the easy place to log them is in a text file on the local machine. However, it makes more sense for our head office to log all quotations from all the company's branches. To do this we have to find a way of sending each calculation off to head office. We could do it in daily batches, but MSMQ seems to offer the opportunity to do it in (almost) real time. We can create a message for each calculation and send it off to head office via MSMQ.
Using MSMQ has another advantage. If we were accessing the head office database using an OLE-DB or ODBC driver directly, our component would be stalled while waiting for the operation to complete—especially if the network was running slowly that day. And if the network was down altogether, we wouldn’t be able to do any calculations at all.
The user interface doesn’t change from the earlier examples. It just submits the values to an ASP script. To do so, it uses a <FORM>
with three HTML text boxes and a SUBMIT
button. Here's the <FORM>
code again:
<FORM ACTION="financeMQ.asp" METHOD="POST">
Total Price ($):
<INPUT TYPE="TEXT" NAME="txtTotalPrice"><BR>
Interest Rate (%):
<INPUT TYPE="TEXT" NAME="txtInterestRate"><BR>
Monthly Payment ($):
<INPUT TYPE="TEXT" NAME="txtMonthlyPayment"><P>
<INPUT TYPE="SUBMIT" VALUE="Submit to ASP script">
</FORM>
And here's what it looks like in the browser:
The page that processes the values sent from the browser is the main business rules component for our application. It calls the PaymentTerms
component to carry out the calculation, and returns the results by writing them into the HTML page that it creates and sends back to the browser. The initial section of this code is the same as we used in Chapter 2:
<%@ LANGUAGE="VBSCRIPT" %>
<HTML>
<HEAD>
<TITLE>Results from the WCCFinance Component</TITLE>
</HEAD>
<BODY>
<%
Set objFinance = Server.CreateObject("WCCFinance.PaymentTerms")
objFinance.TotalPrice = Request.Form("txtTotalPrice")
objFinance.InterestRate = Request.Form("txtInterestRate")
objFinance.MonthlyPayment = Request.Form("txtMonthlyPayment")
intResult = objFinance.GetNumberPayments
Set objFinance = Nothing
If intResult > 0 Then %>
A total price of <B>$<% = Request.Form("txtTotalPrice")%></B>
at a monthly interest rate of <B><% = Request.Form("txtInterestRate")%>%</B>,
and paying <B>$<% = Request.Form("txtMonthlyPayment")%></B>
per month, will require <B><% = intResult %></B> payments.<P>
<% Else %>
Sorry, a monthly payment of
<B>$<% = Request.Form("txtMonthlyPayment")%></B>
is not sufficient to pay off a loan of
<B>$<% = Request.Form("txtTotalPrice")%></B>
at an interest rate of
<B><% = Request.Form("txtInterestRate")%>%</B><P>
<% End If
...
At this point in the script, we've got the results and written them into the returned page. All we need to do now is send them off to head office as well. We'll do the easy bit first and create the message body:
'create the message to send to head office
CRLF = Chr(13) & Chr(10)
strMsgBody = "Finance quote offered on " _
& FormatDateTime(Now, vbGeneralDate) & CRLF _
& "Total Price: " _
& FormatCurrency(Request.Form("txtTotalPrice")) & CRLF _
& "InterestRate: " _
& Request.Form("txtInterestRate") & "%" & CRLF _
& "Monthly Payment: " _
& FormatCurrency(Request.Form("txtMonthlyPayment")) & CRLF _
& "Number of Payments Required: " & CStr(intResult) & CRLF
...
Now we can create an instance of the MSMQQueueInfo
object that we’ll use to define (and if required create) our queue. The PathName
includes the name of our head office machine:
...
'create a queue info object and set the path and name
Set objQueueInfo = Server.CreateObject("MSMQ.MSMQQueueInfo")
objQueueInfo.PathName = "daisy\FinanceQueue" 'remote head office queue
...
Next we attempt to create the queue. The On
Error
Resume
Next
statement will prevent the error that will occur if the queue already exists from breaking our code:
...
'try and create the queue in case it doesn’t already exist
On Error Resume Next
objQueueInfo.Create
...
The next step is to open the queue. The MSMQQueueInfo
object's Open
method returns an MSMQQueue
object, and we can set the values of the two constants we need (remember, ASP doesn’t recognize the MSMQ named constants):
...
'use the MSMQQueueInfo object to open the FinanceQuote queue
Dim objQueue
MQ_SEND_ACCESS = 2 'declare the 'send' and 'deny' constants
MQ_DENY_NONE = 0
Set objQueue = objQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
...
Finally, providing the queue was opened successfully, we can create our new MSMQMessage
object and set its properties before calling its Send
method. We're only using a low priority for this logging operation. The last step is to close the queue, write a message into the returned page, and add the closing HTML stuff:
...
'see if queue opened OK, and if so create and send message
If objQueue.IsOpen Then
Set objMessage = Server.CreateObject("MSMQ.MSMQMessage")
objMessage.Priority = 2
objMessage.Label = "Finance Quote on " _
& FormatDateTime(Now, vGeneralDate)
objMessage.Body = strMsgBody
objMessage.Send objQueue
objQueue.Close
Response.Write "Quote details also logged at head office."
Else
Response.Write "Could not send quote details to head office."
End If
%>
<P><A HREF="finance_useasp.htm">Continue</A>
</BODY>
</HTML>
Here's the result of our ASP code, as seen in the browser. The confirmation message is a little misleading because it really only indicates that the queue was opened successfully. An error in the Send
process wouldn't be detected because of the On
Error
Resume
Next
statement—one reason why we referred to this technique earlier as 'quick and dirty':
To confirm that the message was sent, we can view it in MQ Explorer:
Right-clicking the message and selecting Properties shows details about the message. In the Body tab of this dialog, we can view the first 256 bytes of the message body:
We know that our message was delivered to the destination queue OK, because we've seen it in MQ Explorer. However our application will usually want to do something with the messages, and so we need to be able to receive and read them using code. We've produced a simple program in Visual Basic that will read our message on the destination machine.
To work with MSMQ in Visual Basic (and other languages) it's easier if we provide the programming environment with a reference to the MSMQ type library. In the Project | References dialog, we can select the Microsoft Message Queue Object Library. This will allow us to use the MSMQ named constants in our code, view the MSMQ objects in Object Browser, and get pop-up tips as we type in our code:
The application itself is a simple single form, compiled into a standard EXE project. The form itself contains a couple of labels that will display the values of the message's Label
and Body
properties, a button to close the program down, and a Timer
control:
And because we have a reference to the MSMQ type library, VB knows about the objects that are available as we type the code into the form's Code window:
The Visual Basic code itself is simple. There's a subroutine to close the application down when we click the Close button:
Private Sub cmdClose_Click()
End
End Sub
All the rest of the work is done in the Timer
event of the timer control on the form. We've set the Interval
property to 5000
in the timer's Properties dialog so that it occurs every five seconds. The code that runs each time the timer fires first creates a MSMQQueueInfo
object and points it at the FinanceQueue
queue on the local machine. Remember, this application is running at head office, and we created the queue there in our ASP page:
Private Sub timMsgRead_Timer()
On Error GoTo timMsgRead_Error
Dim objQueueInfo As New MSMQQueueInfo
objQueueInfo.PathName = ".\FinanceQueue"
...
Then it creates a MSMQQueue
object to refer to the queue with, and opens the queue. Next it creates a MSMQMessage
object to hold the received message. Finally, it retrieves the topmost message by calling the queue object's Receive
method, displays the values of the Label
and Body
properties in the label controls on the form, and closes the queue again:
...
Dim objQueue As MSMQQueue
Set objQueue = objQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
If objQueue.IsOpen Then
Dim objMessage As New MSMQMessage
Set objMessage = objQueue.Receive(ReceiveTimeout:=1000)
lblMsgLabel = objMessage.Label
lblMsgBody = objMessage.Body
objQueue.Close
End If
Exit Sub
timMsgRead_Error:
lblMsgLabel = "Error reading message"
lblMsgBody = ""
Exit Sub
End Sub
And here's the result. It would be easy enough now to write the value of the message's Body
property to either a text file or a local database: