Sunteți pe pagina 1din 23

1

Queued Components in COM+


Queued components provide for an easy implementation of asynchronous (as well as
disconnected) method calls, where the client does not have to wait until the method call
finishes. Queued components are very useful in batch processing applications or in a
remote order entry type of environment. The Queued component architecture in COM+
internally uses the MSMQ queues to deliver the messages (method calls) to a COM+
component. The receiving component’s machine does not have to be online to receive the
message. The transmitted message is stored in the queue and attempted for delivery when
the receiving component’s machine becomes available. The basic transmission
mechanism is shown below.
Server

Client MSMQ Queue


Queued Component
Listener
QC Recorder Queued Component
MSMQ Queue
(COM component) Player (COM comp.)
MSMQ Queue
COM Object

You can also use the MSMQ API to implement asynchronous communication but COM+
queued components hide much of the underlying complexity in using the MSMQ queues.
A COM+ application can be set up to support queued components through the properties
dialog using the component services explorer, as shown below.

When you select the above check box for the COM+ application to be queued, COM+
creates a set of MSMQ queues for the application. A Client uses the “Queued Component
2

Recorder” to call any methods on a queued interface of a component. You can think of
the recorder as being similar to a proxy. The recorder has the same methods available as
the queued component’s queued interface. When the client makes a method call, the
recorder records it, and then when the client de-references (or deactivates) the recorder, it
makes a message and sends it to the MSMQ queue. The MSMQ queue usually resides on
the COM server’s computer. On the server side, the “Queued Component Listener”
checks the MSMQ queue for messages. When a message is found to be present, the
listener instantiates a “player” object, which unpacks the method call in the message and
invokes the queued component.
Here are some of the important points to note when dealing with queued
components.

Queued component methods cannot have [out], or [out,retval], or [in,out] type of


parameters i.e., the only allowed attributes for method parameters is [in]. Also, the
methods of a queued interface cannot return application specific HRESULTS. Note that
this is necessary because of the asynchronous nature of calls. The client might have
terminated before the called method returns from a queued component.

If the client is interested in a response from a queued component, then the client
can pass a response object as one of the parameters in a queued component call. Note that
in this case the response object will be configured as the queued component itself. The
client will have to instantiate this response object and specify the client’s machine as the
destination (using moniker approach) before invoking a call to the queued component
method on the server. In typical applications, the response object may write to a database
on the client side or send an email to the client.

The messages in the MSMQ queue are organized by their priority. A higher
priority message will be at the front of the queue even if it arrived later than a low
priority message. For example, in an order processing application, perhaps new orders
should be given higher priority than cancellation orders. You can also use a separate
queue for new order processing, and a separate one for order cancellations, and perhaps
allocate more server computers for the new order processing as compared to order
cancellation processing.

A message can contain any type of data e.g., text, binary, XML, disconnected
recordset, a serialized COM object that implements IPersistStream or IPersistStorage
interface. Note that the receiver of the message has to know in advance the type of
message being received.

The receiving application can peek at the message before removing it from the
MSMQ queue. The message is not dequeued from the MSMQ queue until it is guaranteed
that the entire message has been received correctly. MSMQ also provides a set of COM
classes with connection point interfaces that can be used by the receiver to receive
notification when there is a message waiting in the queue.
Queued applications can be used in a disconnected environment such as wireless
applications. In this case the client should be configured as an MSMQ independent client,
3

so that if the remote server is unavailable (perhaps out of wireless range), the client will
queue the message locally and the local client queue server will attempt to deliver it to
the destination queue at a later time when connectivity is reestablished.

Queued component architecture can be used to develop a distributed and scalable


platform for computationally intensive applications. A group of COM servers can listen
to a queue for computational jobs to arrive. Each server when it becomes available goes
to the queue to pick a job to perform. This technique can be used to develop fault-
tolerant, scalable distributed multiprocessing applications.

There are two types of MSMQ queues, public and private. Public queues are
published in the active directory, whereas a private queue is listed only on a particular
computer and you have to use the exact path name of the private queue to be able to use
it.

Queued components require public queues so the MSMQ has to be set up in a


domain environment mode. Thus for queued components to work, the computer on which
you are using MSMQ must be set up as a primary domain controller and also have the
active directory service running properly.

The message sent can be part of a transaction. More details on it later in this handout.

Configuring your Windows 2000 server for Queued Components:


Step 1: Configure your computer for Active directory services and to be the primary
domain controller. From the start menu choose, Administrative tools->Configure your
server.

Click on the active directory link. Then click on the start Active directory wizard link.
4

Choose domain controller for a new domain if you do not have an existing domain.
5
6

Make sure your computer is online (either via modem, or via the network card) otherwise
you will get the following error message.
7
8

After you click finish, it may take several minutes to install the active directory services
and the DNS server.

Next, you need to install the MSMQ server on your machine. From the control panel,
choose add/remove programs, then click on add/remove windows components.
9

Then check Message Queuing Services and click on next.


10

Follow the wizard screens and accept defaults.

Testing to see if MSMQ is installed properly:


If you are able to successfully create a public queue, then MSMQ is hopefully
installed correctly. From the start menu choose, administrative tools->computer
management. Expand on “Services and Applications”, and then “Message Queuing”.

Right click on the “Public Queues” and choose new “public Queue”. Give the queue a
name of “MyQueue”.
11

If you are able to create the public queue successfully, then MSMQ is correctly installed.

Queued Component Transactions:


Queued components can take a part in a transaction on the client side or on the
server side. If the transaction is aborted on the client side, the message is never sent to the
destination MSMQ. Note that the client sends the message only after the method call
finishes on the client-side.
On the server-side, the QC player which reads the message from MSMQ queue is
a transactional component. If the method call to the server component fails, the QC
player puts the message back in the queue to try it later. However, this may have the
potential for infinite retries if each time the call runs into some kind of error message.
This situation is referred to as a poison message. To solve this problem, COM+ has six
retry queues for each queued application. Five of these are named appname_n where n
varies from 0 to 4. The sixth retry queue is name appname_deadqueue. The message in
the appname_0 queue is retried every one minute and moved to the appname_1 queue if
three attempts fail. Similarly, the message in appname_1 queue is retried every two
minutes (twice as long as the previous queue) and moved to queue number 2 if three
attempts fail. Finally if there are problems, the message will be moved to the dead queue
where it will remain until removed manually using the component services explorer.
12

In some situations, you may want to know if the message had a problem in executing and
is being moved to the dead queue. You can specify an exception class with a queued
component for this purpose through the component services explorer.

Example: First create an SQL server database for testing purposes.


Create a SQL server database called EStore with the following tables in it.
The SQL script file for the EStore database is shown below:

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_Orders_Customers]') and


OBJECTPROPERTY(id, N'IsForeignKey') = 1)
ALTER TABLE [dbo].[Orders] DROP CONSTRAINT FK_Orders_Customers
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_Inventory_Products]') and


OBJECTPROPERTY(id, N'IsForeignKey') = 1)
ALTER TABLE [dbo].[Inventory] DROP CONSTRAINT FK_Inventory_Products
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[FK_Orders_Products]') and


OBJECTPROPERTY(id, N'IsForeignKey') = 1)
ALTER TABLE [dbo].[Orders] DROP CONSTRAINT FK_Orders_Products
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Customers]') and


OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Customers]
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Inventory]') and


OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Inventory]
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Orders]') and OBJECTPROPERTY(id,


N'IsUserTable') = 1)
drop table [dbo].[Orders]
GO

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Products]') and


OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[Products]
GO

CREATE TABLE [dbo].[Customers] (


[CustID] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[CustFname] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[CustLname] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
[CustAccountBalance] [money] NULL ,
[CustCreditLimit] [money] NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Inventory] (


[ProdID] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
13

[InStockQty] [bigint] NOT NULL ,


[Inum] [bigint] IDENTITY (1, 1) NOT NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Orders] (


[OrderID] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[CustID] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[ProdID] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[Quantity] [int] NOT NULL ,
[Price] [money] NOT NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Products] (


[ProdID] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[ProdName] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
[ProdPrice] [money] NOT NULL
) ON [PRIMARY]
GO

You can initialize some data in the database as:


14

Now let us create a queued COM component.


Create an ATL COM wizard project. Name the project OrderQ. Accept all the defaults.
From the insert menu, choose “New ATL object”, then choose simple object. Name the
component Order, select threading model to be Free (we will manually change it to
Neutral later on). Also check the ISupportErrorInfo as shown below.
15

Type the following in the StdAfx.h file, right after #include <atlcom.h> line :
#import "c:\program files\common files\system\ado\msado15.dll" \
no_namespace \
rename("EOF","adoEOF")

Change the threading model in Order.rgs file to ‘Neutral’ as shown below:


….
InprocServer32 = s '%MODULE%'
{
val ThreadingModel = s 'Neutral'
}
Add a method to the IOrder interface called AddOrder with the following IDL.
[in] BSTR oid, [in] BSTR cid, [in] BSTR pid, [in] int qty, [in] CY amt

Type the following in the AddOrder method. You will need to include the <stdlib.h> file
in order to use the _itow function (int to wide character).

// Order.cpp : Implementation of COrder


#include "stdafx.h"
#include "OrderQ.h"
#include "Order.h"
16

#include <stdlib.h>

STDMETHODIMP COrder::AddOrder(BSTR oid, BSTR cid, BSTR pid, int qty, CY amt)
{
// TODO: Add your implementation code here
HRESULT hr;
_ConnectionPtr pCn;
_RecordsetPtr pRecordset;
hr=pCn.CreateInstance(__uuidof(Connection));
if (SUCCEEDED(hr))
{
_bstr_t cn(L"Provider=SQLOLEDB;User ID=sa;Password=;Initial Catalog=EStore;Data
Source=tennis;");
hr = pCn->Open(cn, _bstr_t(L""),_bstr_t(L""),adModeUnknown);
if (SUCCEEDED(hr))
{
_bstr_t SQ(L"INSERT INTO Orders (OrderID,CustID,ProdID,Quantity,Price) ");
SQ += _bstr_t(L"VALUES (");
SQ += _bstr_t(L"'");
SQ += oid;
SQ += _bstr_t(L"',");

SQ += _bstr_t(L"'");
SQ += cid;
SQ += _bstr_t(L"',");

SQ += _bstr_t(L"'");
SQ += pid;
SQ += _bstr_t(L"', " );
wchar_t s1[10];
SQ += _bstr_t(_itow(qty,s1,10));
SQ += _bstr_t(",");
SQ += _bstr_t(amt);
SQ += _bstr_t(L")");
Sleep(10000); // for debugging purposes
_variant_t vRecsAffected(0L);
pRecordset = pCn->Execute(SQ,&vRecsAffected,adOptionUnspecified);
}
}
else return E_FAIL;
return S_OK;
}
Build the dll.

Test VB Client: Create a VB standard exe project, add a reference to the OrderQ type
library. Put a button on the form with the following code:

Private Sub Command1_Click()


Dim s1 As New Order
s1.AddOrder "O101", "C101", "P101", 2, 50#

End Sub
17

Run the client to see if another row for order “O101” gets added to the orders table.
Delete the row corresponding to order O101after it has been added to the Orders table
through the SQL server Enterprise Manager.

Making the component Queued:


Step 1: Add the OrderQ.Order component to an existing COM+ application (or you can
create a new application). I added the Order component to the existing MyST COM+
application on my computer.

Step 2: Make the application queued, and also enable the queue listener on the COM+
application. You can do this by right clicking on the application and choosing properties,
and the queuing tab.
18

Note that as soon as you select the application to be queued, six queues for the
application are created. You can view these by selecting the start->administrative tools-
>computer management and then expanding on message queuing.

Step 3: You need to make an interface in the OrderQ.Order component queued. From the
component services explorer, expand on the Order component, and then the interfaces.
Right click on the IOrder interface, and choose properties, then check the queued check
box from the queuing tab.
19

Note: If the COM+ application to which you added the Order component had the security
enabled on it, and if you had created roles earlier, then either you will have to enable
roles for the Order component, or remove the “Enforce access ..” check mark below, in
order to successfully test the component.

Step 4: Start the COM+ Queued application by going through the component services
explorer and right clicking on the application and choosing “start application. Note that
the Queued COM+ application has to be running in order for the client to be able to
deposit a message in the queue.

Monikers: Monikers are special COM objects whose job is to locate and create other
COM objects. For example the job of the standard new moniker is to locate a COM
object through its CLSID or ProgID, and create an object of it.
As another example of a moniker, there is a standard URL moniker that is used inside
a browser for registering and invoking ActiveX controls that are downloaded from a web
server. An ActiveX control can be embedded inside an HTML page by using the object
tag e.g.,
<object classid="clsid:93CC2ED1-3017-11D4-9F02-00A0CC28BB43" name=clct1 width=100 height=50
codebase="c:\vb6progs\ColorCtl\prjCol.ocx">

When the page is downloaded by the browser, the code for the ActiveX control is
downloaded also, if the control is not registered on the browser’s machine. After
downloading, the Activex control is registered and an object of it is created and launched
in the browser. All of these tasks are accomplished by the URL moniker.

COM+ has introduced two special monikers related to queued components. One is a
queue moniker and the other is the new moniker. The queue moniker is responsible for
20

creating the recorder object on the client side. The queue moniker pipes the recorder to
the new moniker to create a correct client-side proxy for the queued component. In
Visual Basic, the client side code using the queue and the new monikers appears as:
dim recorder as new MyProject.MyQueuedComp
set rc=GetObject(“queue:ComputerName=kiwi/new:MyProj.MyQComp”)
rc.methodname(…)

In a VC++ client, the queue and new monikers can be invoked as:
CoGetObject(L”queue:Priority=5,ComputerName=kiwi/new:MyProj.MyQComp”,
NULL, IID_IDispatch, (void **) &pIDispatch)

Note that the message from the client is not sent out until the client release the reference
to the queued component proxy object i.e., the recorder object created through the
GetObject or CoGetObject call.

Step 5: Modify the VB client to invoke the component through the queue and new
monikers as shown below.
Private Sub Command1_Click()
' Dim s1 As New Order
' s1.AddOrder "O101", "C101", "P101", 2, 50#

Dim objOrder As Order


Set objOrder = GetObject("queue:Priority=5,
ComputerName=tennis/new:OrderQ.Order")
objOrder.AddOrder "O101", "C101", "P101", 2, 50#
End Sub

Run the client, and check the database to see if a new record for the order has been added
to the “order” table (after 10 seconds, recall that the Order component had a Sleep of 10
seconds to test the asynchronous behavior). You will notice that when you press the
button in the VB client, the call returns immediately because now it is an asynchronous
call. Also, the queue moniker feeds to the new moniker to create the client-side side
proxy.

The Orders table should have the Order O101 added to it after 10 seconds (because of an
intentional sleep call in the component) as shown below.
21

Step 6: Delete the Order O101 from the Orders table. Then modify the client to invoke
the COM+ component directly without the queue moniker to test the synchronous call
behavior, as shown below.

Private Sub Command1_Click()


Dim s1 As New Order
s1.AddOrder "O101", "C101", "P101", 2, 50#

‘Dim objOrder As Order


‘Set objOrder = GetObject("queue:Priority=5,
‘ComputerName=tennis/new:OrderQ.Order")
‘objOrder.AddOrder "O101", "C101", "P101", 2, 50#
End Sub

The call will still modify the Orders table, but the client will hang for 10 seconds as it is
now a synchronous call.

Note: If you get error messages or if the database is not being modified, check the public
message queue for the application to see if messages are being delivered. Also, check to
see if the security options on the application and the Order component are set properly. If
security is enabled, then proper roles are also enabled for the Order component.

If the security is enabled on the application and the Order component, then the listener
will pick up the message from the queue, but the call to the Order component will fail
because of the security settings. Since the call is part of a transaction, the message will be
put back and retried each minute as mentioned earlier. After three failed attempts, the
message will be moved to the myst_1 queue, as shown below.
22

Step 7: Delete the Order O101 from the orders table. Enable the security on the COM+
application in which the queued component is residing. Enable the appropriate roles on
the component’s interfaces. Run the client through the moniker to see if the message gets
delivered (i.e., the database is modified for the Orders O101 correctly).
23

S-ar putea să vă placă și