Programmer to ProgrammerTM  
Wrox Press Ltd  
   
  Search ASPToday Living Book ASPToday Living Book
Index Full Text
 
ASPToday Home
 
 
Bullet HOME
Today TODAY
Index INDEX
Full-text search SEARCH
Reference REFERENCE
Feedback FEEDBACK
Advertise with us ADVERTISE
Subscribe SUBSCRIBE
Bullet LOG IN
                         
 
 
  Register for FREE Daily Updates:  
  Email:  
 
  The ASPToday Article
February 20, 2001
      Previous article -
February 19, 2001
   
 
   
   
   
Create Your Own Scalable Session Object    Chalam  
by Chalam
 
CATEGORY:  Site Design  
ARTICLE TYPE: In-Depth Reader Comments
   
    ABSTRACT  
 
Article Rating
 
   Useful
  
   Innovative
  
   Informative
  
 13 responses

One of the issues in web development is how to create a coherent application with several stand-alone HTML pages. Since HTTP is a stateless protocol, each request to the server is taken as an independent request. ASP's intrinsic session objects provide an easy way to maintain the state of the application for each user, which requires no special programming. The state is maintained by using a sessionid cookie in the client (browser) and its related state information in web server memory. In this article, Chalam teaches you how to implement your own scalable session objects.

   
                   
    Article Discussion   Rate this article   Related Links   Index Entries  
   
 
    ARTICLE

One of the issues in web development is how to create a coherent application with several stand-alone HTML pages. Since HTTP is a stateless protocol, each request to the server is taken as an independent request.

ASP's intrinsic session objects provide an easy way to maintain the state of the application for each user, which requires no special programming. The state is maintained by using a sessionid cookie in the client (browser) and its related state information in web server memory.

However, we have been warned about the inability of session objects to work in web farms. Though, using a local director with a "sticky bit" set on maintains session in web farm, it is does not provide true load balancing. There are several out-of-box solutions to overcome this issue, such as software artisan's SA-Session Pro. These solutions are developed in COM and use database or NT files to store the session variables. All the servers in the farm use the same database or the file to retrieve the state information.

One of the most nagging questions in all these solutions is, "is it scalable ?" ASP Intrinsic session objects work well in single server co-hosting, but fail in web farms. Database-driven COM session objects work in web farms, but have severe restrictions in co-hosting. The third party co-hosting provider will not allow the COM DLLs to be registered in their machine. Co-hosting can even be forced on big-sites when they move in for mirror-site in other regions.

Solution

The scalability can be achieved by splitting the application into n-tiers, and using an independent language to develop each tier:

image1

User-Interface Visual Control Objects are plain ASP files, which use the Common Business Object for processing. Common Business Objects and Data Access layers can either be ASP files or COM DLL.

The next obvious question will be, "Can we write a business logic object in pure script as the co-host will not allow COM". Amazingly, YES is the answer - we can do it in VBScript Version 5 and above. You can define class with properties and methods in ASP/VBScript like this:

  <%
     Class myClass

     Private myVar1

     Property Get var1
        var1 = myvar1 
     End Property 
     Property Let var1(value) 
        mstrSessionID = Trimvalue 
   End Property

     Public Function myFunc()
     'blah blah.
     End Function

     End Class
  %>

If say, later, there is a machine that can run COM objects, we can 'port' the CBO code to visual basic with some minor modification and compile scripts. Compiled COM objects provides more security as even a read access (like the recent IIS bugs) to the web server will not expose the business logic.

To implement the scalable session objects, three flavors are discussed here. Flavor 1 uses ASP/VBScript classes and database for storing the session variables. This can be employed in web farms. Flavor 2 is also written in ASP/VBScript but it uses memory for storing the session variables. This implementation is apt for single-server. Flavor 3 is developed using VB/COM and database for persisting the session variables. This flavor will provide good performance in Web farms.

Flavor 1

ASP/VBScript with Database

Common Business Object (clsSession.asp) defines a class cSession with properties

Methods

Class_ Initialize: This function will be called when the object is created. The function tries to retrieve the sessionid from the client. If the sessionid is not found or the corresponding session variables are not found in the server, it creates a new session.

Private Sub Class_Initialize() 
   InitializeVars 
   if RetriveSessionID Then 
      if Not Load(mstrSessionID) Then 
         CreateNewSession 
      End if 
      Else 
         CreateNewSession 
      End if 
End Sub

Private Sub InitializeVars() 
   If IsEmpty(mstrSessionID) Then 
      mstrSessionID = "" 
   End If 
   If IsEmpty(mstrLogIP) Then 
      mstrLogIP = "" 
   End If 
   mstrStatus = "" 
   flgDirty = false 
   Set mcolSessionVars = Server.CreateObject("Scripting.Dictionary") 
End Sub
 
Private Function RetriveSessionID() 
   RetriveSessionID = false 
   mstrSessionID = Getstring("SessionID", 38, false) 
   if Not Len(mstrSessionID) = 0 Then 
      RetriveSessionID = True 
   End if 
End Function

RetriveSessionID uses Getstring function to read the sessionid from client. Getstring is a locally defined function, which searches the given key in querystring, form post and in cookie. This function is defined in i_util.asp

Private Function CreateNewSession() 
   Dim flgStatus flgStatus = CreateSessionID 
   flgStatus = flgStatus and StoreSessionID 
   if flgStatus Then mstrStatus = con_Active 
      mdtLogDate =Date & " " & Time 
      mstrLogIP = Request.ServerVariables("REMOTE_ADDR") 
   End if 
   Save 
   CreateNewSession = flgStatus 
End Function 

Private Function CreateSessionID()
   CreateSessionID = False 
   mstrSessionID = GetUniqueId 
   mstrStatus = con_Active 
   flgDirty = True 
   CreateSessionID = True 
End Function

CreateNewSession will create a new sessionid, save the sessionid in client and then to the database. UniqueID function is defined in daSession.asp. The Save function in turn calls daSave, which is defined in daSession.asp

Con_Active is a constant defined in the same file, while FlgDirty is used to track the changes and to prevent unnecessary database transactions.

Public Function Abandon() 
   mstrStatus = con_InActive 
   mdtLogOutDate = Date & " " & Time 
   flgDirty = True 
   Save 
End Function

Abandon function will be called to delete the session - it'll first update the status flag and LogOutDate, and then save the information:

Public Function Load(strSessionID) 
   Load = daLoad(strSessionID) 
End Function 

Public Function Save() 
   if flgDirty and mcolSessionVars.Count > 0 Then 
      Save = daSave 
   Else 
      Save = False 
   End if 
End Function

The Load and Save functions help in persisting the data across the pages. Save will check for flgDirty to prevent useless repeated updates. As shown in the architecture diagram Common Business Objects does not know about the information storage and retrieval process. This functionality is handled in the separate layer (file) called Data Access Layer. The daLoad and daSave are functions defined in another file, daSession.asp. The advantage is, if we need to use memory instead of the database for persistence, or if there is a change in database design, then we need to change the file daSession.asp only.

In typical n-tier architecture, Data Access Layer and Business Layer communicate through a well-defined structure like ADODB.Recordset, or as a flattened string, or as XML. Here, for performance reasons we'll allow the Data Access layer to directly access the private variables of the Business layer. The file is included as shown:

  <%
     Class cSession

  %>
<!--#include file="daSession.asp"-->
  <%
     End Class
  %>

CAUTION: daSession.asp should be included before the End Class statement.

Data Access Layer

This layer is responsible for data storage and retrieval. It has two major functions - daLoad and daSave, which will be called from Common Business Object

The database has two tables, SessionHdr and SessionDet, with the following structure:

Table name: SessionHdr

Column Name Data Type Comments
SessionID Char(38) To store the session ID. Primary key
LogIP Char(16) The IP address of the client. For tracking purposes. Non-Null
LogDate DateTime Session Creation Date time
LogOutDate DateTime Log out DateTime. Typically the time when user clicks Logout in the web page
Status Char(1) Status of the Session A - Active I - Inactive This can be used to time-out the session. In a typical timeout, Logout Date will be set to NULL and status set to 'I'

Table name: SessionDet

Column Name Data Type Comments
SessionID Char(38) Foreign Key. Links SessionHdr table and SessionDet table.
Name Char(50) Name of the session variable to be stored. Ex, 'Username'
Value Char(100) Value of the session variable. Ex, 'Joe'

Private Function daLoad(strSessionID) 
   daLoad = false
   Dim rs 'as recordeset 
   Set rs = server.CreateObject("ADODB.Recordset") 
   rs.Open Replace(Application("qry_Session_Active_PK"), _
      ":1", strSessionID), _
      conDB, adOpenForwardOnly, adLockReadOnly 
   if not rs.EOF then 
      mstrSessionID = trim(rs.Fields("SessionID")) & "" 
      mstrLogIP = trim(rs.Fields("LogIP")) & "" 
      mdtLogDate = trim(rs.Fields("LogDate")) & "" 
      mstrStatus = trim(rs.Fields("Status")) & "" 
      Set mcolSessionVars = _ 
         Server.CreateObject("Scripting.Dictionary") 
      Do While Not rs.eof
         mcolSessionVars.Add trim(rs.Fields("name")), _ 
                 trim(rs.Fields("value")) 
            rs.movenext 
      Loop
      flgDirty = False
      daLoad = true 
   end if 
   rs.Close 
   Set rs = nothing 
End Function 

Private Function daSave()
   Dim tstrKey daSave = false 
   Dim rs 'as recordeset
   Set rs = server.CreateObject("ADODB.Recordset") 
   rs.Open Replace(Application("qry_SessionHdr_PK"), _
      ":1", mstrSessionID), conDB, _
      adOpenDynamic, adLockOptimistic 
   if rs.EOF then 
      rs.addnew rs.Fields("SessionID") = mstrSessionID 
   End if 
   rs.Fields("LogIP") = mstrLogIP 
   rs.Fields("LogDate") = mdtLogDate 
   rs.Fields("LogOutDate") = mdtLogOutDate 
   rs.Fields("Status") = mstrStatus
   rs.update 
   rs.Close
   'Save Detail... 
   For each tstrKey in mcolSessionVars.Keys 
      rs.open Replace(Replace(Application("qry_SessionDet_PK"), _
         ":1", mstrSessionId), ":2", tstrKey), _
         conDB, adOpenDynamic, adLockOptimistic 
      if rs.eof then 
         rs.addnew rs.fields("SessionID") = mstrSessionID 
      End if 
      rs.fields("name") = tstrKey 
      rs.fields("value") = mcolSessionVars.item(tstrKey) 
      rs.update rs.close 
   Next 
   Set rs = nothing 
   daSave = true 
End Function

If the same sessionid key is found in the header table, then the record is updated, else a new record is added. Again similar action is performed on the detail table. The query is stored in ASP Intrinsic Application Object. For scalability, Use stored procedures.

With this groundwork, coding front pages will be easy.

Visual control object (ASP file), an Interface Utility, will create the session and initialize and provide a routine for accessing the client information. It also controls the database connection (through a separate include file)

The location of Common Business Object and its implementation should be transparent to this object. If we are using ASP/VBScript for Common Business object then we should include the files.

<!-- #include File = "i_db_conn.asp" -->
<!--#include file = "clsSession.asp" -->

The i_db_conn.asp file will establish the connection to the database and provide the service through the variable conDB

Dim objSession 
   CreateSession()

Private Function CreateSession() 
   Set objSession = New cSession 
End Function

CreateSession will create the new cSession object and provide the service through objSession.

User Interface

Our ASP front end to try out this code will look like this:

image2

Once we have entered some session data, we can retrieve it:

image3

If any of the files need to use a session, they have to include i_util.asp and start using objSession as they use for the ASP Intrinsic session object. ObjSession need not be declared in the User Interface page.

For example, to store a data in the session:

ObjSession("myVar") = "myval"

To retrieve:

Response.write ObjSession("myVar")

To abandon:

ObjSession.Abandon

The object-oriented concept has concealed the implementation details and provides an easy programming model.

Flavor 2:

ASP/VBScript without Database

It is sane to ask, "If I'm running my site on co-host, why should I use a database for state maintenance" The architecture also allows you to scale down the program to maintain the state without the database, yet the front end programs continue to use objSession without any change. This is the key advantage of splitting the application into n-tier.

The Data Access Layer can be changed to store and retrieve data using ASP intrinsic session object. Common Business Object will have to amend include file name if there is a change in data access layer file name otherwise there is no change.

The new data access layer will look like this:

Private Function daLoad(strSessionID) 
   daLoad = false 
   mstrSessionID = Session.SessionID 
   mstrLogIP = Request.ServerVariables("REMOTE_ADDR") 
   mdtLogDate = Date 
   mstrStatus = "A" 'Always active 
   Set mcolSessionVars = Server.CreateObject("Scripting.Dictionary") 
   For iCount = 1 to Session.Contents.Count 
   mcolSessionVars.Add Session.Contents.Key(iCount), _
                                              Session.Contents.Item(iCount) 
   Next 
   flgDirty = False
   daLoad = true 
End Function 

Private Function daSave() 
   Dim tstrKey daSave = false 
   For each tstrKey in mcolSessionVars.Keys 
      Session(tstrKey) = mcolSessionVars.item(tstrKey) 
   Next 
   daSave = true
End Function

All other files remain unchanged.

Flavor 3

COM/VB With Database

This is the interesting part. Since ASP/VBScript and Visual Basic share the almost same syntax, it'll be easy to convert the ASP/VBScript code to Visual Basic.

ASP Intrinsic objects can be directly accessed from VB using OnStartPage function. However, it is often tough to debug the program. Therefore, we can avoid using ASP intrinsic objects in the VB/COM object and do some tinkering in our Visual Control Objects (i_util.asp) during instantiation.

Steps to create the COM component

Some common module files are also added (code is copied from ASP files). Next, compile the program.

In i_util.asp, change the Instantiating routine to the following:

Private Function CreateSession() 
   Set objSession = Server.CreateObject("Session.clsSession") 
   objSession.SessionID = GetString("SessionID", 38, False) 
   objSession.LogIP = Request.ServerVariables("REMOTE_ADDR") 
   objSession.Init 
   Response.Cookies("SessionID") = objSession.SessionID 
   Response.Cookies("SessionID").Expires = DateAdd("d", 1, Date) 
End Function

As the COM object cannot read the cookie, we provide the session id value retrieved from the cookie and then initialize the object. Init function will check the validity of sessionid and creates a new id if necessary. The sessionid is stored in the cookie after calling the init function. In the previous implementations, the clsSession directly accessed the cookie value and stored the session id.

Conclusion

We have created a session object that is scalable from co-hostable without database to using in web-farm. The scalability is achieved by using the object-oriented design concept's property of implementation concealment and near common syntax of ASP/VBScript with visual basic. In all the flavors, the user-interface files are not changed. They have to include i_util.asp and use the objSession object for state maintenance.

However, when using VBScript for class definitions and object implementation, there will be a performance hit. The solution can be effectively used for sites, which starts in co-hosting environment and has immediate plans for dedicated/multiple hosting.

The security part of session is not handled in the program. Sessionid is directly stored on to cookie, which will expose the key of the database table. With some tweaking, it'll be possible to read other client's session values. It is strongly advised to encrypt the SessionID before storing on to the cookie. This will involve additional coding in Data Access Layer functions.

Using ASP Intrinsic session (flavor 2) will face a performance hit, as data is stored in two places, in ASP intrinsic session and clsSession. The idea is to use this a stepping-stone for an easy expansion at a later stage.

The full source code is provided. Please look at readme.txt file for implementation details. You'll need ServerObject's Guidmaker for unique id creation. The component is a free download and is available in http://www.serverobjects.com/products.htm#free?WROXEMPTOKEN=711612ZW37kEJTrwI1rvrPIrYD. You can also use database provided unique id.

 
 
   
 

RATE THIS ARTICLE

GUID Generation on Server Tuesday
20 February 2001
3:38 CST
The article is good enough for starting it up. It could be done better without the external guid-generation component. Try to use the build in function from sql-server GETGUID. e.g. with: "select @sSessionID = REPLACE(CONVERT(varchar(255), NEWID() ) ,'-','')" you will get a perfect GUID that can be used as cookie (after encryption) and in url-encoding. This small peace of T-Sql code, integrated in one StoredProcedure for creating the entry on the session table on sql-server will do much better. Additionally, try to use SQL-commands for inserting the data, not dynamic recordsets, this is really slow. Again, don't use more than one table on sql-server for this.

  Please rate this article (1-5). Was this article...
 
 
Useful? No Yes, Very
 
Innovative? No Yes, Very
 
Informative? No Yes, Very
 
Brief Reader Comments?
Your Name:
(Optional)
 
  USEFUL LINKS
  Related Tasks:
 
 
   
  Related ASPToday Articles
   
  • ASP session management in a load balanced environment (January 18, 2000)
  •  
           
     
     
     
           
      Search the ASPToday Living Book   ASPToday Living Book
     
      Index Full Text Advanced 
     
     
           
      Index Entries in this Article
     
  • ASP/VBScript without database flavor
  •  
  • business layer
  •  
  • business logic
  •  
  • business logic objects in script with no COM
  •  
  • COM/VB With database flavor
  •  
  • Common Business Objects
  •  
  • communication with business layer
  •  
  • creating
  •  
  • cSession class
  •  
  • data source tier
  •  
  • data source tier, interacting with
  •  
  • data storage and retrieval
  •  
  • deleting
  •  
  • information storage and retrieval process
  •  
  • Interface Utilities
  •  
  • Item property
  •  
  • LogDate property
  •  
  • LogIP property
  •  
  • LogOutDate property
  •  
  • methods
  •  
  • reading
  •  
  • retrieving from client
  •  
  • SA-Session Pro
  •  
  • scalable
  •  
  • scalable Session objects
  •  
  • Session object
  •  
  • SessionID property
  •  
  • SessionIDs
  •  
  • sessions
  •  
  • SessionVars property
  •  
  • state
  •  
  • Visual control object
  •  
  • web farms
  •  
     
     
    HOME | TODAY | INDEX | SEARCH | REFERENCE | FEEDBACK | ADVERTISE | SUBSCRIBE
    .NET Framework Components Data Access DNA 2000 E-commerce Performance
    Security Admin Site Design Scripting XML/Data Transfer Other Technologies

     
    ASPToday is brought to you by Wrox Press (http://www.wrox.com/). Please see our terms and conditions and privacy policy.
    ASPToday is optimised for Microsoft Internet Explorer 5 browsers.
    Please report any website problems to [email protected]. Copyright © 2001 Wrox Press. All Rights Reserved.