Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

COM+ and .NET - A practical approach - Part 2

0.00/5 (No votes)
2 Mar 2004 1  
A look at COM+ and .NET

(c) 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

Sample screenshot

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.

(d) 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

Sample screenshot

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 (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cossdk/htm/pgservices_queuedcomponents_786q.asp). 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.

(e) 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

Sample screenshot

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.

Next Part

Previous Part

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here