Shiv Kumar
 Hobbyist Filmmaker / Editor

The Multi-Module ISAPI Framework

Not Rated YetNot Rated YetNot Rated YetNot Rated YetNot Rated Yet0votes
January 09, 2008 02:39 PM  Views: 1233   Favorited: 0 Favorite It Comments: 0
Filed Under:  Programming
Tags:  Delphi, ISAPI
 
The Matlus Multi-Module ISAPI Frame work allows you to use multiple WebModules in your ISAPI Projects. Due to this, team development of ISAPI applications has been made simple/possible, since each team member can work with her own set of modules to be later seamlessly merged with the main application. But that's not all. The Matlus Multi-Module ISAPI framework has a number of features (that are lacking in WebBroker) built into it. This framework has been built with robust, Industrial strength applications in mind, thus providing required features out of the box. Complimenting the Multi-Module framework is a component suite enables building such systems

Basic Concepts

Basically, there is a Multi-Module Project Expert that plugs into the IDE. The options you have in the Expert are:
Showing the Multi-Module ISAPI Expert Dialog
  1. The Number of Slave Web Modules for Multi-Module ISAPI project
  2. Addtional Components (that are part of my package)
  3. Output Folder (such as C:\inetpub\scripts)
  4. The Host Application (used for debugging)
exclamation.gif It should be noted that currently, only the default settings are implemented. That is you can build a multi-module ISAPI extension, you can choose the number of slave modules and you can determine the output folder of the compiled dll and the host application.

The expert then builds one Master WebModule (You can have only one Master WebModule per project), and one or more Slave WebModules (in reality, you don't want any slave modules, until you've named your Master Web Wodule. The reason for this will be explained later). There is also a New Slave Module Expert so you can add additional slave modules at a later time.

The Multi-Module framework architecture

The Master WebModule unit the Project Expert Creates
{**********************************************}
{  Multi-Module Support for ISAPI Applications }
{       Developed by Shiv R. Kumar (2001)      }
{              Master Web Module               }
{**********************************************}
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, MsMultiModule, HTTPApp;

type
  TMsMasterWebModule1 = class(TMsMasterWebModule)
    procedure MsMasterWebModuleCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  MsMasterWebModule1: TMsMasterWebModule1;

implementation

{$R *.DFM}


procedure TMsMasterWebModule1.MsMasterWebModuleCreate(Sender: TObject);
begin
{***************************************************************}
{ If you add Slave WebModules to your project, be sure to       }
{ to include a call to the ModuleFactory of the Salve WebModule }
{ Example usage of ModuleFactory                                }
{ ModuleFactory(TMsSlaveWebModule2, True, True);                }
{***************************************************************}
end;

end.

The Dispatching Mechanism

The Master WebModule is an extension of the normal WebBroker WebModule. Therefore it has actions, that function similar to the normal WebModule. Slave WebModules are almost identical to a regular WebModule and so also have their own actions. Both kinds of Web Modules can also have default actions.

The Master WebModule's dispatcher fields all requests made on the ISAPI Dll. The Master WebModule's dispatcher handles dispatching requests either to itself, or the other Slave WebModules.

When a request comes in, the Master dispatcher first tries to determine if the request is for one of it's own actions. If so, it dispatches it to the relevant action. If not, it then tries to determine if the request is for an action in one of the Slave WebModules that have been registered with it. It does this by matching the Class Name (not including the "T") of each of the Slave WebModules and the Request URL (PathInfo).

Lets assume the name of the ISAPI dll is Multimodule.dll. A URL like this:

http://www.matlus.com/scripts/multimodule.dll/MyPathInfo
will be dispatched to the Master WebModule's action whose PathInfo is /MyPathInfo. If an action with this PathInfo does not exist it will dispatch it to it's default action. On the other hand, a URL like this:
http://www.matlus.com/scripts/multimodule.dll/SlaveModule1/MyPathInfo
will be dispatched to the Slave WebModule whose ClassName is TSlaveModule1. The Master WebModule's dispatcher does not try and determine if a corresponding action exists in the Slave WebModule. It simply hands the request over to the Slave WebModule. The Slave WebModule then dispatch the request to the appropriate action. If no action exists, with a matching PathInfo the Slave WebModule's dispatcher will dispatch it to its default action if one exists. If a default action does not exist, the Request is bounced back to the Master WebModule and will then be handled by the default action of the Master WebModule. The actual handling of a request once it has been dispatched to an action is identical to the way you'd handle it in a normal WebBroker application. That is to say, you code the OnAction event of the actions you define to get the job done.

Session Management

The Session Management is comprised of a TMsSessionInfo object. This object holds session information for each session. A TMsSessionBroker object. This object is a thread safe hash table that maintains the list of TMsSessionInfo objects and manages the expiration of sessions etc. For the programmer, the Master WebModule surfaces a TMsSessionManager object. The SessionManager object's methods are similar to the SessionBroker, in that it allows access to an instance of TMsSessionInfo, thereby giving the programmer access to any information stored in the session. Besides a list of Name-Value pairs, the SessionInfo object also has a "Data" property (Pointer).

The Session Management has been built with Remote Session Management in mind and will allow session information to reside across application and machine boundaries. (there is a Port and Server property available at design time as part of the Master WebModule). This feature has not been implemented as yet, but the hooks are in place.

Here is a class declaration of the SessionManager object.
  { TMsSessionManager }
  TMsSessionManager = class(TPersistent)
  private
    FSessionManagerIndentifier: string;
    FSessionManagerTimeOut: DWORD;
    FSessionBroker: TMsSessionBroker;
    FCurrentSessionID: string;
    FServer: string;
    FPort: string;
    FOnBeforeDeleteSession: TOnBeforeDeleteSession;
    procedure SetSessionIdentifier(const Value: string);
    procedure SetSessionManagerTimeOut(const Value: DWORD);
    function GetSessionManagerTimeOut: DWORD;
    procedure SetData(Value: Pointer);
    function GetData: Pointer;
    procedure SessMgrOnBeforeDeleteSession(Sender: TObject; SessionID: string; Data: Pointer);
  public
    constructor Create;
    destructor Destroy; override;
    function CreateSession: string; overload;
    function CreateSession(const TimeOutInMinutes: DWORD): string; overload;
    procedure ClearSessionInfo;
    procedure AddSessionInfo(const Name, Value: string);
    function GetSessionInfo(const Name: string): string;
    procedure DeleteSessionInfo(const Name: string);
    procedure DeleteSession;
    function GetSessionData: TStrings;
    property CurrentSessionID: string read FCurrentSessionID Write FCurrentSessionID;
    property Data: Pointer read GetData Write SetData;
  published
    property SessionTimeOut: DWORD read GetSessionManagerTimeOut write SetSessionManagerTimeOut;
    property SessionIdentifier: string read FSessionManagerIndentifier write SetSessionIdentifier;
    property Server: string read FServer write FServer;{ TODO -oShiv -cFuture : To be Implemented }
    property Port: string read FPort write FPort;{ TODO -oShiv -cFuture : To be Implemented }
    property OnBeforeDeleteSession: TOnBeforeDeleteSession read FOnBeforeDeleteSession write FOnBeforeDeleteSession;
  end;

The Session Management can use Fat URLs, Cookies or Hidden fields. The order of precedence is in that order. By default, the SessionIdentifier is sid, which means when you pass the session id back and forth between the browser and ISAPI, it should be in the form:

  
    http://www.matlus.com/scripts/multimodule.dll/somepathinfo?sid={12805C36-7ABE-4E4D-B6B5-31C3CA0B9BA8}
  
In code however, you would do it like this:
  
    http://www.matlus.com/scripts/multimodule.dll/somepathinfo?sid=
  

If you're using cookies, then it should be in the form

  
    sid={12805C36-7ABE-4E4D-B6B5-31C3CA0B9BA8}
  

If you're using a hidden field then the name of the field should be sid.

The Master WebModule has a property called SessionIdentifier. The value is this property is sid by default. If you change this property to something else, be sure to use this new value everywhere including the special tag .

Creating a new session is as simple as:
  
    SessionManager.CreateNewSession;
  

The CreateNewSession method has a default parameter called SessionTimeOut. This property is defaulted to whatever the Master WebModule's SessionTimeOut property is. The Session Manager allows for each session to have it's own timeout. If you need to use this feature, you simply set the value at the time of creating a new session. The timeout value is in minutes.

When you create a new session, the SessionManager.CurrentSessionID is set to a GUID. You should hardly ever need to use this property, since where you need to put the GUID (in html) you can simply use the special tag .

When a Request comes in, the Session Manager automatically puts you in the context of the session. So any operations you do with respect to the Session Manager is with the session that was identified in the URL (or cookie or hidden field).

In practice, you never have to put the GUID in the URL yourself. If the html you send as a Response is in the form

  
    http://www.matlus.com/scripts/multimodule.dll/somepathinfo?sid=<#sid>
  

The Master WebModule will automatically replace the <#sid> tag with the GUID of the current session. So no matter where you put the <#sid> tag in your html, it will be replaced by the Master WebModule's in built PageProducer. This PageProducer understands some other tags as well. Such as:

  • <#scriptname> is replaced by: Request.ScriptName
  • <#sid name="NameOfSessionItem"> is replaced by the Value of the item
  • <#sid name="sessioninfo"> is replaced by an html page listing all the name=value pairs of the current session

Tagging objects with Sessions

The ability to tag an object with the session is really useful. I've used this to tag a DataModule that contains other component/objects and has it's own methods etc. This ability allows different users to have their own connections to databases, and data access objects, where you might need to have different users connect to different databases (or with different user names). I've used this also in cases where a TMsDataSetTableProducer (the component produces "pages" of a result set, with Paging and navigation etc.) can be used for search results, where each user can potentially get totally different result sets. Besides the fact that you don't need to query the database each time for each user, since each user gets her own instance of a dataset, the cursor remains where you left it, and as a result, you don't need to navigate to the first record only to move to the 200th record say. This speeds the response of the application quite significantly. All in all, Session Management without the ability to tag objects is quite useless in the "real world".

Of course, what good is the ability to tag objects with a session if you don't have the ability to free these objects when the session is deleted? The Master WebModule surfaces an OnBeforeDeleteSession for this purpose. The event prototype looks like this:
The OnBeforeDelete event of the Master WebModule.
procedure TMsMasterWebModule1.MsMasterWebModuleBeforeDeleteSession(
  Sender: TObject; SessionID: String; Data: Pointer);
begin

end;

Since the Data property is a pointer, you are free to tag any object to the session. It could be an ObjectList or a DataModule that has other objects and Objects lists.

Additional Events

In addition to the events of a normal WebModule, the Master WebModule publishes the following events:

Other Events the Master WebModule surfaces.
    property OnModuleHTMLTag: TOnModuleHTMLTag read FOnModuleHTMLTag Write FOnModuleHTMLTag;
    property OnException: TExceptionEvent read FOnException write FOnException;
    property OnSessionInvalid: TOnSessionInvalid read FOnSessionInvalid write FOnSessionInvalid;
    property OnSessionExpired: TOnSessionExpired read FOnSessionExpired write FOnSessionExpired;
    property OnBeforeDeleteSession: TOnBeforeDeleteSession read FOnBeforeDeleteSession write FOnBeforeDeleteSession;

Signatures of some of the additional events.
  TExceptionEvent = procedure (Sender: TObject; E: Exception; var Handled: Boolean) of object;
  TOnSessionInvalid = procedure (Sender: TObject; SessionID: string) of object;
  TOnSessionExpired = procedure (Sender: TObject; SessionID: string) of object;

The Master WebModule has a few "helper" methods as well. They are:

    function ExtractMultipleRequestFields(const FieldName: string): TStrings;
    function GetRequestFieldValue(const FieldName: string): string;

The ExtractMultipleRequestFields coms in handy when you have a multi-select list box in a form and you need to find out which items were selected (The WebBroker framewrork does not support this out of the box). This method (given the field name) returns the StringList containing all the Values that were selected in the list box.

The GetRequestFieldValue makes processing Requests easier since it decouples code from the need to know the type of method (GET or POST) that was used. Frequently, one needs to process boths kinds of Requests in a certain action. Internally, this method determines the method type of the Request and uses either the Request.ContentFields property or the Request.QueryFields property and returns the Value for a given Name.

The (read only) property ModuleName returns the Path and File name of the ISAPI Dll. The read access method of this property uses the GetModuleFileName API.

Handling Special tags - A "Catch All" OnHTMLTag event (built in Page Producer)

Every Response, leaving the application passes through the Master WebModule's internal PageProducer. Besides, processing the tags it understands, as mentioned earlier, the programmers has the opportunity to process tags as well. In case the programmer replaced some tags with tags that this PageProducer understands, these tags will be replaced once again. This makes is really convenient when designing applications, since no matter where (which module) you put these tags, they can be processed at a single point if required. Or no matter where you put in the special tags that the Session Manager understands (as listed above), they are guaranteed to be processed by the Master WebModule.

Communicating between Slave WebModules and the Master WebModule

To create a new Slave Module, you use the New Slave WebModule Expert. This expert creates a WebModule whose class looks like this:

The unit generated by the New Slave WebModule Expert.
{**********************************************}
{  Multi-Module Support for ISAPI Applications }
{       Developed by Shiv R. Kumar (2001)      }
{               Slave Web Module               }
{**********************************************}
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Classes, MsMultiModule, HTTPApp, Unit1;

type
  TMsSlaveWebModule2 = class(TMsSlaveWebModule)
    function GetMaster: TMsMasterWebModule1;
  private
    { private declarations }
  public
    property Master: TMsMasterWebModule1 read GetMaster;
    { public declarations }
  end;

var
  MsSlaveWebModule2: TMsSlaveWebModule2;

implementation

{$R *.DFM}

function TMsSlaveWebModule2.GetMaster: TMsMasterWebModule1;
begin
  Result := TMsMasterWebModule1(MasterWebModule);
end;

end.

Notice that the expert has included the Master WebModule's unit (Unit1 in this case) in the uses clause of the Interface section. Notice also, that the (read only) property Master, returns a type TMsMasterWebModule1. This happens to be the class name of the Master WebModule at the time of creating the new Slave WebModule.

exclamation.gif It is therefore recommended that one should first name and save the Master WebModule (class and unit) before creating additional Slave WebModule. The Expert will then use the unit name and class name when generating the code for the Slave WebModule class.

So essentially, All Slave WebModules, have a property called Master, that returns a reference to the Master WebModule instance. So when you need to reference the Master WebModule's Session Manager, your code would look like this:
Referencing the Master WebModule's Session Manager from a Slave WebModule.
procedure TMsSlaveWebModule2.MsSlaveWebModule2WebActionItem1Action(
  Sender: TObject; Request: TWebRequest; Response: TWebResponse;
  var Handled: Boolean);
begin
  { Adding "ProductID" to the Current Session as received from a Posted Form }
  Master.SessionManager.AddSessionInfo('ProductID', Master.GetRequestFieldValue('ProductID'));
end;

Registering Slave WebModules

Each time you add a new Slave WebModule to your project, you need to register it with the Master Module's Module Factory. An example of the code you need to use is provided as comments in the OnCreate event of the Master WebModule. For the Slave WebModule class shown above, the code would look like this:
Registering Slave WebModules with the Master WebModule.
  ModuleFactory(TMsSlaveWebModule2, True, True);

Comments have been Disabled for this post





Leave A Comment

Shiv Kumar
Gainesville, Virginia,
United States
Member Bio Member Skills/Specialization

Bio

close

Specializations

close
Photographer
Landscape
Nature
Portrait
Videographer/Cinematographer
Interview
Landscape
Nature
Portrait
 
Privacy Policy | Terms Of Service | Contact Us | Support | Help/FAQ | News