Sharing Configuration Data Among
ASP Applications (con't.)
By Shelly Powers
Scroll to the bottom of this page - to the Attachments section - to download a zip file containing code related to this article.
As The Widget Shop can grow in time, there could end up being several Web pages within each ASP application for the site. Because of this, whenever the site schema is changed, we don't want to have to go into each page and change all of the images, stylesheets, and JavaScript files. Neither do we want to have to rename all of the stylesheets, JavaScript files, and images to common central names, which are then hard coded into the ASP pages. Both approaches can become cumbersome and both are prone to errors -- it's too easy to forget to move over all of the images, or all of the scripts, and to remember to change all of the file names, and so on.
Instead, we add placeholders to the site's ASP pages for the individual schema-based items, and pull the information about what image or CSS file or JavaScript file to use from the Application object.
The Widget Shop's ASP application directories each have a default Web page, named default.asp, and you can look at any one of these from the example code to see all of the schema placeholders as they appear in the page. However, instead of showing an entire ASP page in this article -- they are a bit large-- we'll take a look at the schema elements, individually.
The Schema Placeholder Items
All of the Widget Shop's ASP application main pages have the same set of schema elements.
One schema element that repeats across all of the pages of the site is the file containing the CSS style settings. The STYLE tag and the ASP script to pull in the current schema stylesheet is included in the page header section, and is shown in the following block:
<LINK REL = STYLESHEET TYPE = "text/css"
HREF = "http://localhost/widgets/css/<%=Application("stylesheet")%>"
TITLE = "style1">
The Web banner image can also change and the HTML/Script to handle this image is:
<img src = "http://localhost/widgets/mm/<%=Application("banner")%>"
border=0 width=264 height=171>
The banner image can change, but the banner size remains the same, so the graphics width and height are included in the IMG tag.
There are three menu buttons, one for each category. These buttons are schema specific and the HTML/ASP script for all three is:
<a href="http://localhost/xwidgets/default.asp"
onMouseOver="highlight('x')"
onMouseOut="normal('x')">
<img id="x" name="x"
src="http://localhost/widgets/mm/<%=Application("x")%>"
height=68 width=145 vspace=10 hspace=40 border=0></a>
<a href="http://localhost/ywidgets/default.asp"
onMouseOver="highlight('y')"
onMouseOut="normal('y')">
<img id="y" name="y"
src="http://localhost/widgets/mm/<%=Application("y")%>"
height=68 width=145 vspace=10 hspace=40 border=0></a>
<a href="http://localhost/zwidgets/default.asp"
onMouseOver="highlight('z')"
onMouseOut="normal('z')">
<img id="z" name="z"
src="http://localhost/widgets/mm/<%=Application("z")%>"
height=68 width=145 vspace=10 hspace=40 border=0></a>
Notice that the MouseOut and MouseOver event handlers are trapped within the hypertext links surrounding each menu button image. These handlers call JavaScript functions to create the mouse over effect by exchanging the image in the page with a "highlighted" version of the image when the mouse cursor is over the menu button, and changing the image back when the mouse cursor is no longer over the menu button. As the menu buttons are schema specific, different versions of the JavaScript mouse over script are created in separate files for each schema. The code from one of these JavaScript files is shown in Example 1.
Example 1: JavaScript to handle Widget Shop Menu MouseOvers
// unhighlighted images
menuimage1 = newArray(3);
menuimage1["x"] = newImage();
menuimage1["x"].src = "http://localhost/widgets/mm/xmain1.gif";
menuimage1["y"] = newImage();
menuimage1["y"].src = "http://localhost/widgets/mm/ymain1.gif";
menuimage1["z"] = newImage();
menuimage1["z"].src = "http://localhost/widgets/mm/zmain1.gif";
// highlighted images
menuimage2 = newArray(3);
menuimage2["x"] = newImage();
menuimage2["x"].src = "http://localhost/widgets/mm/xmain2.gif";
menuimage2["y"] = newImage();
menuimage2["y"].src = "http://localhost/widgets/mm/ymain2.gif";
menuimage2["z"] = newImage();
menuimage2["z"].src = "http://localhost/widgets/mm/zmain2.gif";
function highlight(name) { document.images[name].src = menuimage2[name].src;
}
function normal(name) { document.images[name].src = menuimage1[name].src;
}
Within the applications ASP page, a schema placeholder is created for
the JavaScript file, shown next:
<script language = 'javascript' type = 'text/javascript'
src = 'http://localhost/widgets/scripts/<%=Application("script")%>'>
Each of the Widget Shop's pages also share a common greeting. This greeting can change based on the schema, and the placeholder for this item is:
<h3><%=Application("greeting")%></h3>
Finally, each page also has an image placeholder used to hold a special image created for a specific event. Normally, this image is a transparent 1-pixel GIF image, which doesn't show in the page. Occasionally, though, another image might be used and the schema placeholder for this image is:
<img src = "http://localhost/widgets/mm/<%=Application("specialimage")%>"
align="right">
Unlike the menu buttons and the page banner image, the special image can vary in size so the width and height of the image is not included within the IMG tag.
Each of the site's Web pages has these placeholders defined in the page. The problem though is not all of the site pages share the same ASP Application object and have access to the same information. That's where the SessionPro component comes in. First, though, we have to capture the schema information.
The Widget Schema Recorder
To capture the schema information, the Widget Shop has the Widget Schema Recorder, two ASP pages consisting of an HTML form containing elements for each of the schema items in one page, the server-side script to process the form in a second ASP page.
The page with the form is named default.asp and is located in the admin subdirectory. It is also a consumer of the schema information in the Application object, primarily to display the current information within the form elements when the form page is first loaded, as shown in Figure 2.
Figure 2: The Widget Schema Recorder
To process the Schema Recorder form contents, a second page, process.asp, pulls the form information from the Request object and assigns the schema items to the widget_admin Application object.
Storing the schema information in the widget_admin Application object makes the information available within the Widget Shop site's administration application, but not to other applications. Nor does this persist the schema beyond the current application runtime environment.
To persist the ASP Application object beyond the current runtime environment, and also to make the schema information available to the other Widget Shop applications, the process.asp page instantiates an instance of the SessionPro. Once instantiated, the script assigns the value of "schema" to the object's Namespace property, and then calls the SaveApplicationState method, as shown in Example 2.
Example 2: Persisting the Widget Shop schema information
<%
Response.Buffer=True
%>
<HTML>
<HEAD>
<TITLE>The Widget Shop - Schema</TITLE>
</HEAD>
<BODY>
<H3>Schema is:<H3>
<%
Dim stylesheet
stylesheet = Request("stylesheet")
Dim script
script = Request("script")
Dim banner
banner = Request("banner")
Dim greeting
greeting = Request("greeting")
Dim specialimage
specialimage = Request("specialimage")
Dim x
x = Request("x")
Dim y
y = Request("y")
Dim x
z = Request("z")
'--- Save values to Application object
Application.Lock
Application("stylesheet") = stylesheet
Application("script") = script
Application("banner") = banner
Application("greeting") = greeting
Application("specialimage") = specialimage
Application("x") = x
Application("y") = y
Application("z") = z
Application.UnLock
Response.Write "Stylesheet is " & stylesheet & "<P>"
Response.Write "Script file is " & script & "<P>"
Response.Write "Banner is " & banner & "<P>"
Response.Write "Greeting is " & greeting & "<P>"
Response.Write "Special Image is " & specialimage &
"<P>"
Response.Write "XWidget image is " & x & "<P>"
Response.Write "YWidget image is " & y & "<P>"
Response.Write "ZWidget image is " & z & "<P>"
Set oSASession = Server.CreateObject("SoftArtisans.SASessionPro.1")
oSASession.Namespace = "schema"
oSASession.SaveApplicationState
</BODY>
</HTML>
That's all you have to do to persist the Application object's state.
In addition to persisting the Application object, process.asp also re-displays the schema information, as feedback to the Widget administrator that the schema changes have been successfully saved. Figure 3 shows the Widget Schema Recorder second page, after the information has been processed.
Figure 3: Recorder page after successfully processing the schema information
Once the schema information has been processed, and the Widget administration Application object has been persisted with SessionPro, the second part of sharing the configuration data across separate ASP applications, is to load the schema information into these applications. This is covered in the next section.
Retrieving the configuration data
To retrieve the schema information, a global.asa file is created within the admin subdirectory. As you're most are probably aware, each ASP application can have one global.asa file, located at the root directory for the application. This file contains object definitions used by the ASP pages to instantiate external components. The file can also contain specific event handlers such as the OnStart and OnEnd event handlers for a session and for the ASP application.
Within the global.asa file created for the Widget Shop, the only script contained in the file is for the Application_OnStart event handler subroutine. In this script, an instance of SessionPro is created, its Namespace property is set to a value of "schema", and its RestoreApplicationState method is called:
<SCRIPT LANGUAGE = "VBScript" RUNAT = Server>
Sub Application_OnStart
Set oSASession = Server.CreateObject("SoftArtisans.SASessionPro.1")
oSASession.Namespace = "schema"
oSASession.RestoreApplicationState
End Sub
</SCRIPT>
By specifying the same namespace value to restore the application information as was used to save the information, the new schema recorded with the Widget Shop Schema Recorder is loaded into the Application object of the ASP application that owns the global.asa file.
Once the first global.asa file has been created, it is copied to all of the Widget Shop applications. Based on the functionality just described, the next time the ASP application is restarted, the OnStart event handler script within the global.asa file is processed and the schema information is loaded into the Application object for the application.
To test the Schema Recorder and the new global.asa files, we'll create our first schema and give it a try.
Creating a new Schema
The first schema created is the Main Widget Shop schema - the one that is shown when no other schema is in effect. The schema placeholders and their associated values are shown in Table 1.
Table 1: Widget Shop Main Schema
Schema Placeholders |
Placeholder Values |
stylesheet |
main.css |
script |
mnmouseover.js |
banner |
banner.jpg |
greeting |
Widgets r Us! at... |
specialimage |
blank.gif |
x |
xmain1.gif |
y |
ymain1.gif |
z |
zmain1.gif |
The Widget Schema Recorder application is accessed and the information is entered into the Web page form, and then submitted. The second page of the Recorder than processes the information, stores it into and then persists the widget_admin's Application object, using SessionPro.
After successfully persisting the Application object, the Widget Shop applications need to be restarted for the OnStart event handler script within the global.asa files to be processed, and the schema changes to take effect. An approach I used while working on the application was to access the Services Control Manager from the Control Panel and to stop the service titled "World Wide Web Publishing Service", and then restart it once the service was successfully stopped.
Use the approach you're most comfortable with to restart the ASP applications.
To view the new schema, we'll access the main Widget page using an URL similar to the following (when running the example using localhost as the domain name):
http://localhost/widgets/
Figure 4 shows the Widget Shop main page using the new Main schema.
Figure 4: Widget Shop using Main schema
The banner schema item is the image shown in the top left corner of the page. The font and colors for the page come from the schema CSS file. The highlighted second menu button in the image is due to the mouse cursor being over the Y Widgets menu button when the application image was captured, triggering the mouseover effect created using the schema JavaScript file. No special image is showing as the schema is using the transparent GIF for this placeholder item.
Creating one schema and testing this out on all the ASP application pages is fun, but to make sure the application works with more than one example, I need to continue testing new schemas.
I created another schema to further test the configuration sharing functionality. This new schema has an autumn theme, and is supported by all new images, and new CSS and JavaScript files. The autumn schema placeholders and their values are shown in Table 2.
Table 2: Widget Fall Schema
Schema Placeholders |
Placeholder Values |
stylesheet |
fall.css |
script |
flmouseover.js |
banner |
fall.jpg |
greeting |
Spend your autumn days with... |
specialimage |
blank.gif |
x |
xfall1.gif |
y |
yfall1.gif |
z |
zfall1.gif |
To test this new schema the Widget Shop's ASP applications have to, again, be restarted. Once restarted, the new schema is in effect as shown in Figure 5.
Figure 5: Widget Shop with Autumn Schema
This configuration sharing worked with the new example. However, the second test did demonstrate a weakness with the current design for handling configuration data sharing. Creating a new schema for the Widget Shop using the implemented approach means having to stop and restart the Shop's ASP applications. Sometimes restarting an application in order to upload new configuration data isn't the best approach for the Web site. Instead we might want to use an "on-demand" approach and manually control when and how the configuration data is made available to site's ASP applications.
Adding On-Demand Configuration Sharing
Earlier, when the widget_admin Application object was loaded into the other ASP applications, the only data impacted for the target application was the schema items stored in the Application object. As the Application object didn't have any other data at the time because the ASP application had just been restarted, the issue of what data is impacted isn't that important as long as the schema values are uploaded.
However, if the ASP application had been running for some time, and other information was stored in the Application object, then uploading a new schema and have the existing one overlaid with the new information, but without any impact to any other application data becomes a very important issue.
That's where the namespace of the SessionPro is so handy. By associating the persisted schema information with the "schema" namespace, only the "schema" namespace data is loaded into the Application object. As long as the ASP developer doesn't use the schema placeholder names for other data in the object, no other application data is impacted. Because of all of this, the SessionPro's RestoreApplicationState method can be safely used with ASP applications that are currently running, and this forms the basis of our on-demand configuration sharing.
A new ASP page is created within the widgets directory, and given the name refresh.asp. This script in this page instantiates an instance of the SessionPro component and loads the ASP Application information associated with the "schema" namespace into the current application's environment. Feedback is then provided that the application's schema has been refreshed, and containing a link to the application's main Web page in case the administrator wishes to review the changes.
Once the first refresh.asp is created, it is copied to all of the Widget Shop's ASP application directories and modified to reflect the content handled by the specific application. Example 3 shows the contents of the page defined for the YWidgets application.
Example 3: Schema refresh page for the YWidgets ASP Application
<HTML>
<HEAD>
<TITLE>The Widget Shop - Schema Refresh</TITLE>
</HEAD>
<BODY>
<%
Set oSASession = Server.CreateObject("SoftArtisans.SASessionPro.1")
oSASession.Namespace = "schema" '---
namespace
oSASession.RestoreApplicationState
%>
<H3>The Application object for <strong>YWidgets</strong>
has been refreshed.</H3>
<P>Go to the <A HREF="default.asp">YWidgets Main
Page</A>
or hit the back button to refresh the other Widgets applications.</P>
</BODY>
</HTML>
To refresh the individual applications, new HTML was added to the Schema Recorder, specifically to the process.asp file in the admin subdirectory. At the bottom of the file, a message was added about refreshing the contents for each Widget Shop application by clicking on the link for the application. The links contain the refresh.asp pages for each of the Shop's ASP applications, as shown in Example 4.
Example 4: Links to refresh pages for each ASP application
NOTE: These links work only if you have the widget application installed. Scroll to the bottom of this page - to the Attachments section - to download a zip file containing code related to this article.
Refresh the dependent ASP applications by clicking:
- here to refresh Widgets
- here to refresh XWidgets
- here to refresh YWidgets
- here to refresh ZWidgets
You can choose not to refresh the applications now -- they'll pick up the schema modifications when they are restarted.
To test this new on-demand configuration sharing, another Widget Shop schema is defined, this one focusing on the 4th of July holiday. One difference between this new schema and the previous ones is that the Shop is also having a sale at the same time, so a new sales image is created and reference within the specialimage placeholder. Table 3 contains the Schema placeholders and associated values for the new schema.
Table 3: Widget Shop 4th of July Sale Schema
Schema Placeholders |
Placeholder Values |
stylesheet |
frth.css |
script |
frthmouseover.js |
banner |
frth.jpg |
greeting |
Happy 4th of July at... |
specialimage |
sale.gif |
x |
xfrth1.gif |
y |
yfrth1.gif |
z |
zfrth1.gif |
The new schema is recorded using the Widget Schema Recorder. However, when the second page of the Recorder is shown, at the bottom are links to refresh the individual ASP applications.
Clicking on the link associated with YWidgets application, a page shows with information that the application has been refreshed and with a link to the application's main page, to review the results. Clicking on this link opens the main page for YWidgets, as shown in Figure 6.
Figure 6: YWidgets Application with 4th of July Schema
With the new on-demand configuration sharing, the Widget administrator now has a choice about when to share the configuration data between the Widget Shop's ASP applications: immediately using on-demand configuration sharing, or when the ASP applications are restarted.
Summary
As Web site infrastructures become more complex the task of maintaining these sites becomes correspondingly more complicated .Web servers are now providing more options for how a Web site is structured in order to meet the new and increased demands being placed on these sites.
Among the techniques that IIS supports is the concept of creating separate ASP applications for one Web site, by splitting the site's contents into separate Virtual Directories. However, a task that's impacted by splitting a Web site into multiple ASP applications is the sharing of necessary configuration and other application-specific data among all applications of the site.
This article provided an example of how the task of sharing configuration data can be greatly simplified, just by persisting ASP Application object data from one application, and then providing a means to "load" this persisted application state into the other Web site's ASP applications. The key functionality to the approach used in the example, and to sharing the configuration data across different ASP applications, was provided by SoftArtisans SessionPro component. This component can persist application state, and then restore it with only two method calls (my kind of component).
I hope this article and its associated example, the Widget Shop, have demonstrated that just because Web sites are getting more complicated, our work to maintain them doesn't have to be -- at least not in regards to sharing configuration data across ASP application.
NOTE: The Widget Shop source and all associated graphics, and CSS and JavaScript files are included in a ZIP file attached to this article. To try the example out for yourself, unzip the contents, making sure to keep the existing directory structure. Then, in IIS, create a new Virtual Directory for each of the following directories: admin (widget_admin), widgets (widgets), xwidgets (xwidgets), ywidgets (ywidgets), and zwidgets (zwidgets). Run the Schema Recorder by accessing the widget_admin site, and add the schema and save the schema information. Now, when you access the main Widget Shop directories, the saved schema is reflected in the page.
|