XML Publishing with Axkit

     

Filter chains are made available by adding the Apache::Filter extension to mod_perl via a PerlModule configuration directive to your httpd.conf file. Then, to set the filter chain for a specific resource, you set mod_perl as the main Apache handler and pass a whitespace-separated list of Perl classes to the PerlHandler directive. That done, the output of the first Perl content handler is sent as the input to the next , and so on (similar to AxKit's own style transformation chains):

PerlModule Apache::Filter <Location /path/to/my/app> SetHandler perl-script PerlSetVar Filter On PerlHandler Filter1 Filter2 Filter3 </Location>

AxKit's standard distribution includes a simple, but powerful, Provider class, Apache::AxKit::Provider::Filter, that uses the Apache::Filter interface to capture the result of one or more mod_perl content handlers for further processing inside AxKit. This means that any Apache::Filter-aware handler can be used to provide XML content to AxKit.

AxKit requires that data passed in through the ContentProvider interface be a well- formed XML document. That does not mean that the source of that content must be XML. As long as the returned result is well-formed XML, you are free to use whatever means necessary to generate it. AxKit does not care how the XML gets there, only that it does.

9.2.1 CGI

First, let's examine how you can use a basic Perl CGI script to generate XML that is transformed and delivered by AxKit. The script itself is quite straightforward: it uses Perl's built-in print( ) function to generate a simple XML document with a top-level root element that contains a series of field elements. Each field element contains a name attribute that holds the name of one of the server's environment variables as well as text that contains the value of that variable:

#!/usr/bin/perl use strict; print qq*<?xml version="1.0"?> <root> *; foreach my $field ( keys(%ENV) ) { print qq*<field name="$field">$ENV{$field}</field>*; } print qq*</root> *; 1;

Running this script as a CGI inside Apache produces a document like the following:

<?xml version="1.0"?> <root> <field name="SCRIPT_NAME">/axkitbook/samples/chapt09/env.cgi</field> <field name="HTTP_ACCEPT_ENCODING">gzip,deflate</field> <field name="HTTP_CONNECTION">keep-alive</field> <field name="REQUEST_METHOD">GET</field> <field name="SCRIPT_FILENAME">/www/site/axkitbook/samples/chapt09/env.cgi</field> <field name="HTTP_ACCEPT_CHARSET">ISO-8859-1,utf-8;q=0.7,*;q=0.7</field> <field name="QUERY_STRING"></field> <field name="REQUEST_URI">/axkitbook/samples/chapt09/env.cgi</field> <field name="GATEWAY_INTERFACE">CGI-Perl/1.1</field> <!-- and so on --> </root>

For this experiment to fly, you need to transform the generated XML into a form easily viewable in all web browsers. Applying the following XSLT stylesheet transforms the generated XML into an HTML document containing a two-column table of key/value pairs. For added visual clarity, the background of every second table row will be light silver.

<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <html> <head><title>Server Environement</title></head> <body> <xsl:apply-templates/> </body> </html> </xsl:template> <xsl:template match="root"> <table> <tr><th>Key</th><th>Value</th></tr> <xsl:apply-templates/> </table> </xsl:template> <xsl:template match="field"> <tr> <!-- alternate row colors --> <xsl:if test="position( ) mod 2 != 1"> <xsl:attribute name="bgcolor">eeeeee</xsl:attribute> </xsl:if> <td><xsl:value-of select="@name"/></td> <td><xsl:value-of select="."/></td> </tr> </xsl:template> </xsl:stylesheet>

You need to place the XML generated from the env.cgi script into AxKit so that the XSLT stylesheet can be applied. To do this, add a few key directives to the host's httpd.conf or a local .htaccess file. The following shows a sample snippet with a <Files> block containing the necessary directives:

<Files env.cgi> Options ExecCGI SetHandler perl-script PerlSetVar Filter On PerlHandler Apache::RegistryFilter AxKit AxContentProvider Apache::AxKit::Provider::Filter AxAddProcessor text/xsl styles/env.xsl </Files>

First, set mod_perl as the main Apache handler for all requests by passing the value perl_script to the SetHandler directive. Next, enable content filtering by using the PerlSetVar directive to set the Perl variable Filter to On and by passing the two Perl handlers that you want to run to the PerlHandler directivein this case, Apache::RegistryFilter and AxKit. Apache::RegistryFilter is simply a version of the Apache::Registry handler that allows most Perl CGI scripts to run unaltered inside the stricter mod_perl environment and that takes advantage of the Apache::Filter interface. Then, set Apache::AxKit::Provider::Filter as AxKit's ContentProvider, so the data returned from the CGI script will be passed into AxKit via the Apache::Filter. Finally, configure AxKit to apply the env.xsl XSLT stylesheet to that generated content by passing the appropriate MIME type and file path to the AxAddProcessor directive.

You can use this same basic configuration pattern to plug AxKit into any Perl handler that implements the appropriate Apache::Filter hooks. Once the Apache::Filter extension is loaded, setting Apache::AxKit::Provider::Filter as the ContentProvider for a given resource invisibly passes on the result of the upstream Perl handlers into AxKit; the only thing that often changes list of Perl handlers themselves .

9.2.2 Apache::ASP

Intended as a direct port of Microsoft's Active Server Pages technology, Apache::ASP offers a complete implementation of that model for use in an Apache and mod_perl environment. A predictable, object-oriented API provides easy access to session, application, and server data. Although Apache::ASP offers its own facilities for transforming generated content with XSLT, it assumes that you either need just one predetermined stylesheet per resource or that you will build conditional transformations by calling out to an XSLT processor directly from within the page's code. In any case, Apache::ASP's basic goals differ from AxKit's and therefore lack the extreme flexibility in chaining and choosing styles at runtime. Fortunately, the existence of Apache::Filter and AxKit's Filter Provider class means that developers can take advantage of the strengths of both environments. Here is a tiny page that reimplements the CGI script from the previous example as an ASP page:

<?xml version="1.0"?> <root> <% my $env = $Request->ServerVariables; foreach my $field ( keys( %$env) ) { $Response->Write ( qq*<field name="$field">$env->{$field}</field>* ); } %> </root>

The configuration block needed to enable the ASP-generated XML content to be processed by AxKit looks almost identical to the block used for the CGI example. In this case, a list of handlers configures Apache to process the document using Apache::ASP (instead of Apache::RegistryFilter), before passing the result to AxKit:

<Files env.asp> SetHandler perl-script PerlHandler Apache::ASP AxKit PerlSetVar Filter On AxContentProvider Apache::AxKit::Provider::Filter AxAddProcessor text/xsl styles/env.xsl </Files>

9.2.3 HTML::Mason

Another popular mod_perl web-application framework is HTML::Mason. As with ASP and XPathScript, Mason combines literal markup and inline Perl code, set off by special delimiters, to construct a dynamic result. Syntactically, Mason differs a bit from the others in that it offers more than one type of delimiter . Lines beginning with % are treated as free-form Perl code for creating conditional blocks. Blocks delimited by <% . . . %> are used for interpolating generated data into the result. Expressions contained by <& . . . &> are treated as paths and arguments passed to Mason components. Components are smaller, reusable bits of code and markup combined by higher-level components to create the final result. Much of Mason's popularity is based on the flexibility and modular extensibility that its component-based approach provides.

The following shows a simple Mason component that returns the list of server environment variables in the form from the previous two examples. By default, Mason looks for components, starting at the current host's DocumentRoot. Create a directory called mason_components inside that directory and save the following as a plain-text file named env :

% while (my ($key,$value) = each(%ENV)) { <field name="<% $key %>"><% $value %></field> % }

You need the document that calls the env component. You obtain this by passing the path to components between a pair of special component delimiters. And, since that component returns only a flat list of elements, you need to wrap its interpolated value in a single top-level element to ensure that the resulting document is well-formed XML:

<?xml version="1.0"?> <root> <& /mason_components/env &> </root>

The configuration block required to hand off your Mason-generated XML to AxKit for further processing is essentially identical to those from the previous two examples: set mod_perl as the Apache handler, toggle on the Apache::Filter extension, define the chain of Perl handlers, and set AxKit's ContentProvider to the Apache::AxKit::Provider::Filter class. Again, the only difference is that you pass the document through HTML::Mason::ApacheHandler instead of Apache::ASP or Apache::RegistryFilter, before pulling it into AxKit.

<Files env.html> SetHandler perl-script PerlHandler HTML::Mason::ApacheHandler AxKit PerlSetVar Filter On AxContentProvider Apache::AxKit::Provider::Filter AxAddProcessor text/xsl styles/env.xsl </Files>

The flexibility and power provided by chaining AxKit together with other larger application frameworks, as you have done here, does not come without cost. Depending on the feature set of the framework in question, your web server processes can grow shockingly large, and you probably will not be able to serve lots of dynamic content for a popular site from a single, repurposed desktop PC. On the other hand, developer time is typically more costly than new hardware. If you or members of your team are creating application content that's been proven to be productive with a tool such as Mason, then there's no reason to abandon it. You can use Mason for what it is good for while taking advantage of the features that AxKit offers. At the very least, the Apache::Filter-based solutions discussed here provide a gentle migration path for those who want to experiment with AxKit but don't want to learn XSP or XPathScript, or how to write custom Provider classes right away.

Категории