7.1 Introduction to eXtensible Server Pages

     

Originally created by the developers of AxKit's sister Apache project, Cocoon, eXtensible Server Pages (XSP) offers an XML-centric implementation of the Server Pages model. Unlike XSLT or XPathScript, an XSP document is not a separate stylesheet that is applied to an XML document to transform it; rather, XSP is concerned solely with generating content. Literal markup is mixed with functional code and elements from the XSP grammar within a single document to create an XML instance dynamically when that document is passed through the XSP processor. In AxKit's implementation, the embedded programming language is Perl.

As with AxKit's other Language modules, support for XSP must be explicitly enabled using the AxAddStyleMap configuration directive:

 AxAddStyleMap application/x-xsp Apache::AxKit::Language::XSP 

XSP diverges from the norm a bit in how documents are associated with the XSP processor. Unlike XSLT or XPathScript, XSP is self-contained, and there is no external stylesheet to apply. This means that when setting up the style processing directives for XSP, you use the literal string NULL , whereas if you use another Language module, you would typically put the path to the stylesheet.

 # The familiar pattern, using XSLT AxAddProcessor text/xsl /styles/somestyle.xsl # In XSP there is no stylesheet, so the  # string NULL is used instead AxAddProcessor application/x-xsp NULL # The same, but as a conditional processor, based # on the top-level element name and its namespace URI AxAddRootProcessor application/x-xsp NULL {http://apache.org/xsp/core/v1}page 

7.1.1 XSP Basics

XSP's basic syntactic requirements are very simple: every valid XSP document must be a well- formed XML document, and all XSP elements must be bound to the namespace URI http://apache.org/xsp/core/v1 (binding that URI to the prefix xsp is conventional, but certainly not required). In contrast to XPathScript, Active Server Pages, PHP, and similar technologies that use special pseudo tags (such as <% . . . %> , for example) as delimiters to separate code from literal markup, XSP uses an XML application grammar that separates literal and processed output through the use of specific XML elements bound to the XSP namespace. For example, the xsp:logic element creates a block that can contain any block of free-form Perl code, while the xsp:expr element interpolates a single expression into its literal result. The following shows a minimal, valid XSP document that inserts the current time into the content via an xsp:expr element:

 <?xml version="1.0"?> <application>   <time>       <xsp:expr xmlns:xsp="http://apache.org/xsp/core/v1">scalar localtime( )</xsp:expr>.   </time> </application> 

Although XSP elements and code can be embedded into any XML document and passed to the XSP processor, an XSP page more commonly contains: a top-level xsp:page element containing optional xsp:structure and xsp:logic elements, and a required non-XSP element that becomes the top-level element of the result generated by the XSP processor (this is often called the user -root element). The following illustrates this basic structure:

 <?xml version="1.0"?> <!--  The  top-level <xsp:page> element, bound to the XSP namespace URI --> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1">          <!--  optional <xsp:structure> used to import external Perl modules for use  in the current page-->     <xsp:structure>         <xsp:import>Some::Perl::Module</xsp:import>     </xsp:structure>          <!-- class-level logic (subroutines, global initialization, etc.) -->     <xsp:logic>         sub now {             return scalar localtime( );         }     </xsp:logic>          <!-- The following <application> element is the user-root        that will become the top-level element after processing -->     <application>         <time>             <xsp:expr>now( )</xsp:expr>         </time>     </application> </xsp:page> 

The following shows the result returned from both of the above examples:

 <?xml version="1.0"?> <application>   <time>Thu Feb 12 20:26:11 2004</time> </application> 

Before we dig further into the details of XSP's syntax, it is important to note that the most effective use of XSP extends the Server Pages model beyond the code-mixed-with-HTML mess that may have driven you to look for an alternative environment such as AxKit in the first place. Yes, XSP allows and even encourages the mixture of XML markup and Perl code, but this is not the same as having code hardwired to generate presentational markup (which is arguably the true weakness found in most uses of the Server Pages technologies, not simply the fact that code and markup appear in the same document). To get the maximum benefit from XSP and AxKit, it is best to make sure that the markup that your XSP pages generate conforms to a grammar that best reflects the semantics and state of the application, irrespective of how that content may be presented. As long as your XSP-based applications meet this criteria, the syntactic details of how that data may be generated become solely a matter of personal taste.

For many web developers, the XML-influenced approach to generating dynamic content requires a little bit of mental adjustment. One tends to think of generating pages or screens , rather than constructing semantically meaningful data structures. It helps to remember, though, that the result generated by the XSP processor is really the beginning of the processing chain, not the end. In most cases, at least one transformative style will be applied to the XML created, and it is the stylesheet's job to transform the generated data into a usable interface. Therefore, you are free to generate simpler markup that reflects only the application's state and data structures.

Now that this conceptual foundation is laid, let's get to the business of introducing XSP. First, here is a summary of the grammar as a whole:


xsp:page

The optional top-level element for an XSP document. If present, it must be the top-level element of the document.


xsp:structure

A wrapper element for one or more xsp:import elements. May appear only as a child of a top-level xsp:page element and before the opening tag of the document's user-root element.


xsp:import

The text content of this element is expected to contain the name of a Perl class to be loaded during processing (similar to Perl's use function). This element may only appear as a child of the xsp:structure element.


xsp:logic

The content of this element is expected to be a free-form Perl block that is evaluated during processing.


xsp:expr

The content of this element is expected to be a Perl expression that is interpolated during processing. The result is added to the output as a literal string.


xsp:content

The content of this element is expected to be a well-formed XML fragment and is added, as is, to the resulting output. Although it may appear anywhere inside the user-root element, it most often appears as the child of an xsp:logic element, as a convenient way to escape out of the code to generate markup.


xsp:element

Creates an XML element whose name is defined by the required name attribute.


xsp:comment

The contents of this element will be wrapped in an XML comment in the resulting output.


xsp:pi

Will be expanded into an XML processing instruction, whose target is defined by the required target attribute.

XSP's grammar is quite smalljust nine elementsbut these elements, plus Perl's conditional statements, loops , and other logical constructs, can be combined to create very sophisticated XML content.

7.1.1.1 Generating content

First, it is important to keep in mind that all non-XSP elements, not otherwise skipped over based on conditional logic, are passed through the XSP processor verbatim into the generated result:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1"> <application>     <user-info/> </application> </xsp:page> 

Running this through the XSP processor gives the following predictable result:

 <application>     <user-info/> </application> 

Free-form Perl code can be embedded any place inside an XSP document using the xsp:logic element. At the same time, single elements or well-balanced chunks can be generated from within those logic blocks by wrapping the output in an xsp:content element. Also, the xsp:expr element can be used to interpolate a simple Perl scalar expression into a literal string:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1"> <application>     <user-info>     <xsp:logic>         my $connection = $r->connection;         my $ip = $connection->remote_ip;                  <xsp:content>             <ip-address><xsp:expr>$ip</xsp:expr></ip-address>         </xsp:content>                  if ( $ip =~ /^192\./ ) {             <xsp:content>               <is-local>true</is-local>             </xsp:content>         }         else {             <xsp:content>               <is-local>false</is-local>             </xsp:content>         }     </xsp:logic>     </user-info> </application> </xsp:page> 

In most cases, the XSP processor is smart enough to tell the difference between Perl code and literal markup that is meant to be part of the generated result. This makes the xsp:content element optional. Therefore, you could trim the previous example into the following and get the same result:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1"> <application>     <user-info>     <xsp:logic>         my $connection = $r->connection;         my $ip = $connection->remote_ip;         <ip-address><xsp:expr>$ip</xsp:expr></ip-address>                  if ( $ip =~ /^192\./ ) {               <is-local>true</is-local>         }         else {               <is-local>false</is-local>         }     </xsp:logic>     </user-info> </application> </xsp:page> 

There are a few things to remember about xsp:logic elements. First, any logic block that appears outside of the user-root element is considered a special, global block that the XSP processor only interprets once . This global section is intended to provide a handy place to declare any subroutines or constant variables that your XSP page may need, while saving the overhead associated with reevaluating the code for every request. This provides a performance boost, but the behavior of the special, global case can sometimes snare the unwary. The following two XSP pages exemplify a common trap:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1">     <!-- logic block outside of the user-root -->     <xsp:logic>         my $time = scalar localtime( );     </xsp:logic> <application>     <time><xsp:expr>$time</xsp:expr></time> </application> </xsp:page> 

The $time scalar variable is set in an xsp:logic block outside of the user-root application elementthat is, in the special, "global" section. Compare that with this very similar example:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1"> <application>     <!-- the same block, but inside the user-root -->     <xsp:logic>         my $time = scalar localtime( );     </xsp:logic>     <time><xsp:expr>$time</xsp:expr></time> </application> </xsp:page> 

The two appear identical, but in the second example, the $time scalar is set in a logic block within the user-root element. You may expect that the two pages would return the same results, but remember that any logic block that appears outside of the user-root element is a special case that is only evaluated once. So the result returned from the first page shows the same time string no matter how many times the page is requested , while the second updates the time string for each request.

However, it is perfectly safe to put subroutines in the global block. In this case, it doesn't matter that the code is only evaluated once. The subroutine still executes at request time. So if you don't want to put the xsp:logic element inside the user-root, the following still works as you'd expect:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1">     <!-- logic block outside of the user-root -->     <xsp:logic>         sub now { return scalar localtime( ); }     </xsp:logic> <application>     <time><xsp:expr>now( )</xsp:expr></time> </application> </xsp:page> 

The second thing to keep in mind when creating inline xsp:logic blocks concerns possible syntactic conflicts between Perl and XML. Certain common Perl expressions can cause XML well- formedness errors unless they are properly escaped. The specific characters you have to watch out for are « & » and « < ».

 <xsp:logic>     # Parser sees '<' and thinks it's the beginning of a new XML element     if ( $this_number < $that_number ) {  }     my $here_doc = <<"TARGET";          # Parser sees '&' and thinks it's the beginning of an XML entity reference     if ( $this_condition && $that_condition ) {  }     my $val = &sone_function( ); </logic> 

The preferred solution here is to wrap the contents of the xsp:logic element that contain the conflicting characters in a CDATA (character data) section that the XML parser passes over without trying to parse it:

 <xsp:logic><![CDATA[    # You are now free to use & and < to your heart's content  ]]><xsp:logic> 

Using a CDATA section to tell the XML parser to skip the contents of the logic block also tells the parser to ignore any XSP elements or literal output, as well, so be sure to break out of the CDATA if you want to return content.

New XML elements may also be generated through use of the xsp:element element. The string passed to this element's required name attribute becomes the name of the newly generated element. The value set for the name attribute is usually a literal string, but you can also pass in a Perl scalar expression by wrapping it in curly braces. This is similar to XSLT's attribute value templates and offers the ability to produce elements whose names are generated programmatically at request time.

 <params>   <xsp:logic>   my %query_params = $r->args;   foreach my $key ( keys( %query_params )) {       <xsp:element name="{$key}"><xsp:expr>$query_params{$key}</xsp:expr></xsp:element>   }   </xsp:logic> </params> 

Similarly, XML attributes can be generated via the xsp:attribute element. This element's required name attribute becomes the name of the newly created attribute, while the contents of this element become the attribute's value.

 <params>   <xsp:logic>   my %query_params = $r->args;   foreach my $key ( keys( %query_params )) {       <param>         <xsp:attribute name="name"><xsp:expr>$key</xsp:expr></xsp:attribute>         <xsp:attribute name="value"><xsp:expr>$query_params{$key}</xsp:expr></xsp:attribute>       </param>   }   </xsp:logic> </params> 

Finally, comments and processing instructions can be generated using the xsp:comment and xsp:pi elements, respectively. Generating comments can be especially useful when debugging your XSP while it is still in development. Rather than dumping information to the host's error log, you can simply produce a comment for nonfatal but unexpected errors, and the result appears directly in the output in a way that's easily distinguished, visibly, from the rest of the content:

 <xsp:logic>     if ($var eq 'this' ) {         <this><xsp:expr>$var</xsp:expr></this>      }      else {          <xsp:comment>Was expecting $var to be 'this' but got <xsp:expr>$var</xsp:expr > instead.</xsp:comment>      } </xsp:logic> 

7.1.1.2 Using Perl modules in XSP pages

Perl's popularity as a programming language has as much to do with the army of useful, open source extension modules freely available from the Comprehensive Perl Archive Network (CPAN) as it does with any particular feature of the language itself. Once installed (details about installing modules from CPAN can be found by running perldoc CPAN at a shell prompt on any machine on which Perl is installed), these modules can be used from within your XSP pages by adding an xsp:structure element as a direct child of the top-level xsp:page element, then adding an xsp:import element containing the name of the Perl package for each module that you want to use in your page:

 <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1">   <xsp:structure>  <xsp:import>Geo::IP</xsp:import>  </xsp:structure>    <application>     <user-info>     <xsp:logic>         my $connection = $r->connection;         my $ip = $connection->remote_ip;  my $mapper = Geo::IP->new( );  <ip-address><xsp:expr>$ip</xsp:expr></ip-address>         <country><xsp:expr>  $mapper->country_name_by_addr($ip)  </xsp:expr></country>     </xsp:logic>     </user-info> </application> </xsp:page> 

You should now have a pretty good idea about XSP's page-level syntax and structure. There's more to the story, though. From what you have learned so far, you can generate application content quickly, but each page exists only unto itself, and the solutions are not reusable. Fortunately, AxKit's XSP implementation allows you to extend this basic framework to include your own custom application grammar extensions that can be reused in any number of pages to meet a variety of needs. These extensions are called tag libraries .

7.1.2 XSP Tag Libraries

XSP tag libraries (or taglibs , for short) provide a means to extend XSP's functionality by mapping XML elements in a specific, custom XML grammar to Perl code that expands and replaces those elements with other markup generated dynamically, performs a programmatic task behind the scenes, or most often, both. A tag library's elements are distinguished from literal output and elements in the XSP core grammar by binding those elements to a specific XML namespace. The following shows a simple XSP page that employs the Util tag library (available on CPAN) to return the current time:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1"  xmlns:util="http://apache.org/xsp/util/v1"  <application>       <time>  <util:time/>  </time>   </application> </xsp:page> 

XSP taglibs can be implemented in one of two ways: as a special XSLT, XPathScript, or other stylesheet (called a logicsheet ) used to preprocess the content, expanding the elements in the taglib grammar into the XSP elements and Perl codes that implement the taglib's behavior before the document is passed on the XSP processor, or more commonly, as a special Perl module registered with AxKit and used to extend the functionality of the XSP processor itself. You learn how to create both types of taglibs later in this chapter. For now, let's focus on some existing module-based taglibs and how they can be used to extend your XSP pages. The following explains just a few of the more popular or interesting XSP taglibs available for use in your XSP applications:


AxKit::XSP::Wiki

A complete Wiki application implemented in XSP. Uses POD as the page syntax.


AxKit::XSP::WebUtils

A general-purpose utility class that provides access to request and server data. Among other things, you can get or set HTTP headers, issue client redirects, examine whether the current request is using secure HTTP connections, and much more.


AxKit::XSP::Sendmail

Send email using this simple XSP interface to Perl's popular Mail::Sendmail module.


AxKit::XSP::ESQL

Feature-rich taglib for selecting data from any relational database supported by Perl's DBI.


AxKit::XSP::Util

Offers several utility functions for including content from local files, remote URI, and interpolated expressions, as well as the ability to get the current date and time in a variety of formats.


AxKit::XSP::LDAP

An easy-to-use XSP interface for retrieving data from LDAP servers.


AxKit::XSP::PerForm

Robust XSP helper class for creating and validating data entry applications.


AxKit::XSP::Swish

XSP interface to the popular site search/indexing tool, Swish-e.

There are many benefits of using XSP taglibs, but the most important is that they provide XSP developers with the ability to create generic, reusable libraries that implement common features or solve common problems.

7.1.2.1 Installing module-based XSP taglibs

Installing module-based XSP taglibs is exactly the same as installing any Perl module. If the taglib you want to install is available from CPAN, installation can be achieved quickly and painlessly using the CPAN shell that installs with Perl itself. Simply ask your systems administrator to install the taglib. (Be sure to provide her with the exact name of the taglib module you wish to install.) Or if you have permission, become root (superuser) and enter the following at a shell prompt:

 perl -MCPAN -e shell install Some::XSP::Taglib 

Once the module is installed, you must first register the taglib with AxKit before you can start using it with your XSP applications. This is achieved by adding an AxAddXSPTaglib configuration directive to your httpd.conf or other Apache configuration file and passing the Perl package name of the taglib module as the sole argument. The following registers the Param and ESQL taglibs with AxKit:

 AxAddXSPTaglib AxKit::XSP::Param AxAddXSPTaglib AxKit::XSP::ESQL 

Adding the taglib modules in this way does two things: it configures AxKit to load the module code (similar to Perl's built-in use statement), and it registers the unique XML namespace associated with that taglib grammar so that the XSP processor knows to dispatch the processing of elements in that namespace to the taglib's Perl package and not to simply pass them through as literal content.

Once a taglib module is registered, you may use the elements from its grammar in any XSP page. You need only to make sure to bind those elements to the XML namespace URI associated with that tag library; the XSP processor handles the rest.

The XSP taglib modules are already available from CPAN and cover an interesting set of cases. They are fantastic for adding reusable features to your own applications with a minimum of effort. However, there will surely be cases when what's out there does not meet your needs, and you should implement your own taglibs. Now that you have an idea about what XSP taglibs are and how they are used, let's examine how to create your own custom tag libraries.

7.1.2.2 Writing logicsheet taglibs

Module-based XSP taglibs (which we will examine shortly) are by far more common, but we would be remiss not to touch on the alternative, logicsheet taglibs , first. As I mentioned earlier, a logicsheet taglib is an XSLT, XPathScript, or other stylesheet applied to the source XSP document before it is handed to the XSP processor. During this transformation, elements in the tag library's grammar are expanded and replaced by the core XSP elements and Perl code that are required to implement the taglib's behavior.

Suppose that while designing our next XSP application, you discover that certain bits of environmental data are needed throughout each stage in the application. You decide to encapsulate access to that information in a tag library rather than duplicate the code in each XSP document. Specifically, you need to access the server's environment variable ( %ENV ), you need to know whether the current user has logged in (based on the presence of an HTTP cookie), and you want a list of links to simplify the creation of the ubiquitous "breadcrumb" navigation bar. Given these requirements, you could define your taglib grammar in the following way:

The XML namespace URI for this grammar is http://localhost/xsp/myapp , and the preferred namespace prefix is myapp .


myapp:env

This element is replaced by an env element containing a list of field elements, each containing name and value elements that reflect the name and value of each field in the %ENV hash.


myapp:breadcrumb

This element is replaced by a breadcrumb element that contains an ordered list of link elements. Each link element represents a link in the breadcrumb chain and contains both an href attribute containing the URI of that step and a title attribute that may be used when creating a hyperlink for that step.


myapp:logincheck

This element is replaced by a logged-in element whose text contents are either true or false depending on the presence or absence of an HTTP cookie. If the value of the logged-in element is true , a username element containing the current user's name will also appear.

An XSP page using this taglib grammar may look something like Example 7-1.

Example 7-1. minimal_myapp.xsp
 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1"           xmlns:myapp="http://localhost/myapp"> <xsp:structure>    <xsp:import>Apache::Cookie</xsp:import> </xsp:structure> <application>   <meta>     <myapp:breadcrumb/>     <myapp:env/>     <user>       <myapp:logincheck/>     </user>   </meta> </application> </xsp:page> 

Now you must create the logicsheet that expands the elements in the MyApp grammar into the XSP elements and Perl code that actually make the taglib work as you expect. Example 7-2 shows one possible way to do this, using XSLT to implement the logicsheet.

Example 7-2. myapp.xsl : the MyApp taglib as a preprocessed XSLT logicsheet
 <?xml version="1.0"?> <xsl:stylesheet   version="1.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   xmlns:xsp="http://apache.org/xsp/core/v1"   xmlns:myapp="http://localhost/xsp/myapp" > <xsl:template match="myapp:env"> <env>   <xsp:logic>     foreach my $key (keys(%ENV)) {         <field>           <name><xsp:expr>$key</xsp:expr></name>           <value><xsp:expr>$ENV{$key}</xsp:expr></value>         </field>     }   </xsp:logic> </env> </xsl:template> <xsl:template match="myapp:breadcrumb">   <breadcrumb>    <xsp:logic>     my $path ='/';              <link title="home" href="/"/>        my @steps = split '/', $r->uri;     for ( my $i = 0; @steps > $i; $i++ ) {         my $step = $steps[$i];         next unless length $step;         $path .= $step;         $path .= '/' unless $i =  = $#steps;              <link>           <xsp:attribute name="title">              <xsp:expr>$step</xsp:expr>           </xsp:attribute>           <xsp:attribute name="href">               <xsp:expr>$path</xsp:expr>           </xsp:attribute>        </link>     }   </xsp:logic>   </breadcrumb> </xsl:template> <xsl:template match="myapp:logincheck"> <xsp:logic>     my $cookie = Apache::Cookie->fetch;     if ( defined( $cookie->{'username'} ))  {         my $username = $cookie->{'username'}->value;         <logged-in>true</logged-in>         <username><xsp:expr>$username</xsp:expr></username>     }     else {         <logged-in>false</logged-in>     } </xsp:logic> </xsl:template>      <xsl:template match="*">   <xsl:copy>     <xsl:copy-of select="@*"/>     <xsl:apply-templates />   </xsl:copy> </xsl:template> 

This logicsheet taglib is really just a plain XSLT stylesheet that replaces the elements in the MyApp grammar with the code, XSP elements and literal markup required to implement the taglib. Obviously, for this to work, you must apply the myapp.xsl stylesheet to the source document:

 <FIles minimal_myapp.xsp>   AxAddProcessor text/xsl /styles/myapp.xsl </Files> 

With this snippet added to your configuration file, a request for the minimal_myapp.xsp document gives the following result:

 <?xml version="1.0"?> <xsp:page xmlns:xsp="http://apache.org/xsp/core/v1" xmlns:myapp="http://localhost/myapp"> <xsp:structure>    <xsp:import>Apache::Cookie</xsp:import> </xsp:structure> <application>   <meta>     <breadcrumb><xsp:logic>     my $path ='/';     <link title="home" href="/"/>                  my @steps = split '/', $r->uri;     for ( my $i = 0; @steps > $i; $i++ ) {         my $step = $steps[$i];         next unless length $step;         $path .= $step;         $path .= '/' unless $i =  = $#steps;            <link>             <xsp:attribute name="title">                 <xsp:expr>$step</xsp:expr>             </xsp:attribute>             <xsp:attribute name="href">                 <xsp:expr>$path</xsp:expr>             </xsp:attribute>         </link>     }   </xsp:logic></breadcrumb>     <env><xsp:logic>     foreach my $key (keys(%ENV)) {         <field>             <name><xsp:expr>$key</xsp:expr></name>             <value><xsp:expr>$ENV{$key}</xsp:expr></value>         </field>     }   </xsp:logic></env>     <user>       <xsp:logic>     my $cookie = Apache::Cookie->fetch;         if ( defined( $cookie->{'myapp_username'} ))  {         my $username = $cookie->{'myapp_username'}->value;         <logged-in>true</logged-in>         <username><xsp:expr>$username</xsp:expr></username>     }     else {         <logged-in>false</logged-in>     } </xsp:logic>     </user>   </meta> </application> </xsp:page> 

As expected, the elements from the MyApp grammar are replaced and all other content is copied through verbatim. You only need to configure AxKit to apply the XSP processor to this intermediate step to get the final result:

 <Files minimal_myapp.xsp>   # Preprocess using the XSLT logicsheet   AxAddProcessor text/xsl /styles/myapp.xsl      # And send the result to the XSP processor   AxAddProcessor application/x-xsp NULL </Files> 

This simple two-step processing chain delivers the final, desired result:

 <?xml version="1.0" encoding="UTF-8"?> <application>     <meta>         <breadcrumb>           <link title="home" href="/"/>           <link title="axkitbook" href="/axkitbook/"/>           <link title="samples" href="/axkitbook/samples/"/>           <link title="chapt07" href="/axkitbook/samples/chapt07/"/>           <link title="logicsheet.xml" href="/axkitbook/samples/chapt07/minimal_myapp"/>         </breadcrumb>         <env>           <field>            <name>PATH_INFO</name>            <value/>          </field>          <field>            <name>PATH</name>            <value>/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/kip/bin</value>         </field>         <field>            <name>GATEWAY_INTERFACE</name>            <value>CGI-Perl/1.1</value>         </field>        <field>           <name>MOD_PERL</name>           <value>mod_perl/1.29</value>        </field>     </env>     <user>       <logged-in>true</logged-in>       <username>ubu</username>     </user>   </meta> </application> 

If this were a real-world XSP page, you would certainly include more that just the bits of metadata that you have here. Also, you would probably have a content or state element that contains the data associated with the current state of whatever application you were implementing, but this example shows enough of the basics to get you up and running.

7.1.2.3 Writing module-based taglibs using TaglibHelper

Logicsheet-based taglibs such as the one you just created can be handy, but in practice, most XSP tag libraries are implemented as Perl modules that are then registered with the XSP processor. There are several benefits to using module-based taglibs rather than logicsheets. First, module-based taglibs do not require the XSP source to be preprocessed before execution; this not only saves the overhead associated with adding an additional transformation, but it often greatly simplifies the style processor configuration needed to serve the XSP pages throughout a given site. Also, module-based taglibs offer additional performance, since the Perl code that implements the taglib functions will be cached in memory, so the XSP processor is able to dispatch the handling of the taglib elements to the external module as quickly as it processes the elements in the core XSP grammar.

While it is possible to write taglib modules that interact directly with the low-level components of AxKit's XSP engine, this approach is repetitive and error-prone , and requires intimate knowledge of the XSP processor's internals. It is usually better to use one of the helper modules available to streamline and simplify the process of writing custom tag libraries. In Example 7-3, the MyApp grammar from the logicsheet sample is reimplemented as a Perl module using Steve Willer's Apache::AxKit::XSP::TaglibHelper module that ships with the core AxKit distribution.

Example 7-3. MyApp.pm
 package TaglibHelper::MyApp;          use strict; use Apache::AxKit::Language::XSP::TaglibHelper; use Apache::Cookie;      use vars qw( @ISA $NS @EXPORT_TAGLIB ); @ISA = qw( Apache::AxKit::Language::XSP::TaglibHelper ); $NS = 'http://localhost/xsp/myapp';      @EXPORT_TAGLIB = (     'logincheck( )',     'env( ):listtag=env:itemtag=field',     'breadcrumb( ):as_xml=1', ); sub logincheck {     my $cookie = Apache::Cookie->fetch;     my $out = {  };          if ( defined( $cookie->{'username'} ))  {         my $username = $cookie->{'username'}->value;         $out->{'logged-in'} = 'true';         $out->{username} = $username;     }     else {         $out->{'logged-in'} = 'false';     }     return $out; } sub env {     my @out = ( );     foreach ( keys( %ENV ) ) {          push @out, { name => $_, value => $ENV{$_}};     }     return \@out; } sub breadcrumb {     my $out = '<breadcrumb>';     $out .=   '<link title="home" href="/"/>';              my $path ='/';     my $r = AxKit::Apache->request( );          my @steps = split '/', $r->uri;     for ( my $i = 0; @steps > $i; $i++ ) {         my $step = $steps[$i];         next unless length $step;         $out .= qq<link title="$step" href="$path"/>;     }           $out .= '</breadcrumb>';     return $out; } 1; 

There are several things to note here. First, the module is a subclass of the TaglibHelper class itself. This allows taglib module authors to implement only the functions required to react to the tags in that specific taglib while hiding the tedious low-level processing. Next, TaglibHelper works by allowing subclass authors to write simple Perl subroutines whose names match the local name of the elements in the taglib's grammar. Each time a given element from the taglib being implemented is encountered by the XSP processor, the matching subroutine is called.

The arguments passed to the module's subroutines and the way the values returned from those subroutines will be processed are determined by the function specifications passed to the @EXPORT_TAGLIB array. Each element in this array takes the form of a string that follows the pattern illustrated here:

 tagname([argument specification])[: additional options ] 

While tagname is the local name (unprefixed) of the taglib element to match, argument specifications is an optional comma-separated list of any child elements of the taglib element that should be passed as arguments to the taglib subroutine, and additional options provide additional information about how the data returned from the taglib subroutine is processed and included in the result of the XSP process. Look back at the @EXPORT_TAGLIB array for your MyApp taglib module:

 @EXPORT_TAGLIB = (     'logincheck( )',     'env( ):listtag=env:itemtag=field',     'breadcrumb( ):as_xml=1', ); 

This tells the TaglibHelper parent class to look for three elements from the taglib's namespace, logincheck , env , and breadcrumb . It further indicates that the logincheck subroutine expects no arguments (note the empty argument specification) and that the subroutine returns a simple Perl data structure that should be serialized to XML. The specification for the env subroutine shows that it too expects no arguments, that it will return a list reference whose entries should be named field ( itemtag=field ), and that the entire list should be wrapped in an env element ( listtag=env ). Finally, the specification for the breadcrumb indicates that it also expects no arguments and that the result returned is a well-balanced chunk of XML that should be parsed and included in the final result ( as_xml=1 ).

With this module installed, you only need to register it by name via the AxAddXSPTaglib directive. You can begin using the tags from the MyApp grammar in your XSP pages without having to preprocess them using the logicsheet you created earlier.

 # Load and register the taglib module AxAddXSPTaglib TaglibHelper::MyApp <Files minimal_myapp.xsp>   # No need to preprocess, just send the source directly to the XSP processor   AxAddProcessor application/x-xsp NULL </Files> 

The result returned from a request to minimal_myapp.xsp using your new taglib module is exactly the same as the result returned for the previous logicsheet taglib, so I will not duplicate the output here.

In the same way that Perl's many extension modules increase the power and value of the language as a whole by providing out-of-the-box solutions for common tasks , the judicious use of XSP tag libraries can add significant value to your XSP applications by reducing common application features to a handful of editor-friendly XML elements. If you are serious about building XSP applications with AxKit, I strongly suggest spending the time to learn the ins and outs of the TaglibHelper class or one of the other similar classes (SimpleTaglib or ObjectTaglib) that seek to streamline the process of writing custom XSP tag librariesthe time spent will more than pay for itself in the long run.

7.1.3 XSP Debugging Tips

When you are first starting out with XSP, it can be hard to track down precisely where something went wrong with your page or taglib code. The reason is that, to make XSP pages fast, flexible, and cache-friendly , AxKit's XSP engine is quite complex. (It's actually a SAX Handler that dispatches events to the core event handlers and various taglib modules while dynamically constructing an intermediate Perl class that uses the DOM interface to generate the results!) A lot of advanced Perl magic happens behind the scenes. There are, however, several AxKit configuration directives that you can use to make debugging your XSP applications more straightforward.

First, be sure to set the AxDebugLevel directive to maximum (10). This dumps copious amounts of information about what AxKit is doing behind the scenes to your web host's error log. Next, be sure that the AxStackTrace directive is set to On . This causes the AxKit error handling mechanism to produce a deep stack trace in the case of a fatal exception.

Also, be sure to set up the AxErrorStylesheet directive properly. As with the style processing directives, this option accepts the MIME type associated with one of the Language processing modules and the path to a stylesheet that will be applied to the XML error document that AxKit produces if this directive is present. For example, the following configures AxKit to apply the /styles/axkit_error.xps stylesheet to the generated XML stack trace in the case of a fatal processing error:

 AxErrorStylesheet application/x-xpathscript /styles/axkit_error.xps 

This sends the transformed version of the Perl error and stack trace to the requesting client instead of the disappointing default 500 Internal Server Error that Apache usually offers. This saves you development time by obviating the need to look at the error log every time something blows up. See the entry about the AxErrorStylesheet directive in Appendix A for details about the XML that AxKit produces in these cases.

Finally, AxKit offers the helpful AxTraceIntermediate directive explicitly to help debug XSP pages and complex processing chains. This directive takes the path to a directory as its sole argument. Then, when a document is requested, AxKit generates a series of files in that directory (with the file extensions 0-n)one for each transformation in the processing chain. If the chain includes an XSP process, a special .XSP file is generated that shows the intermediate Perl class that AxKit generates to implement the XSP page. This invaluable tool can save hours of groping for subtle bugs in your XSP code. To ensure that the results are easy to read, combine this directive with the AxDebugTidy On directive to format the intermediate files for better readability.

A word of caution, thoughwith the exception of the AxErrorStylesheet directive (which is good to use in any case), each directive listed here has a negative impact on AxKit's performance and should, therefore, only be used in a development environment, not on a production server.



XML Publishing with AxKit
XML Publishing with Axkit
ISBN: 0596002165
EAN: 2147483647
Year: 2003
Pages: 109
Authors: Kip Hampton

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net