Copyright © 2003-2004 (c) NPSL Ltd.
Abstract
Gnomon is an XML/XSLT processor for IIS, based on Apache's Xalan XSLT processor and Xerces XML processor. It is used to apply XSL stylesheets to XML pages on the server side with minimal effort on the part of the user. It also allows XSL to access HTTP request (cookies, forms, query-strings, cgi variables), session state and databases, allowing stylesheets to react to input.
Even if these extra features are not used, then gnomon provides a very straightforward way to develop and test stylesheets designed to produce html pages.
This product includes software developed by the Apache Software Foundation (http://www.apache.org/).
Table of Contents
Gnomon is an ISAPI extension that is capable of applying one or more XSLT transformations to XML documents. These XML documents may be static (e.g. disk files) or dynamic (e.g. generated by ASP, or ASP .NET).
In addition, gnomon provides XSLT functions that allow XSL access to:
Gnomon has support for caching both source XML documents and compiled XSLT stylesheets.
Gnomon uses the Xalan-C XSLT processor as its XSLT processing engine.
Table of Contents
Firstly, download the current gnomon installer package. When installing, be sure to install the samples. All being well then gnomon will add a document mapping for .acds pages to itself and create a virtual directory under the chosen (or default website) called /gnomon. If this process fails then try checking the steps listed in the manual installation chapter.
To upgrade gnomon from a previous release, first stop any web services and close any instances of perfmon. This step saves having to reboot the server to complete installation by releasing any in use gnomon files. Then simply run the installer package as described in the quick start section.
If you are using the gnomon COM+ package for session, gnomonASP handling, or other code you will need to recreate this package when upgrading. To do this, delete the existing gnomon COM+ package using the Component Services applet in Control Panel/Administrative tools, and then run the gnomon COM+ setup program. You can either do this before install, when you are offered the option of creating the package, or after install by using the link installed in the start menu.
You should be able to view the first sample after installation. Essentially, what this demonstrates is the default operation of gnomon, which is:
Stylesheets are always cached by gnomon. The same compiled stylesheet is used for any subsequent transforms. To ease development, gnomon tracks changes in stylesheets and dependencies, and automatically recompiles the stylesheet when it changes.
The second sample demonstrates the use of some gnomon extension functions by dumping out the query-string, form, server-variables and cookies collections. These functions are installed in the namespace http://www.npsl.co.uk/gnomon and are described in detail in the chapter on gnomon built-in functions.
The third sample demonstrates dynamically generating XML from an ASP page. The page simply writes out the current time, but the principal can be expanded to dynamically generate any valid XML page (perhaps including content from a database).
If you are having trouble with gnomon starting, that is it IIS reports a problem with loading the ISAPI extension DLL when you request a resource, then you should look in the Application Log (in Control Panel/Administrative Tools/Event Viewer). Gnomon reports fatal errors encountered during startup here. If these error messages contain insufficient information to figure out what is stopping gnomon starting, then you can post a message to the mailing list for support.
Gnomon is an ISAPI extension, and as such needs to be configured to handle one or more specific file types. IIS maps processing to ISAPI extensions by file extension. The file extension used by NPSL (and that which is setup by the sample configuration application) is .ACDS. You can add ISAPI mappings at any point in the tree shown in the Internet Information Services mmc snap-in (found in administrative tools). Gnomon should be configured to handle the chosen extension for GET and POST requests.
If you want to generate pages dynamically using ASP or ASP.NET, you can either use rewrites in gnomon.config to request .asp/.aspx pages, or you can add a mapping for a different extension. It is possible to get pages from any source, so you could use PHP, perl, etc, or even a different server.
NPSL use ASP to process files with a .xml extension as this gives greater clarity on the process; this is not required. If you choose to follow the same configuration then you should add a script mapping for .xml files pointing to the ASP script engine (this is typically found in c:\winnt\system32\inetsrv\asp.dll) -- check the existing ASP mapping.
If you choose not to add a .XML mapping or use a different extension then be sure to modify the URI rewrites in gnomon.config (see the chapter on the gnomon.config file for more information on how to do this).
If you choose not to use the gnomon binary package then you will have to build it from source. Currently gnomon relies on a number of libraries:
The include and library directories for all of the above must be in the include and library paths for the compiler. The following items are optional:
Xalan and Xerces both require small patches, which can be downloaded from the gnomon home page and are also included in the gnomon source in subversion. (These patches are being considered for inclusion in the respective projects). For Xalan, the patch adds support for returning XML fragments from extension functions. For Xerces, the patch adds the SAX2 EntityResolver2 feature, which is required for correctly resolving certain external URIs. Both patches are required to build gnomon.
For gnomon version 1.1 the patches work in a slightly different way, they expose the new XMLEntityResolver to Xalan that was introduced into Xerces with version 2.4 and up.
Xalan and Xerces as provided by the installer are both configured to use the ICU for transcoding. For Xalan, this has the important implication that the format-number() function operates correctly. If you choose to build without the ICU, then things should work, but this configuration has not been tested.
Currently gnomon only builds with Visual Studio .NET 2003. Project files are not available for .NET 2002 at this time, but you should be able to create them straightforwardly. For some reason, the project files are not backwardly compatible. If you want me to do this, send a mail to the gnomon list and I'll make the appropriate project files available. It should also not be too difficult to build gnomon with Visual C++ 6. Any ports are gratefully accepted.
Finally, to build gnomon with all of the above in place, just open the gnomon solution file and press build. If you have trouble, again, contact the list for support.
Gnomon can be extended by writing special extension DLLs. Extensions generally install one or more external functions into a particular namespace to make them available from XSLT. The gnomon extension API is not currently documented, but the code is well commented (with javadoc style comments). It is recommended that you use the SQL, tidy and session extensions provided with the distribution as a basis for new extension functions. Better documentation for extensions will be provided in future, depending on the level of interest.
Table of Contents
The gnomon configuration file allows you to tailor the behaviour of gnomon as desired. Gnomon's configuration is hierarchical; most settings are inherited from the parent configuration file (as detailed below). There is an inbuilt default configuration, which represents the root of the hierarchy, this is used if no other configuration is present. Directories represent the levels of the hierachy, you can provide a separate gnomon.config in each directory. If no configuration file is preseent, then all settings are inherited from the previous level. The first possible provided configuration file is at the root of each website (this represents the second level of the configuration hierarchy, the first is the default configuration). It is possible to have multiple sites each with different configuration files, this is handled transparently.
A complete sample configuration is listed below. Each element of the configuration file is described in detail in the following sections.
<config> <!-- Limit the amount of POST data that gnomon will handle. 0 is unlimited (not recommended!) --> <post-limit size="1048576"/> <!-- Define custom error handlers for errors that may be produced during processing. This allows you to show users a generic error page, and possibly have code that logs or alerts based on the errors. The warnings-as-errors attribute allows you to force gnomon to report warnings encountered during XSLT processing. By default, they are ignored. --> <errors override-parents="yes" warnings-as-errors="1"> <!-- Generic ASP error handling page, for all errors except SERVER_TOO_BUSY (which only supports the redirect and file methods) --> <handler code="*"> <uri method="POST">/errors/gnomon_error.asp</uri> </handler> <!-- A straightforward error message to display if the server is currently too busy to handle the load --> <handler code="SERVER_TOO_BUSY"> <file>c:\inetpub\wwwroot\gnomon\gnomon_too_busy.html</file> </handler> </errors> <cache> <!-- Find the cachable HTTP methods for the XML files (templates) using this xpath expression Override the parent's setting to no-cache by setting this empty. --> <select-cache-methods xpath="/document/@cache-methods"/> --> </cache> <stylesheets> <!-- How to get stylesheets. http or file are supported. file is the default (maps the URI to a file and reads it in), as it is faster and typically stylesheets are not generated dynamically. --> <fetch-method>file</fetch-method> <!-- Use processing instructions. These are less flexible than the select-stylesheets method as they give no opportunity to override default stylesheets. --> <use-processing-instruction value = "1"/> <!-- Find the stylesheets from the document using this xpath expression. Note that use-processing-instruction overrides this. --> <select-stylesheets xpath="/document/stylesheets"> <!-- List any default stylesheets to apply before/after those listed by the individual document --> <default-stylesheets override-parents="first,last"> <first> <stylesheet href="/styles/some-first-sheet.xsl"/> </first> <last> <stylesheet href="/styles/some-last-sheet.xsl"/> </last> </default-stylesheets> </stylesheets> <!-- The codepage to use when parsing GET/POST data --> <uri-codepage>utf-8</uri-codepage> <!-- Used for redirecting URIs, basically for making loopback requests rather than external ones. e.g. where www.npsl.co.uk resolves to an external load balanced IP, we actually want to fetch the URI from localhost. override-parents will clear any inherited rewrites. All rewrites are applied in order. Use conditionals in the format to prevent inadvertent rewrites. (This may change in future if it proves to be sufficiently inflexible) --> <uri-rewrites override-parents="yes"> <!-- Lazy all hosts match --> <uri match="(https?://)([^:/]*)(:[^/]*)?(/)?([^?]*)\.acds(\?[^#?]*)?" format="?1http\://localhost$3/$5.xml$6:$0" case-sensitive="no" /> </uri-rewrites> <!-- Settings for the http request engine --> <http-request> <!-- Sets the send/recv timeout in seconds. The default is 0 (infinite), which is fine if you are not accessing external resources, as IIS's default handling will ensure that loopback requests complete one way or another... --> <timeout>120</timeout> </http-request> <!-- Extension function DLLs. These are cached, but are installed per configuration file (with inheritance and override-parents). There are no default extensions. --> <extensions override-parents="yes"> <extension path="C:\Program Files\gnomon\bin\gnomonSessionExtension.dll"/> <extension path="C:\Program Files\gnomon\bin\gnomonSQLExtension.dll"/> <extension path="C:\Program Files\gnomon\bin\gnomonTidyExtension.dll"/> </extensions> </config>
This element contains the maximum number of bytes of POST data that gnomon is prepared to deal with. By default, the limit is 5Mb. If you want to disable POST data limiting you can indicate this explicitly with a value of 0). This may be applicable if you are using, e.g. Microsoft's URLScan to limit POST data at an earlier stage, but the double protection doesn't hurt.
If the maximum size of the POST data is exceeded, then gnomon will attempt to output an error message and will cease to read POST data. Unfortunately by this stage, IIS will have replied to the POST request with a '100 Continue' response (where applicable, basically all browsers these days). Now RFC2616 states:
8.2.2 Monitoring Connections for Error Status Messages
An HTTP/1.1 (or later) client sending a message-body SHOULD monitor the network connection for an error status while it is transmitting the request. If the client sees an error status, it SHOULD immediately cease transmitting the body.
However, it seems most clients DO NOT follow this sage advice, and will instead simply report that the server closed the connection. If this is a problem to you, then you will need something like Microsoft's URLScan ISAPI filter which is able to enforce the limit at an earlier stage in the process.
On a final note, if you are using ASP to handle large forms, then you should be aware of Q273482, PRB: "Request object, ASP 0107 (0x80004005)" Error When You Post a Form. In short "The size limit of each form field is exactly 102,399 bytes". If you really want to handle large forms from ASP, there are some 3rd party objects that will do this for you, or you can use Request.BinaryRead and handle the data yourself.
This node encapsulates the configuration for gnomon error handling.
The attribute warnings-as-errors enables you to report the XSLT warning stream from Xalan. By default,this stream is suppressed unless an error occurs in the page. Viewing warnings may help with debugging your XSLT.
The rest of this node is concerned with the custom error handling functionality that gnomon provides. This is modelled on the custom error documents built in to IIS. For each type of gnomon error that occurs, you may specify how the error document finally presented to the user is generated.
By default, error handling mechanisms are inherited from the parent configuration file. To clear all inherited error handling mechanisms (resetting them to the default pages generated by gnomon), then you can set the "override-parents" attribute to "yes". It is not generally necessary to do this, as any custom error handlers specified in a child configuration file will automatically override the value for that error handler that was specified in the parent configuration file.
An error handler can provide a document from one of the following sources (except for SERVER_TOO_BUSY, see the explicit note below):
An error handler is begun with a handler node indicating the specific error that is being handled (e.g. <handler code="SERVER_TOO_BUSY">). The next node specifies the handling mechanism, and additional data that is required for it.
The list of possible error codes is:
Default handler example:
<handler code="TRANSFORM_FAILURE"> <default/> </handler>
This causes gnomon to generate error pages internally, and is the initial default mechanism for all pages. If one of the error mechanisms listed below fails (e.g. because a file is specified that does not exist or an external URI could not be accessed due to a network error), then gnomon falls back to the internal default pages.
File handler example:
<handler code="SERVER_TOO_BUSY"> <file>c:\inetpub\wwwroot\gnomon\gnomon_too_busy.html</file> </handler>
This causes gnomon to return the specified file to the client.
URI redirect example:
<handler code="STYLESHEET_COMPILE_FAILURE"> <uri method="REDIRECT">http://www.myserver.com/compile_failure.html</uri> </handler>
This causes gnomon to produce a 302 Object Moved response with the Location: header set to the specified resource.
URI GET example:
<handler code="STYLESHEET_COMPILE_FAILURE"> <uri method="GET">/errors/gnomon_compile_failure.asp</uri> </handler>
This causes gnomon to get the specified resource via HTTP and return it to the client. If this is not possible then the default error handling mechanism is used. Note that this resource SHOULD set an appropriate error status code (e.g. using something like Response.Status = "500 Internal Server Error" from ASP). Gnomon will forward the page regardless of the HTTP error status, but in the case that the page cannot be retrieved due to a network error, then it will fall back to the default handling.
URI POST example:
<handler code="TRANSFORM_FAILURE"> <uri method="GET">/errors/gnomon_transform_failure.asp</uri> </handler>
This causes gnomon to post data to the specified resource describing the problem that was encountered. The same conditions about network failure as described for the HTTP GET method also apply to the POST method.
The posted data is in XML form (what else?). The posted XML is URI encoded (as is necessary for a form POST) and is stored in the request variable "errors". Thus, if this is an ASP page for example, Request.Form("errors") would give you XML suitable for loading directly into an XML parser.
The posted XML data looks like:
<errors> <error code="TRANSFORM_FAILURE"> <source>xml-parser</source> <description>One of my nodes dropped off!</description> <uri>http://localhost/test.xml</uri> <line>23</line> <column>9</column> </error> <warning code="TRANSFORM_FAILURE"> <source>xsl-processor</source> <description>A warning</description> </warning> <message code="TRANSFORM_FAILURE"> <source>xsl-processor</source> <description>An interesting message</description> </message> </errors>
Each error, warning or message is contained within the root node errors.
The source node gives the originator of the error. This is one of gnomon, xml-parser or xsl-processor.
The description node gives a textual description of what went wrong.
The uri, line and column nodes indicate the location that the error occurred. These are optional (as for some errors this information is not relevant).
<cache> acts as a container for configuration settings describing how gnomon should cache included source documents.
The xpath expression attribute is used to select an attribute in XML documents which contains a list of HTTP methods to cache the document for. It is of interest to cache source documents for the transform when the XSLT is made dynamic via reference to a database or request state. Within the attribute (or node), the HTTP methods are provided in a comma separated list, e.g.:
<select-cache-methods xpath="/document/config/@cache-methods">
and in the source document:
<document> <config cache-methods="get,post"> </document>
The currently support methods are get and post.
This node encapsulates configuration for the stylesheets that are applied to XML documents. The basic mode of operation is to use any xml-stylesheet processing instructions encountered. Multiple occurences of these directives are allowed, but not well defined -- here gnomon applies all of the stylesheets as listed, in order. For multiple transforms, the resultant document of each intermediate transform must be a valid XML document.
This node tells gnomon how to fetch stylesheets. In the default mode of operation, then the stylesheet URI is mapped to a physical path first, and the stylesheet is read from disk. If your stylesheets reside on another server, then you can also get them with HTTP. This mode of operation was introduced with gnomon 1.1 -- previous versions always used HTTP to fetch stylesheets. The file method is substantially better performing than the HTTP method, even for a single server.
This directive, defaulting to 1, directs gnomon to look for and use any xml-stylesheet processing instructions that occur in the source document. Any xml-stylesheet instructions with a type that is not of text/xsl or application/xslt+xml is ignored.
This node provides an alternative (and more flexlible) method of determining which stylesheets to apply to the source document. This node is of lower precedence than the >use-processing-instruction>&. The xpath expression (e.g. xpath="/document/stylesheets") should select a node something like: <stylesheets override-parents="first,last"> <stylesheet href="some-stylesheet"/> <stylesheets> The override-parents attribute allows preventing application of the listed first, last or both default stylesheets (see below). Each stylesheet is applied in order, as for the xml-stylesheet processing instructions.
This node is a container for specifying any default stylesheets to be applied. Stylesheets are inserted before or after the list specified in the document, depending on whether they occur in the first or last container. It is not possible to override these stylesheets with processing instructions. Any stylesheets inherited from parents are inserted before any <first> stylesheets at this configuration level, and likewise any inherited stylesheets in the <last> container are inserted after any after any stylesheets at this configuration level. override-parents will clear any stylesheets from all parents, for those which appear in the first or last containers as specified in the attributes.
This container specifies stylesheets to insert before the stylesheets listed for the current document, using either processing instructions or the select-stylesheets instruction.
This container specifies stylesheets to insert after the stylesheets listed for the current document, using either processing instructions or the select-stylesheets instruction.
This node is listed under the first or last containers to give the location for a stylesheet.
This specifies the code page to use when interpreting post and query string data. For i18n, the most reasonable choice is some form of unicode, and for web pages, this usually means utf-8. Note that ASP usually interprets this data in the local codepage, to make ASP behave consistently with non-ASCII data, you should set the codepage explicitly. There are two ways of doing this, the first is with Session.CodePage (=65001 for utf-8), or better, with the metabase W3SVC/ASPCodepage property.
This section tells gnomon how to rewrite URIs that it needs to fetch. This is typically used for redirecting requests for the initial page to either a static resource, or a dynamic page produced by ASP or ASP.NET.
The rewrites use the boost::regex_merge algorithm, and are all applied in order. The default match rule rewrites all incoming requests for .acds pages to point to .xml pages stored on the local server. (These resources could come from an external server if required). It is:
<uri match="(https?://)([^:/]*)(:[^/]*)?(/)?([^?]*)\.acds(\?[^#?]*)?" format="?1http\://localhost$3/$5.xml$6:$0" case-sensitive="no" /gt;
The first portion (the match) approximately follows extended POSIX regular expression syntax. The format string directs how to rewrite the URI based on the match. The ?: construction is a conditional which operates as for the ?: operator in the C language. ?1 tests for the first subexpression of the match being non-empty. If it is true, then the URI is rewritten as indicated (part up to the :), otherwise the expression is left untouch ($0 contains the original input to the match).
The case-sensitive attribute (defaults to no) allows URIs to be matched regardless of case.
This section configures settings for the http requests made by gnomon. At present, only the send/recv timeouts can be set. The timeout values is in seconds, and must be contained within a timeout node, e.g. <timeout;>60</timeout;>. If the request times out then a URI_FETCH_ERROR is reported.
This section allows the use of gnomon extension DLLs. These are objects that can add additional extension functions into gnomon -- they allow it to be extended at run time. For more information on how to write extension dlls, see the gnomon extensions chapter By default there are no extension DLLs, and any extension DLLs are inherited from the parent level of the configuration hierarchy. You can prevent this behaviour by setting the override-parents attribute to the appropriate value.
Listed inside the extensions container are the paths to each extension DLL. The extension interface of each DLL takes care of providing any extension functions necessary.
Gnomon caches extension DLLs globally for speed purposes, but makes them available only at the configured scope.
Table of Contents
Gnomon provides a number of built-in functions that allow accessing the HTTP request state and modifying the response state. These functions cover reading HTTP headers, query string and POST data, and cookies.
The namespace for the builtin extension functions is http://www.npsl.co.uk/gnomon. To use them, map this namespace to a prefix in your stylesheet and then call the function via its qualified name, for example:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:gnomon="http://www.npsl.co.uk/gnomon"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl gnomon">
<xsl:output method="html" indent="yes" version="4.0" encoding="utf-8" media-type="text/html />
<xsl:template match="/">
<html>
<head><title>Cookie test</title></head>
<body>
<p>The single value of the foo cookie is: <xsl:value-of select="gnomon:read-cookies('foo','')"/></p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Gnomon's cookie handling is modelled on that of ASP (so that cookies can be accessed in the same way from ASP and ASP.NET pages). This means that cookies can have either a single value, or be considered as a key/value dictionary. These uses are mutually exclusive; setting a key/value pair will destroy any existing single value and vice versa.
ASP also limits itself to the ASCII character set, any characters with values outside the range 1-127 are silently discarded. Gnomon follows this model for compatibility. (Strictly speaking, cookies are opaque -- hence we could store any data, especially if it is URIEncoded to save confusing clients).
Gnomon also supports only the cookie attributes that ASP does. These are:
The cookies collection is updated in linear processing order. If a cookie is added or modified, then the new or modified cookie will be available to any included templates (even if they are generated from ASP or ASP.NET pages). This gives a consistent view on cookies.
read-cookies()
This function returns a nodeset representing all cookies in the request. A sample nodeset is:
<cookies> <cookie name="foo" secure="0" path="/" value="single-value"/> <cookie name="foo2"> <value name="key1" value="value1"/> <value name="key2" value="value2"/> </cookie>
read-cookies(cookie-name)
This function returns a nodeset representing a single cookie. The nodeset consists of a cookie node as shown above, or is empty if the cookie is not found.
read-cookies(cookie-name, key-name)
This function returns a string representing the named key from the cookie. If the key name is '' (empty string) then the single value of the cookie is returned. If the named key is not present, and empty string is returned.
read-cookies(cookie-name, attribute-or-key-name, attribute-flag)
This function allows the Expires, Domain, Path and Secure attributes to be read from the cookie. Note that if the cookie is sent by the client, typically these attributes are not reported -- you can read the back if they have been written by an earlier call to write-cookies or via a call to an ASP page. If the attribute-flag is zero, this function behaves exactly as for the two argument version above. If the attribute-flag is non-zero, this function will interpret the attribute-or-key-name parameter as an attribute name. The returned value is a string for Expires, Domain and Path or a number for Secure.
write-cookies(cookie-name, value)
This function sets the single value of the given cookie. If the cookie does not exist, a new cookie is created. This will destroy any dictionary values of the cookie.
write-cookies(cookie-name, key-name, vakue)
This function sets a key of the cookie to the specified value. This will destroy any single value previously set.
write-cookies(cookie-name, key-or-attribute-name, value, attribute-flag)
This function allows write access to the attributes of the cookie. If attribute-flag is 0, then this function behaves exactly as the three argument version above.
Gnomon allows full access to the GET (query string) and POST (form) data of a request. Currently only POST data in application/x-www-form-urlencoded is processed; that is multipart form encodings are not yet handled. (However, generally these are used for file uploads, so you would not want to process these in gnomon).
For non-ASCII data, handling of GET and POST data can require some care. There is little agreement about the codepage that such data should be sent in. The typical action of a web browser is to send data in the original encoding of the page. By default, ASP interprets such data in the local code page of the server (as can be seen, this is generally wrong). The preferred solution that I adopt is to send all pages in UTF-8, set the codepage for ASP to UTF-8, and configure gnomon to interpret URIEncoded data as UTF-8. You can pick a different codepage if you like; but the key to good inter-operation is a consistent choice! See the note on configuring ASP and gnomon's URI codepage for more information.
Gnomon gives a single overview on request data. That is query-string, post and template collections. POST data can be split per form to POST appropriate data back to different templates. For more information on gnomon templates, see the templates chapter. The request function can be limited to return data from individual templates and collections. Templates are identified by template-id (which, as discussed, is always avaialble as a parameter), and the collection by a letter:
The collections are searched and returned in the order specified in the search string (e.g. qfp). The search string is case-sensitive, and the request function will raise an error if any other value is specified. It is permissble, though pointless, to search a particular collection more than once.
Each item in a collection can have more than one value. (This can occur in real data for example in the case of multiple select lists). The values are considered as a vector, and can be accessed by index. Indexes start at zero.
request(search-string)
This function returns values for all templates from the given collections. The returned value is an XML fragment, which typically looks like:
<request> <collection id="0"> <query-string> <item name="foo" value="value1,value2"> <value index="0">value1</value> <value index="1">value2</value> </item> <item name="bar" value="single-value"> <value index="0">single-value</value> </item> </query-string> </collection> </request>
request(search-string, template-id)
This function returns values for the specified template from any given collections. The returned value is an XML fragment, which is as detailed above apart from the fact that the "collection" node is the root of the fragment.
request(search-string, template-id, key-name)
This function returns a string which is the value associated with the given key. For example, if the URI of a document is http://localhost/test.acds?foo=value&bar=value1&bar=value2 then request('q', 0, 'foo') returns 'value'. If multiple values are present for a given key, then they are returned as a comma separated list. Commas within values are not quoted, so you should use one of the other forms of this function (particularly the next one) if multiple values are of interest. For example, request('q', '0', 'bar') would return 'value1,value2'. If the key does not exist, an empty string is returned.
request(search-string, template-id, key-name, value-index)
This function returns the value with index value-index associated with key-name. This allows you to return individual items that are associated with the same key. If the key or index are not found, then an empty string is returned. As in the above example, request('q', 0, 'bar', 0) returns 'value1' and request('q', 0, 'bar', 1) returns 'value2'.
Gnomon provides access to CGI variables. See the documentation for the ASP.ServerVariables collection for more details on which variables exist. Note that all headers sent in the request in the server variables collection, and are renamed to HTTP_header, e.g. server-variables('HTTP_HOST') will return the the Host: header. (This is normal for CGI applications).
server-variables()
This function returns a nodeset containing all of the known CGI variables. A sample nodeset is:
<server-variables> <variable name="APPL_MD_PATH" value="/LM/W3SVC/1/Root"/> </server-variables>
server-variables(variable-name)
This function returns the value of a single server variable. If the variable is not found then an empty string is returned.
parse-xml(xml-string)
This extension function parses an XML fragment and returns a nodeset containing the parsed XML. This allows you further process XML that is stored in strings using normal XSLT. This can be very useful if the XML has been retrieved by a database query. For example:
<xsl:variable name="frag"> <some-xml> <node/> </some-xml> </xsl:variable> <xsl:apply-templates select="gnomon:parse-xml($frag)"/>
parse-xml(xml-string, ignore-errors)
This function behaves exactly for the single argument form if ignore errors is not 1. Otherwise, rather than reporting any errors that arise during xml parsing, an empty nodeset is returned.
Gnomon provides a single global dictionary that can be used to store any XSL object. This violates the functional programming style of XSLT (as it could be argued does write-cookies) Since Xalan processes XSLT linearly, this works, and using dictionaries (and counters -- see below), you can make XSLT behave as a standard linear programming language. You can consider this a hack, but nonetheless, it's very useful.
dictionary()
This returns the entire contents of the dictionary as a nodeset.
dictionary(key)
This returns the object associated with key. This object may be any valid XSL object -- nodeset, boolean, result tree fragment, number or string.
dictionary(key, value)>
This associates the value with the key. As above the object may be any valid XSL object. For example:
<xsl:variable name="dummy" select="gnomon:dictionary('foo',bar')"/>
<xsl:variable name="foo" select="gnomon:dictionary('foo')"/>
This would store the value 'bar' in the variable 'foo'.
Gnomon provides a dictionary of named counters. Each counter has an initial value of zero. Counters, like dictionaries, allow linear programming in XSL, and can be considered simply as a useful hack. Counters are 32 bit signed integers, and may become negative.
counter(counter-name)
This returns the value of the named counter. All counters exist, and have an initial value of zero.
counter(counter-name,action ('inc'|'dec'))This increments or decrements the named counter according to the specified action. The return value is the new value of the counter. If action is not inc or dec then an error is raised.
counter(counter-name,action ('inc','dec','set'),value)This increments, decrements or sets the named counter. If the action is set, then the counter is given the specified value. If the action is inc or dec, then the counter is incremented or decremented by the given value. The function returns the new value of the counter. If the action is not inc or dec then an error is raised.
Gnomon provides a function for including XML documents that is similar to the XSL document() function, save for the fact that parameters can be passed to the included document. These parameters are accessible through the request function described earlier in this chapter. The included XML documents are gnomon as templates, this is described in detail in the chapter on templates.
include-xml(document-href, parameters)
The document-href specifies the URI of the included document. If this is a relative path then it is resolved relative to the parent document. The parameters specifies a nodeset containing a list of parameter nodes. The name attribute of these nodes identifies the name of the parameter, and the contents are its value. For example:
<xsl:variable name="params">
<parameter name="foo">bar</parameter>
</xsl:variable>
<xsl:variable name="dummy" select="gnomon:include-xml('../some-include.xml', $params)"/>
Gnomon has the ability to include other documents in the root level document. (This is separate from the XSL document() function, which acts as normal.) When this functionality is used, then gnomon calls all such documents templates. Templates differ from normal included documents in three ways:
The functionality for splitting POST data into per-template data is dependent on the existence of a special value in the post data - gnomon-multipart-form. If this value is present, then the data will be split, otherwise it will be interpreted as all belonging to the top level template (i.e. as if the POST data splitting was turned off).
The name of each value in a valid gnomon-multipart-form follow a specific format. That is, the name is templateid_valuename. Any value without a valid prefix is assigned to the top level template (id zero). It is expected that to facilitate producing such forms, the XSL will assign the names for the form element based on the value of request('template-id').
Parameters are an extra, optional collection that is provided when using templates. The contents of the parameters collection can be accessed through request('p', ...). When parameters are based between templates, they are URI encoded according to the codepage set in the gnomon.config file. In general, parameters allow controlling the processing of the include template, for example looking up different database items. You are free to ignore the parameters collection if desired.
Parameters are passed to included templates by the use of an extra HTTP header: X-Gnomon-Parameters. The header looks like:
X-Gnomon-Parameters: encoding=utf-8; foo=value1&bar=value2
i.e., essentially using the encoding x-www-form-urlencoded. Gnomon uses the encoding specified in the uri-codepage option in the gnomon.config file (this defaults to UTF8). Any templates that wish to access gnomon's parameter data should do so through the use of this header (e.g. in ASP, you can use Request.ServerVariables("HTTP_X_GNOMON_PARAMETERS" to access it.
For ASP only, gnomon provides a replacement for the ASP request object that allows parameters to be accessed in a straightforward fashion. This object is called gnomonASP.Request and can be instantiated created by a call to Server.CreateObject. If not using parameters, the ASP Request collection can be accessed as normal.
Dim gnomonRequest
Set gnomonRequest = Server.CreateObject("gnomonASP.Request")
This extension works exactly as for the Request object, save that:
The gnomonASP object must be in a COM+ package. This is because it uses COM+ object context to access ASP objects. The installer will create an appropriate package, if used. If not used, you should place gnomonASP.dll into an empty library package.
include-xml(document-href, parameters)
This function includes the template indicated by document-href, passing the parameters collection as described above.
The SQL extension allows gnomon to access databases via OLEDB. The SQL extension provides principally for SELECT queries and stored procedure calls that return rowsets. You can make other calls (UPDATE, INSERT, etc), but output parameters (other than rowsets) are not presently supported.
sql-new('connection')This function is slightly misnamed, in that it does not, in fact, create a new connection -- it merely performs a registry key lookup to find a connection string. (This is a convience that fits well into other NPSL code). This may change in future (when I think of a better way of doing it).
The connection string searched for is under the following registry key:
HKEY_LOCAL_MACHINE\Software\NPSL\npslda2\[CONNECTION NAME]\ConnectionString
Here, [CONNECTION NAME] is the argument passed to sql-new. Whilst strange, this function is reasonably convenient and keeps long connection strings out of XSLT, so its use is recommended.
sql-query(connection-string, query, parameters)
This executes a SQL query on the database identified by the given connection string. The query should return a rowset (i.e. be a SELECT statement). The function returns the result of the query as an XML fragment. To reduce database load, it is better to store the result of a query in a variable (or gnomon:dictionary.)
If you have chosen to follow the NPSL format and store connection strings in the registry, then you can do something like the following:
<xsl:variable name="connstr" select="gnomon:sql-new(gnomon:server-variables('HTTP_HOST'))" />
<xsl:variable name="params">
<parameter><xsl:variable name="emp_id" select="gnomon:request('q','EMP_ID')"/></parameter>
</xsl:variable>
<xsl:variable name="sqldata" select="gnomon:sql-query($connstr, 'SELECT FROM EMP WHERE emp_id = ?', $params)" />
This looks for a database named as the host serving the request, and selects an employee based on an ID passed in the query string, a sample request may be something like http://www.mycompany.com/showempdetails.acds?emp_id=100. Security considerations aside, this shows how dynamic gnomon allows XSLT to become.
The parameters argument specifies any parameters to bind to the query. parameters is a nodeset consisting of <parameter> nodes. Any parameters in the query text should be specified by using a ? (this is standard ODBC call syntax). An example of the parameters variable:
<parameter>value</parameter> <parameter>value</parameter>
The resulting rowset is as follows:
<sql> <metadata> <column-header column-label="label" position="0"/> </metadata> <row-set> <row> <col> <value>some-value</value> </col> <col> <value>array-value-0</value> <value>array-value-1</value> </col> </row> </row-set> </sql>
An example of invocation:
<xsl:variable name="result" select="gnomon:sql-query('Provider=OraOLEDB.Oracle;PLSQLRset=1;User ID=scott;Password=tiger;Data Source=test;Persist Security Info=True;', 'SELECT from emp')"/>
<xsl:apply-templates select="$result"/>
Unusually, the sql-query function is capabable of processing array valued columns -- this currently is only known to be possible with custom OLEDB providers. (NPSL use a custom OLEDB provider for their content management system).
sql-sp(connection-string, sp_name, parameters)
This executes a call to a stored procedure on the database identified by the given connection string. The stored procedure should return a rowset, which will be exposed as an XML fragment exactly as above. Any parameters in the parameters collection are bound to the stored procedure call.
The session extension allows for storing session state in a database. This extension does not provide access to ASP sessions, but instead replaces them with a method of storing state that works across multiple (load balanced) servers. The replacement session works much as for the ASP session object, in that it provides a dictionary of key/value pairs.
The connection string for the database holding the session state must be found under the following registry key:
HKEY_LOCAL_MACHINE\Software\NPSL\npslda2\security\ConnectionString
(This name comes from other NPSL code).
The session extension/NPSLSession3 object have a number of other options that can be controlled by setting registry values under the registry key
HKEY_LOCAL_MACHINE\Software\NPSL\Session3
These are:
gnomon does not provide functions to access the session timeout, or migrate or abandon the session. The NPSLSession3 object (described below) has properties/methods that can Abandon, Migrate and set the Timeout of sessions from ASP code, so this needs to be done from an ASP page for now.
Note that neither the gnomon session extension nor the NPSLSession3 object actually delete session data. When a session has expired, it is simply hidden (by the WHERE clauses of SQL queries). To delete the actual data, you should add a database job that cleans up old sessions based on the last_used column of the sessionstate table. This is generally a more effective process than having each call to the session object have to check for expired sessions. Note that we cannot rely on the user to tell us when the session is finished: them closing the browser doesn't tell us that they have finished.
The session can be accessed from ASP pages using the NPSLSession3.SessionX object (the bizarre name is historical). When instantiated, this object works as for the ASP session object, but will share the values that are accessed through the gnomon:session function. To ensure consistency across included templates, before any document (including those included through the XSL document() function), session state is saved, and after the inclusion of the external document, it is re-read.
For example,
Dim SessionX = CreateObject("NPSLSession3.SessionX")
SessionX("foo") = bar
Response.Write "foo is " & SessionX("foo")
The session extension supports Oracle and SQL server databases (other databases may be supported in future -- the modifications required to support a different database should be minor). In Oracle, a table of the following format must exist:
CREATE TABLE SESSIONSTATE ( SESSION_ID CHAR (36) NOT NULL, TIMEOUT NUMBER (10), LAST_USED DATE, STATE BLOB, CONSTRAINT PK_SESSIONSTATE PRIMARY KEY ( SESSION_ID )) ORGANIZATION INDEX NOCOMPRESS PCTTHRESHOLD 50 TABLESPACE INDX;
For old version of Oracle (<= 7, i.e. those not supporting BLOBs), a LONG RAW column may be used.
In SQL server a table of the following format must exist:
CREATE TABLE [SessionState] ( [SESSION_ID] [uniqueidentifier] NOT NULL , [TIMEOUT] [real] NULL , [LAST_USED] [datetime] NULL , [STATE] [image] NULL , CONSTRAINT [PK_SESSIONSTATE] PRIMARY KEY CLUSTERED ( [SESSION_ID] ) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
The NPSLSession3.SessionX object must be placed into a COM+ library package, as it uses COM+ context to access the ASP objects. If you have not selected to configure the gnomon COM+ package at installation time and you wish to use this object, you should perform these steps manually.
Both NPSLSession3 and gnomon's session extension rely on a dictionary object: NPSLDictionary. This object is essentially the same as Scripting.Dictionary, save for the fact that it is thread safe (therefore suitable for use as a cross-page cache in the application object), and can be persisted. The source code for the dictionary object is provided, and the dictionary object will be installed by the installer. The dictionary object does not use COM+, so nothing special needs to be done with it.
session(key)
This function returns the named key from the current session. If the key is not present, then an empty string is returned.
session(key, value)
This function sets the key to the given value.
The HTML tidy extension uses the HTML tidy parser to loosely parse HTML, cleans it up so that it is valid XML, and then produces a nodeset from it. This support is useful for applying XSLT to legacy HTML pages (although this is obviously a more expensive process than parsing XML pages).
parse-html(html-string)
html-string is the html to parse. The result is an empty nodeset if the HTML could not be parsed, or a nodeset representing the entire parsed document if parsing was successful. Note that tidy produces a valid document structure, so this will always include the root (<html> node), <head> and <body> nodes.
Table of Contents
Gnomon has a number of settings that can be tuned to maximize performance. Whilst the default configuration works well, specific tuning for your application can provide significant benefits.
The settings that can be tuned are contained in the registry, and are read once at startup time. These settings cannot be modified whilst gnomon is running -- you will need to restart the web service in order to do this. Also note that by default these settings do not exist.
In order to understand how to best tune gnomon, you will need to understand a little of how it works internally. There are two basic concepts:
Note that there is not necessarily a 1-1 mapping between threads and requests. It is entirely possible to have more requests executing than threads: this is because the requests are processed asynchronously. When a request is not assigned to a thread, it is effectively dormant (generally waiting on network I/O).
Technical detail (not required to follow the rest of this section): since Xalan operates in a mode in which it cannot be interrupted to perform network I/O the request code is run in a separate fiber. Fibers are self scheduled threads, with lightweight context switches. When Xalan requests a document, we save the request state by switching fiber, start network I/O and return the thread that was executing the transformation to the pool. When the network I/O completes, then we switch the fiber back in and carry on where we left off.
By default, gnomon creates 8 worker threads per processor, and allows 64 concurrent requests. Thus, on a single CPU machine, 8 requests can be concurrently performing CPU work, with the other 56 blocked on network I/O or waiting for a pooled thread to become available to handle them.
Note that these settings are per instance of gnomon. It is possible to have multiple instances of gnomon if you have websites or virtual directories set to run in different processes. For example, if you have two high (isolated) applications you will be running two instances of gnomon, or if you have two websites with two different protection levels -- perhaps one low (IIS process) and one medium (pooled). You should bear this in mind when performance tuning and consider also setting the application protection levels differently.
To decide on the number of worker threads, you should take the following factors into account:
To decide on the number of concurrent requests, consider the following:
Here you can see that increasing the number of concurrent requests increases memory pressure on the server, which if pushed too far will lead to swapping to disk. This will considerably slow things down. However, you want enough concurrent requests to be executing so that the CPU is kept busy with work -- i.e. gnomon is not waiting on network I/O. Tuning this depends entirely on your application, i.e. the properties of the code generating the source XML.
Also note that increasing the number of worker threads beyond the number of concurrent requests will mean that there will always be idle threads in the pool.
Any requests that can't be handled immediately are placed into a queue. This is the case if the number of concurrently executing requests exceeds the configured maximum. If the backlog queue is exceeded, then a server SERVER_TOO_BUSY error is generated (and is handled by the error handling mechanism specified in the configuration file).
The goal when tuning the backlog queue length is to ensure that people do not have to wait too long for a server too busy message (say 10-15 seconds), but that outstanding requests are not rejected too early. The length of queue that you will want depends roughly on the average page generation time for your application. If the page generation time is very small, then you will want a longer backlog queue. If the page generation time is very larger, then you will want a shorter one. The best way to decide how long to make the backlog queue is to stress test your application with a large queue length, determine the average response time, and then set the queue length based on (Concurrent Requests * Desired Maximum Queuing Time) / Average Response Time.
As a point of interest, there was some experimentation to try to get gnomon to do this automatically; but it seemed that measurements of the current average response time was subject too much variation.
The configurable registry entires are stored under the registry key
HKEY_LOCAL_MACHINE\Software\NPSL\gnomon
This key will be created by default by the gnomon installer, or you can create it manually if it doesn't exist. Gnomon will start without this registry key using default settings.
Gnomon provides performance counters that can be used to give an overview of what it is doing at any one time. These performance counters are made available to Window's Performance Monitor as for any other performance counter. Note that until gnomon has started, these counters will not be visible. IIS does not start ISAPI extensions until at least one request has made, so if you are having trouble seeing the counters, make a page request first.
To view performance counters, start Performance Monitor (from Control Panel/Administrative Tools/Performance), request a page to ensure that gnomon is running, then click the '+' icon. The performance object is called 'gnomon XSLT processor' and should be visible in the list. Each performance counter provides detailed help. Click on the Explain... for a description. In addition, the available counters and their meanings are outlined below.
Note that if multiple instances of gnomon are running then the performance counters will be the aggregate of those used on all instances of gnomon. Also note that the average transformation time and last transformation time counters do not lock between instances of gnomon (and both are 64-bit), so in rare circumstances they may become corrupted and an erroneous value may be displayed temporarily. (This is not dangerous, merely cosmetic). Multiple instances of gnomon may be running if you have websites set to run in different processes, e.g. two high (isolated) sites or one low (IIS process) and one medium (pooled).
Bearing in mind the details outlined in the sections above, here is one way of approaching performance tuning.
Firstly, get hold of a copy of the freely available Microsoft Web Application Stress Tool. This is an excellent tool to help you stress test load your website and is useful in both eliciting problems and in load planning. Next, create an appropriate script in WAST to stress your site. Once you have this script setup and working, then you will want to decide what kind of load to tune for. Excellent resources for this are existing web and traffic logs. Also estimate the growth of this traffic with time to ensure that you have enough hardware to scale upwards.
When these numbers are determined, the stress level in WAST can be set according to the expected maximum load. Whilst the script is running, monitor the following gnomon performance counteres:
The goal of tuning is to maximise the Requests succeeded/sec figure whilst keeping the errors and too busy replies to a minimum. To tune the loopback request errors to be as low as possible, ensure that IIS itself is configured to handle the load that you are generating. See IIS documentation and MSDN for performance tuning IIS.
To tune concurrent requests and worker threads, take the factors outlined above into account to decide on some reasonable initial values. Start by using more concurrent requests than you think you will need, in order to tune the number of worker threads (bear in mind that these create additional memory pressures so do not increase the value too much). (Having more concurrent requests than are needed will prevent blocking on network I/O from affecting the test results).
Then, gradually decrease the number of threads (with at least one per processor) and the Requests succeded/sec figure should increase. This will also increase the variability of the response times, so you should decide on what balance of fairness and performance is acceptable. If you are using the session or sql extension, keep an eye on the CPU load here - if it begins to drop below 100% then you will need more threads as threads will be waiting for the database.
To tune concurrent requests, decrease the number of concurrent requests until the CPU load starts to decrease. When this occurs then requests are blocking on network I/O so you should increase the number again to just above this point. Using the minimum number of concurrent requests leaves the maximum available memory for the rest of the server.
To tune the too busy replies, you should use WAST to measure the average response time when gnomon is under load. To do this first ensure that there are no too busy replies by increasing the backlog queue to a sufficiently large number. The backlog queue takes memory, so don't increase it too far otherwise the artificial memory pressure on the server will affect the results. Then having measured this response time then you can use the formula (Concurrent Requests * Desired Maximum Queuing Time) / Average Response Time to set the backlog queue to be length to be equivalent to the desired maximum queueing time on average. Any request that would take longer than that time will be rejected with a "503 Service Unavailable" response.
Gnomon is distributed under an open source derived from the Apache license. The license is reproduced below.
The NPSL Software License, Version 1 based on the Apache Software License, Version 1.1 Copyright (c) 2003-2004 NPSL Ltd. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by NPSL Ltd. (http://www.npsl.co.uk/)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The names "Gnomon" and "NPSL" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact mark@npsl.co.uk. 5. Products derived from this software may not be called "gnomon", nor may "NPSL" appear in their name, without prior written permission of NPSL Ltd. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NPSL LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Gnomon includes software developed by the Apache Software Foundation (http://www.apache.org/), namely Xalan and Xerces.
Gnomon includes OW32 (see http://www.blushingpenguin.com.) OW32 is licensed under the GNU Lesser General Public License, which is reproduced below.
GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS