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.
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
directive—in 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.
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>
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.
3.23.101.60