Add Server-Side Caching for Speed
The simple process you've seen loads the XML document and XSLT stylesheets for each request. It works fineand it's faster than retrieving the list of links from a database and then formatting it with VBScript code on the server. But there's a problem with this methodit's not fast enough, and you can optimize it. To do so, use the free-threaded version of the DOMDocument object to cache the XML document containing the links and the XSLT file containing the stylesheet in memory. By doing that, you avoid having to create DOMDocument objects and read the document from disk each time. Although you would normally load these objects in your global.asa file, you can just as easily load them during any page request. For example:
<%@ Language=VBScript %>
appPath = Server.MapPath(".")
LinkData_filename = appPath & "\menu.xml"
if isEmpty(Application("LinkData")) or _
Request("refresh") = "true" then
Response.Write "Loading from disk"
' load the XML document
set xml = Server.CreateObject _
call loadXMLFile(xml, LinkData_filename)
call updateAppVariable("LinkData", xml)
Response.Write "Loading from cache"
set xml = Application("LinkData")
The first time a client requests this page, the server creates and stores a FreeThreadedDOMDocument object at Application scope, where it's available for each subsequent request. You can see the difference in performance between the initial load and the cached copy by requesting the page twice (see the file testMenu2.asp
in the downloadable code for the example. For the purposes of this example, I've made it write messages to the browser. The first time you load the page, it prints "Loading from disk," but all subsequent times, it prints "Loading from cache."
The loadXMLFile() function is a wrapper that displays error information if the XML file fails to load properly.
sub loadXMLFile(xmldoc, filename)
if not xmldoc.load(filename) then
Response.Write "Error loading the file '" _
& filename & "'.<br>"
Response.Write "Description: " & _
xmldoc.parseError.reason & "<br>"
Response.Write "Line: " & _
xmldoc.parseError.line & "<br>"
Response.Write "Line position: " _
& xmldoc.parseError.linepos & "<br>"
When you update values stored in the Application object, you need to lock the Application to avoid the problem of one thread attempting to retrieve the data while another thread is changing it. Programmers sometimes forget to lock the Application object. The updateAppVariable
function protects and generalizes setting Application variables:
sub updateAppVariable(varName, value)
if isObject(value) then
set Application(varname) = value
Application(varname) = value
You use a slightly different sequence to cache XSLT stylesheets. The XSLTemplate object has a stylesheet
property that accepts a FreeThreadedDOMDocument object. You create the XSLTemplate object, set its stylesheet property, and then cache the XSLTemplate at Application scope rather than the FreeThreadedDOMDocument itself. The process of loading and caching the XSLTemplate object is very similar to storing a FreeThreadedDOMDocument alone. For example, the following code loads the XSLT file that creates the list of links:
' load the list XSL file
set xsl = Server.CreateObject _
call loadXMLFile(xsl, LinkSelect_filename)
set template = Server.CreateObject _
set template.stylesheet = xsl
call updateAppVariable("LinkSelect", template)
After creating the XSLTemplate object, you use its createProcessor
method to create an XSLProcessor instance that actually performs the transformation. You set the XSLProcessor's input
property to the XML file and retrieve the results via the output
set template = Application("LinkSelect")
set proc = template.createProcessor
proc.input = xml
The output is a string containing the transformed XML data. XSLTemplates have a speed advantage over caching the stylesheet directly as a FreeThreadedDOMDocument object because the stylesheet is pre-compiled, rather than compiled for each use. In addition, the XSLTemplate objects make using XSLT parameters very easy, as you'll see later in this article.