COM+ and .NET - A practical approach - Part 2
By Natty Gur | Published: 20 June 2005 |
Reader Level: Intermediate
A look at COM+ and .NET
(l) Applying security
COM+ and role based security are fully integrated. Both of those abilities let
administrators to create groups of user, which are roles, and to assign those
groups security rights regarding COM+ components and methods. All this process
can be done easily in COM+ MMC.
Role base security can be use to enforce security checking and authorization
for performing database actions (Insert, Update, Delete and Select). In order
to use role base security you need to create your application in layer fashion.
When crafting the data layer you need to create classes that implement
interfaces consist on insert, update, delete and select functions. Doing so
enables administrators to create roles such as managers, users, sales or others
and assign roles to those class's database interface functions. Administrators
can add or remove users from groups and impose authorization on database
actions.
Library applications and server applications that called from web application
based on windows authentication, impersonate and set to interactive user
identity can use ContextUtil.IsUserInRole() function to check if the current
user exist in a given role. After determine the user role we can use that data
against the Database to enforce data compartmentalization. Every Database and
organization got their own implementation for data compartmentalization. One of
the common and easy uses is to add compartmentalization letter or to use
bitwise field for each row and to use it when selecting data. Alternatively,
calling different store procedures can be employ so every member of each group
will get other set of data from Database.
As you probably figure out you can use attributes to set COM+ role base
security and roles for Application, component and methods
(ApplicationAccessControlAttribute, ComponentAccessControlAttribute,
SecureMethodAttribute). You can use SecurityRoleAttribute Attribute to set the
role or roles (you need to use the attribute for each role) for application,
component or method. Using this attribute will add the role but won't add users
to role. Entering users to role it's a task that must perform by administrators
unless SecurityRoleAttribute use with its second constructor parameter set to
true. Using that constructor will end everyone group as role users.
Let's gather all that information into one sample page in our application. Our
page (ContractsLogic.aspx) need to show data from array list that holds list of
company contracts. Contract data should be compartmentalization between manager
that can see all the list and workers that can see only certain contracts. The
application is build utilizing layers application paradigm. The page class
calls ContractsLogic class that calls ContractsData to get data from the
DataBase, and return arraylist back to the ContractsLogic. ContractsLogic
implies the security requirements and return arrayList holding the data per
user to the page that bind arraylist to grid.
Figure 2.0

The page class calls ContractsLogic class, that set as COM+ library
application, to get data and bind it to DataGrid.
private void Page_Load(object sender, System.EventArgs e)
{
try
{
IContractsLogic oLogic = new Contracts.ContractsLogic();
this.DataGrid1.DataSource = oLogic.GetByUser ();
DataGrid1.DataBind();
}
catch (Exception err)
{
string s = err.Message ;
}
}
In order to work with security within the assemblies classes we must use the
ApplicationAccessControl attribute.
using System;
using System.EnterpriseServices;
using DemoInterfaces;
//[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationAccessControl]
Our next step is to use ComponentAccessControlAttribute to set security access
to the class and SecurityRoleAttribute to add users and managers Roles. We use
SecurityRoleAttribute constructor that let us set everyone group to users role.
[ComponentAccessControlAttribute(true), SecurityRoleAttribute ("users",true),
SecurityRoleAttribute ("managers")]
public class ContractsLogic : ServicedComponent ,IContractsLogic {
The interesting part here is GetByUser implementation of IContractsLogic
interface. In order to enable security settings to method Interfaces should be
use. Although we aren't going to implement method security I use them to let
administrator the option to authorize database actions. Interfaces should be in
separate assembly at the GAC to enable web application and COM+ server
application to share interfaces. GetByUser calls ContractsData to get contract
data. For this demo ContractsData simply create array list but its original
destination is to retrieve data from Database and apply Compartmentalization in
that call. In that demo, compartmentalization is done by checking user in role
and using bitwise to get the right data.
System.Collections.ArrayList IContractsLogic.GetByUser()
{
IHandleData oData = new ContractsData();
System.Collections.ArrayList oArr = oData.Get();
System.Collections.ArrayList oRetArr =
new System.Collections.ArrayList();
if (ContextUtil.IsCallerInRole("users") )
{
System.Collections.IEnumerator oEnum = oArr.GetEnumerator ();
while (oEnum.MoveNext ())
{
if ((((Contracts)oEnum.Current).Comp & 2) == 2 )
oRetArr.Add(((Contracts)oEnum.Current).ContractName );
}
}
if (ContextUtil.IsCallerInRole("managers") )
{
System.Collections.IEnumerator oEnum = oArr.GetEnumerator ();
while (oEnum.MoveNext ())
{
if ((((Contracts)oEnum.Current).Comp & 1) == 1 )
oRetArr.Add(((Contracts)oEnum.Current).ContractName );
}
}
return oRetArr;
}
Running the demo for the first time will create the COM+ application with
roles. You will see just part of the data since we set everyone group to users
role. Now add with COM+ MMC to manager role everyone group and just refresh the
page. This time you will see all the contracts since your user now is part of
managers.
Although it's not directly connected to COM+ I just want to mention that using
role base security can achieve compartmentalization of data via manipulation of
visual control. ASP.NET function IsUserInRole can be use to determine the user
role and controls can be set to visible or invisible as a result of that
determination. ASP.NET does the job with .Net implementation of role base
security. .Net mechanism is more flexible since you can use it to work with
users that aren't windows accounts, its more integrated into .Net component
(functions, interfaces, attributes, classes) and can be impose not just in run
time. Note that .NET Framework and COM+ role-based security mechanisms are
independent, and you can use only one mechanism within a single application.
(m) Increase server output and application income
.Net un-synchronic mechanism imposes both the caller and called object to be
alive when un-synchronic call occurred. Some times this behavior can cause our
application to run into unstable situation since that called object isn't
available for some reason. Even if we will catch that situation and will notify
our user that the service isn't available it will still cause financial damage.
To overcome this situation we can save the request details and handle it later.
But we need to find mechanism to ensure availability of the cached request
until the application successfully process the user request.
Instead of implement such mechanism you can use COM+ queued components (QC). QC
on behalf of you calls MSMQ queue with component name, method to activate and
parameters. As a result COM+ will activate the request component on the request
server. Writing message into MSMQ enables us to call component a-synchronically
since there isn't any connection between caller and called object. Request to
activate object data exists in MSMQ queue and will stay there as long as COM+
won't activate the request COM+ component. Writing message to MSMQ also ensure
us that the message will be persist until retrieval of the message from the
queue.
Let's see how using COM+ QC will help us in real life scenario. Assume that you
are building E-commerce application that enable users to buy goods. User
selects his goods and then submits them for process on the server, along with
his credit card details. The server beginning long workflow starting with
credit card validation and ending with notify the customer that the goods
shipped. The order process, of course, builds from several components: credit
card validation, validation of goods existence, credit card debit, send order
request to storehouse and inform customer about goods shipment. That's a long
process to go through with several potential fails points. This process can be
split into two parts front end and back end. Front end responsible for getting
the user request, ensure goods existence, validate credit card and debit. Back
end part responsible for other order process tasks. Separating back end tasks
components co-existence is obvious. Every task takes its own time, result in a
need to call next process component just when given task ends. Register back
end components as QC ensure that the process continue to work even if one of
the tasks components is temporary unavailable.
Front end component can be handling in two ways. They can be process in
sequence order until credit card debit, or also split to unconnected
components. Choosing the second method present advantages as you will see. The
debit component needs to call credit card Company to process the credit card
debit. This process is usually engaged with network call to the credit card
company that can cause long time period and unavailability of credit card
company service. Long response time can end up with server ability to handle
fewer requests. Furthermore unavailability of Credit Card Company won't enable
the server to fulfilled client request. Thus separating the credit card debit
component and register it as COM+ QC can solve lot of problems and contribute
to the server production. Our ASP.NET application will get the request and
validate the credit card. Then request to continue the request process will be
sending to the QC debit component via MSMQ and the client will be notifying
that his request received and being process. When the debit component finish
its work an e-mail notification will be sends to the client and the request
will be send to the next QC component to continue the process.
To see what the actual effects of that theory are we will build new page and
classes that will demonstrate such e-commerce application. We will build just
the front end components and run the sample with debit component as QC
component and as regular components, collect RPS and watch the results. Figure
3.0 shows the general design of our e-commerce application. Note that option
"A" register debit component as QC.
Figure 3.0
We will add two pages (BuyQC.aspx, BuyNoQC.aspx) to AvailabilityCheck web
project to simulate the debit action with or without Queued Component.
BuyNoQC.aspx will call new class (MyClass) that I add to AvailabilityCheckDll
library with one function that simulated the debit action simply by suspend the
thread for 1 second. The more interesting part is BuyQC.aspx which calls new
assembly (QCClass) and class (MyClass), with the same function as I add to
AvailabilityCheckDll library. MyClass will be register as queued component due
to using of ApplicationQueuing attribute, that set to enable queuing and
listening, at AssemblyInfo file.
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationQueuing(Enabled = true,QueueListenerEnabled = true)]
[assembly: ApplicationName("QCDemoSrv")]
[assembly: AssemblyTitle("")]
The second step is to create an Interface and mark it with InterfaceQueuing
attribute to enable queuing support. Both the queued component and calling page
will use that interface as their calling contract. When you design Interface
that will be used for queued components remember no to use out or ref
parameters due to the a-synchronic working pattern of QC.
[InterfaceQueuing]
public interface IMyInterface
{
void CreditDebit(string CardNumber, double amount);
};
Finally we build up a class that will be inheriting from ComponentServices and
implement IMyInterface interface. Note that it's a regular class, nothing
should be done in order to use it as queued component. I just add security
attribute to the class to enable it working with the default security enabling
of COM+ server application registered by CLR 1.1.
[ComponentAccessControl(true), SecurityRoleAttribute("users",true)]
public class MyClass : ServicedComponent,IMyInterface
{
public MyClass()
{}
public void CreditDebit(string CardNumber, double amount)
{
System.Threading.Thread.Sleep (1000);
// Optional – write to eventLog
//System.Diagnostics.EventLog.WriteEntry ("QC check","Debit finished");
MessageBox.Show("Debit finished","Queued Component");
return;
}
}
To use lazy registration of our COM+ application I add instantiation of the
class in Application_OnStart event. Those lines of code save us from the need
to start the COM+ application manually from COM+ MMC.
protected void Application_Start(Object sender, EventArgs e)
{
QCAssembly.MyClass o = new QCAssembly .MyClass ();
System .EnterpriseServices .ServicedComponent .DisposeObject (o);
}
BuyQC.aspx is calling the queued component class by using BindToMoniker function
of Marshal class. A moniker acts as a name that uniquely identifies a COM
object. You can find good explanation on Queued Components moniker syntax in
MSDN.
I use the syntax to create object from local queue but you can use the syntax
to call remote queue. After getting the object and casting it to IMyInterface
interface we can use the interface methods and release the object from COM+.
private void Page_Load(object sender, System.EventArgs e)
{
IMyInterface myObj;
myObj=(IMyInterface)System.Runtime.InteropServices.Marshal.
BindToMoniker("queue:/new:QCAssembly.MyClass");
myObj.CreditDebit ("423423423423",10.0);
System.Runtime.InteropServices.Marshal.ReleaseComObject(myObj);
Response.Write("Order received and processed");
}
This page simply use queued component to generate new message into the component
queue and return indication that the process done. When the queued component
listener find new message inside the component queue COM+ will activate and run
the given function with given parameters, ending up with messagebox popup.
Below (table 2.0) you can see results of running BuyQC.aspx, and BuyNoQC.aspx
using ACT against 5, 10, 50 concurrent users. As you can see queued component
scenario is at least 17 times faster when performed task take 1 second.
Table 2.0
| Number of simulate users |
BuyNoQC.aspx |
BuyQC.aspx |
| 5 |
5(RPS) |
212(RPS) |
| 10 |
10(RPS) |
216(RPS) |
| 50 |
12(RPS) |
211(RPS) |
MQ components can be mark as transactional components ensuring that components
request won't be removed from Queue until the function finished its work and
successfully end transaction flag is raised.
(n) Monitoring and squeezing licenses usage
One of the common issues especially inside enterprises is licenses usage. Most
of the third party components that used by IT department are based on licensing
agreement usually with limitation of the licenses concurrent usage (such as
ESRI products). Our IT department needs to ensure that the license policy is
enforced and that enforcing license policy won't cause any user working with
the product to receive error due to license policy validation. This section
will show you how you can achieve those goals and even enable usage of few
licenses by much many users by using COM+ features.
To deal smoothly with license issue we can use COM+ polling service. Creating
component that will use the license product and register this component as COM+
server application using object pooling can let administrator to control
license usage. Setting maximum pool size to specific number enables COM+ to
create that number of objects where each and every one of them using one of
third party application license. Furthermore COM+ queue requests that exceed
the specific objects that set by administrator until one object finished his
services. This ability save us the code needed to be writing for prevents
errors if license not available, to process client request.
Using COM+ not just let administrators to control dynamically the maximum
number of pooled object. By using Just In Time Activation (JITA) COM+ can serve
many users by few components object thus enable many users to use few licenses.
JITA actually “connect” client to object just when the client call to one of
the objects methods or properties. On the first call to the object COM+ creates
context object for the client but a real object creates only upon the client
first request for object properties or methods. Before COM+ creates an object
for client request COM+ check if such an object already exist and isn't serve
any call. If such an object found COM+ use that object instead of creating new
one. As you can see JITA behavior lets COM+ to use much less components to
serve much more clients.
COM+ object pooling let the programmer to decide programmatically if an object
will return to the pool or not by overloading CanBePooled protected method.
This opportunity enables programmers to get out malicious or misbehaved object
from the pool.
For this sample I created simple COM EXE C++ application. I add a member data
to CComModule class for licenses usage and override FinalConstruct and
FinalRelease COM class methods to imitate license application that limited to
two licenses.
Figure 4.0

STDMETHODIMP CLicensesApplicationCls::FinalConstruct ()
{
if (_Module.UsageCount == 2)
{
Error(L"No more licenses available for usage!",
IID_ILicensesApplicationCls,(HRESULT)-111111);
return (HRESULT)-111111;
}
_Module.UsageCount += 1;
return S_OK;
}
void CLicensesApplicationCls::FinalRelease ()
{
_Module.UsageCount -= 1;
}
To work smoothly with the EXE application I use tlbimp.exe and sn.exe utilities
to create managed wrapper for the COM EXE application. Next step is to create
two new COM+ class LicensesUsageCls and LicensesUsagePoolJitaCls. One for using
COM object with Pool set to maximum two objects and JITA options and the other
without.
[ObjectPooling(true,2,2), JustInTimeActivation (true),
EventTrackingEnabled (true)]
public class LicensesUsagePoolJitaCls : ServicedComponent
{
public LicensesUsagePoolJitaCls()
{
}
public void UseOneLicense()
{
licensesapplicationNet.LicensesApplicationClsClass oLA = new
licensesapplicationNet.LicensesApplicationClsClass();
oLA.GetName();
}
}
public class LicensesUsageCls : ServicedComponent
{
public LicensesUsageCls()
{
}
public void UseOneLicense()
{
licensesapplicationNet.LicensesApplicationClsClass oLA = new
licensesapplicationNet.LicensesApplicationClsClass();
oLA.GetName();
}
}
What left are two pages, each one of them will call its equivalent class.
public class LicensesUsage : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
try
{
LicensesUsageClass.LicensesUsageCls o =
new LicensesUsageClass.LicensesUsageCls();
o.UseOneLicense ();
System.EnterpriseServices.ServicedComponent.DisposeObject (o);
}
catch(Exception Err)
{
throw Err;
}
}
}
public class LicensesUsagePoolJita : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
LicensesUsageClass.LicensesUsagePoolJitaCls o =
new LicensesUsageClass.LicensesUsagePoolJitaCls ();
o.UseOneLicense ();
System.EnterpriseServices.ServicedComponent.DisposeObject (o);
}
}
To check the pages behavior we will stress them using ACT with at least 5
concurrent users. You will find out that LicensesUsagePoolJita page run without
any errors while LicensesUsage return approximately 10% of errors due to
licenses over use. If you will inspect the RPC you will see that using this
technique we enable approximately 26 concurrent users to use our 2 license
application.
About Natty Gur
Natty Gur is free consultant specialized in architecture and developing system
of systems using ASP.NET. Natty has 12 years of architecting, designing
developing and distributing software experience, mainly focusing on enterprise
application Involving legacy applications in open systems. Natty available for
short contract offers (complex problems highly appreciate). To contact Natty
e-mail him at natty@dao2com.com or call 972-52-8888377. Read his blog at:
http://weblogs.asp.net/ngur