Perl Documentation
Writing Plugins
Everything in AxKit2 is controlled via plugins. This helps keep the core of AxKit2 very simple and lightweight, yet makes it very powerful and extensible.
Although AxKit comes with some included plugins to deliver flat files, and some demo plugins to show how to transform XML, the real power of AxKit2 will only be realised when you write your own plugin. Don't be afraid - this is very simple to do.
To make life easy, lets step through a working example.
A Simple XSLT Engine
We'll start with a simple plugin that transforms XML files to HTML using XSLT. This example will cover some of the basics of how the XML transformation system of AxKit2 works.
Setting up the Directories
Start in the AxKit2 root directory (where you can run ./axkit
from). First
we'll create a directory to store your XML and your stylesheets:
$ mkdir myfirstplugin $ mkdir myfirstplugin/webroot $ mkdir myfirstplugin/stylesheets $ mkdir myfirstplugin/plugins
Server Configuration
Now we need to tell AxKit where all our files are and what port to listen on.
Here's a minimal config which you should store in myfirstplugin/axkit.conf
:
# Setup error logging to the maximum level: Plugin logging/warn LogLevel LOGDEBUG # we need to load this to map URLs to filenames Plugin uri_to_file # Setup our server <Server> Port 8080 DocumentRoot myfirstplugin/webroot DirectoryIndex index.xml # Load our plugin PluginDir myfirstplugin/plugins Plugin serve_xml </Server>
Now we can try and start AxKit and see what happens:
$ ./axkit -c myfirstplugin/axkit.conf L7 uri_to_file register_hook: uri_translation => hook_uri_translation could not open myfirstplugin/plugins/serve_xml: No such file or directory at lib/AxKit2/Plugin.pm line 103, <$fh> line 13.
Well we should have expected this -- we haven't written our plugin yet!
Plugin Code
So load up your editor on myfirstplugin/plugins/serve_xml
and enter the
following perl code:
#!/usr/bin/perl -w sub hook_xmlresponse { my ($self, $input) = @_; $self->log(LOGDEBUG, "Serving up a file"); my $stylesheet = './myfirstplugin/stylesheets/default.xsl'; my $out = $input->transform(XSLT($stylesheet)); return OK, $out; }
The key part of this is the $input->transform(...)
method call, which
transforms the input using the given transformation types. Available types
include XSP()
, TAL()
, XPathScript()
and of course XSLT()
.
Once the transformation is done we return OK, $out
to let AxKit know that
the transformation was successful and we should deliver the result to the
browser.
[As a sidenote here, I include the #! perl shebang line to help syntax highlighters know that this is perl code.]
Now if you run AxKit again we shouldn't see any error, but instead some debug output telling us our plugin was loaded:
$ ./axkit -c myfirstplugin/axkit.conf L7 uri_to_file register_hook: uri_translation => hook_uri_translation L7 serve_xml register_hook: xmlresponse => hook_xmlresponse
Now if we point a web browser at this server we will get an error because we
haven't created an index.xml
and default.xsl
yet. So lets do that now.
XML and XSLT Files
myfirstplugin/webroot/index.xml
:
<?xml version="1.0"?> <dromedaries> <species name="Camel"> <humps>1 or 2</humps> <disposition>Cranky</disposition> </species> <species name="Llama"> <humps>1 (sort of)</humps> <disposition>Aloof</disposition> </species> <species name="Alpaca"> <humps>(see Llama)</humps> <disposition>Friendly</disposition> </species> </dromedaries>
myfirstplugin/stylesheets/default.xsl
:
<?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="/"> <html> <head><title>Know Your Dromedaries</title></head> <body> <table bgcolor="eeeeee" border="1"> <tr> <th>Species</th> <th>No of Humps</th> <th>Disposition</th> </tr> <xsl:for-each select="dromedaries"> <xsl:apply-templates select="./species" /> </xsl:for-each> </table> </body> </html> </xsl:template> <xsl:template match="species"> <tr> <td><xsl:value-of select="@name" /></td> <td><xsl:value-of select="./humps" /></td> <td><xsl:value-of select="./disposition" /></td> </tr> </xsl:template> </xsl:stylesheet>
Now when you request the page you should get a HTML table listing the dromedaries in the index.xml file.
Some browsers might also request the non-existant favicon.ico too, which may cause an error to appear in the terminal. Don't worry about that for now.
Congratulations, your first plugin is complete!
Making Your Plugin Configurable
One area where AxKit2 really shines is that each plugin can define its own configuration parameters. Our plugin is a perfect candidate for that because we hard-coded the XSLT stylesheet. Lets make that configurable now.
A Sidetrack - What is a 'Plugin'?
Skip this section if you don't want to know the gory details - you don't need to know about for writing simple plugins.
Knowledgable perl hackers will have noticed that our
hook_xmlresponse
method above received two parameters, the first being$self
, and yet we didn't create a perl class with all the annoyingbless
verbiage. Well yes, a plugin is a perl class, and$self
is an instance of that class. We simply went out of our way to hide all the horrible perl guts from you.If you prefer all the perl guts to be exposed, you can create a full blown package as your plugin instead, install it in the usual perl install locations, and load it with:
Plugin My::PluginAs long as it has '::' in the name, AxKit will recognise it as a perl module and load it that way. Just be sure to 'use base q(AxKit2::Plugin)' so you get all the base class methods.
To add configuration directives we just add sub definitions starting with "conf_":
sub conf_XSLTStyle;
There we added a configuration directive called XSLTStyle
which we'll use
to define our stylesheet. Using this default behaviour, AxKit will store any data
in the configuration file as $self->config('XSLTStyle')
.
Simple huh?
Now we need to tie all that together. First add this to your config after
"Plugin serve_xml
":
XSLT_Style myfirstplugin/stylesheets/default.xsl
And now modify your hook_xmlresponse()
method to get this new value using
$self->config(...)
:
sub hook_xmlresponse { my ($self, $input) = @_; $self->log(LOGDEBUG, "Serving up a file"); my $stylesheet = $self->config('XSLTStyle'); my $out = $input->transform(XSLT($stylesheet)); return OK, $out; }
And we're done! Run (or restart) axkit (./axkit -c myfirstplugin/axkit.conf
)
and ensure that it all works.
Chained Transformations
One of the powerful features of AxKit was always being able to easily chain transformations together, this makes complex XSLT transformations much simpler, among other benefits.
Lets modify our plugin to support multiple XSLT transformations.
The simplest way to do this is to modify our XSLT_Style
directive to support
multiple values, each value being a new XSLT stylesheet. First lets modify our
configuration to detail what we want:
XSLT_Style myfirstplugin/stylesheets/sort.xsl \ myfirstplugin/stylesheets/default.xsl
The idea here being that one XSLT will sort our dromedaries, and the second will turn them into HTML.
Lets create our sort.xsl
file first:
<?xml version="1.0" encoding="ISO-8859-1" ?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:template match="dromedaries"> <dromedaries> <xsl:apply-templates select="species"> <xsl:sort select="@name"/> </xsl:apply-templates> </dromedaries> </xsl:template> <!-- Identity Template --> <xsl:template match="*|@*"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> </xsl:stylesheet>
Note the use of the identity template. If you haven't used XSLT before take note of that - it's a very useful template for debugging.
So now we need to have a list of XSLT stylesheets in our config, and make use
of a list in the ->transform()
method.
The nice thing about AxKit's configuration mechanism is that it copes with multi-valued configuration easily. We have to change our config declaration just a little bit:
sub conf_XSLTStyle : Validate(STRINGLIST);
When we retrieve the values, we just call $self->config(...)
in array context and we get all values instead of just one.
sub hook_xmlresponse { my ($self, $input) = @_; $self->log(LOGDEBUG, "Serving up a file"); my @stylesheets = $self->config('XSLTStyle'); my $out = $input->transform(map XSLT($_), @stylesheets); return OK, $out; }
So instead of running XSLT()
once, we map it over @stylesheets
.
And that's it. Restart AxKit and make sure this returns a table of sorted results.
What Are the APIs?
We've glossed over a lot of what's actually going on under the hood here, so if you want to know more, here are some pointers to extended documentation.
Plugins
To find our more about the Plugin class, which is the $self
object in the
examples above, see AxKit2::Plugin. This document also covers all the
available hooks and what effect the return codes for each of those hooks may
have.
Transformations
In AxKit2 the $input->transform()
method is provided by the Processor
module. See AxKit2::Processor for the API details in there.
Config
So far only seen in our $self->config('XSLTStyle')
method. The config
object gives us access to the axkit.conf file and a storage place for
plugin-specific configuration parameters. See AxKit2::Config for details
on the API available.
The Connection
So far we haven't dealt with the client at all - AxKit did all that work for us.
However many plugins will want to read headers, set outbound headers, and
maybe even write data directly to the client. This is done through the
Connection object which is accessed via $self->client
in a plugin. See
AxKit2::Connection for the available API.
There are three client APIs which I'll detail here as a shortcut as they are the most used (though more documented in the above link).
$client->headers_in
returns the inbound headers.
$client->headers_out
returns the outbound headers.
$client->send_http_headers
writes the outbound headers to the client.
For documentation on the header object that headers_in()
and headers_out()
return, see AxKit2::HTTPHeaders.
Constants
Finally, for a list of constants useful as return values from plugins, see AxKit2::Constants.
Copyright
This document is Copyright 2006 Apache Software Foundation.