By default, AxKit gets all its runtime configuration information from
a set of extensions to Apache’s standard
configuration directives. In fact, whether the task simply required
adding a new XSP taglib or setting up a complex processor-to-resource
style mapping, all example applications that we have examined
throughout this book rely on the fact that the default ConfigReader
is being used. This does not have to be the case. Apart from the top
level PerlModule
directive that loads AxKit in the
first place and an AxConfigreader
directive that
loads the new class responsible for loading the configuration data,
AxKit’s setup can be completely uncoupled from the
Apache configuration system.
Reasons for implementing a custom ConfigReader vary. For
example, the people responsible for setting up style-to-resource
mappings (the part of an AxKit configuration that changes most often)
may have limited or no access to the Apache configuration files.
Here, providing a different way to set up those mappings while
avoiding possible risks associated with doling out write access to
httpd.conf
and .htaccess
files (or, worse, forcing developers to nag the webmaster for every
change) makes everyone’s life a bit easier. Or, you
may decide that an XML-based configuration system such as the
sitemaps in Apache Cocoon 2 fits your needs best—all you need
to do is implement a ConfigReader that can extract and process the
relevant AxKit configuration information from your XML configuration
documents. Custom ConfigReaders are swapped in using the
AxConfigReader
directive:
AxConfigReader My::Custom::ConfigReader
The ConfigReader class implements a small army of methods responsible for providing AxKit with all its runtime configuration data. From the ContentProvider and StyleProvider classes used to get the data for the current request, to the list of plug-ins that will run, to the default style definitions—every aspect of AxKit processing can be controlled from the methods in the ConfigReader. Given this wide scope, unless you intend to separate AxKit’s runtime setup as much as possible from the Apache configuration system, you should consider the wisdom of implementing your custom ConfigReader as a subclass of the default Apache::AxKit::ConfigReader and implementing only those methods required to suit your specific purpose.
Called during object initialization and passed a copy of the current
Apache object as its sole argument, the
get_config
offers the simplest path to creating
a custom Config- Reader class. This method is expected to return a
hash reference containing all or some of the data used to control
AxKit’s configuration. While it is generally better
to explicitly override a given option’s accessors
method, the value returned from the default classes’
implementation can be altered by directly setting the appropriate
underlying data structure in the reference returned from
get_config
:
# Return a simple, mostly hard-coded mapping for certain options sub get_config { my $self = shift; my $r = shift; my %config = ( ContentProvider => 'My::Custom::Provider', CacheDir => '/www/mysite/.axkitcache'. DebugLevel => 10, ); return \%config; }
The StyleMap
method associates stylesheet MIME
types with the Language processor used to apply those stylesheets to
the content. It is expected to return a reference to a HASH whose
keys are the MIME types and whose values are the package names of the
Language modules associated with those types:
# a simple, hard-coded mapping sub StyleMap { my %mapping = ( 'text/xsl' => 'Apache::AxKit::Language::LibXSLT', 'application/x-xpathscript' => 'Apache::AxKit::Language:: XPathScript', 'application/x-xsp' => 'Apache::AxKit::Language::XSP', ); return \%mapping; }
The ContentProviderClass
is expected to return
the package name of the module used to fetch the source XML content
for the current request:
sub ContentProviderClass { my $self = shift; # Emulate a FilesMatch block for zip archived content. if ( $self->{apache}->uri =~ /.zip/ ) { return 'Provider::Zip'; } else { return 'apache::AxKit::Provider::File'; } }
Similar to its sister method,
ContentProviderClass
, this method should return
the package name of the module responsible for getting the source for
any stylesheets to be applied to the content.
By default, AxKit builds a list of file dependencies that it
encounters during processing. For example, an XSLT stylesheet that
contains an xsl:import
element can be said to
depend on the file that contains the stylesheet
being imported. To make caching work as expected, the list of
dependencies for each resource is also cached. Then during each
subsequent request, the files in the dependency list are examined for
changes. If a dependency has changed, the cache for the parent
resource is considered invalid, and the sources are reprocessed. This
design provides AxKit with its fine-tuned caching facilities, where
dynamic or changed content is always fresh, but anything that can be
cached is saved from further processing. In most cases, this is
exactly the behavior that you want and expect.
In cases in which file dependencies are known to be stable and
static, however, even greater cache performance can be gained by
configuring AxKit to skip its dependency checks
and serve cached content based solely on the last modification time
of the top-level resources. This behavior is achieved by returning
0
or undef
from this method.
The value returned by the PreferredStyle
method
declares the preferred style name for the current resource. This name
is passed, along with the value returned from the
ConfigReader’s PreferredMedia
method, into the ContentProvider’s
get_styles
method. There it is used to match
against any default configuration-based named styles or any alternate
styles declared via xml-stylesheet
processing
instructions in the content document. See Section 4.3.1 for an overview of how named styles work
and are typically used.
ConfigReader classes implementing this method do well to examine the
preferred_style
entry in the notes table of the
Apache::Request
instance for the current request. Otherwise, the default StyleChooser
and other plug-in modules that use that property do not work as
expected. If no style name applies to the current resource, an empty
string should be returned, rather than simply
undef
.
sub PreferredStyle { my $self = shift; # give the plug-ins preferred_style precedence my $style = $self->{apache}->notes('preferred_style') || $self->{some_application_object}->preferred_style_name( ) || ''; return $style; }
Similar to PreferredStyle
, the
PreferredMedia
method has final say over the
media type name used to map the current resource to the appropriate
styles. As with PreferredStyle
, precedence
should be given to any value stored in the
preferred_media
field in the notes table. That
allows the MediaChooser or other plug-in modules configured for the
current resource to work as expected. On the other hand, if your goal
is to intentionally short-circuit the default behavior of the
MediaChoosers for a given case, this is the place to do it—just
be sure that you know what you are giving up.
sub PreferredMedia { my $self = shift; # Override the plug-in's preferred_media, but fallback to it # if no style is returned by the application object my $media = $self->{some_application_object}->preferred_media_name( ) || $self->{apache}->notes('preferred_media') || # explicitly set the default value here. 'screen'; return $media; }
This method is expected to return a single scalar containing the name of the Perl package used as the cache module for the current content and stylesheet documents.
Expected to return a value between 0 and 10,
DebugLevel
determines the level of detail and
number of errors that will appear in the host’s
error log. The following shows how a subclass of the default
ConfigReader may be used to make developers’ lives a
little easier by allowing the debugging level to be controlled at
request time:
sub DebugLevel { my $self = shift; my %params = $self->{apache}->args( ); # if you get a query string param called 'noisylog' # crank the debugging level to maximum if ( defined( $params{noisylog} ) ) { return 10; } # otherwise, fallback to the default return $self->SUPER::DebugLevel( ); }
A defined return value from the LogDeclines
configures AxKit to provide additional information in cases in which
a Declined
exception has been thrown.
If this method returns a defined value, AxKit will offer up an XML representation of the items in a directory listing (which can then be transformed for delivery) instead of the default Apache directory index.
The IgnoreStylePI
method determines whether or
not the ContentProvider for the current XML source takes any
xml-stylesheet
processing instructions contained
in that document into account when mapping the document to the
current list of styles. If this method returns a nonzero value, the
processing instructions are ignored.
Returning a defined value from this method indicates the intention and ability to send the final transformed content for the current resource in a character encoding other than the one that the final Language processor may have used. It is used by some of the more esoteric Language processors to avoid corrupting the (usually nontextual) data on the way to the client. Therefore, any implementation of this method must allow the value to be set by the classes that call it, rather than simply returned.
sub AllowOutputCharset L my $self = shift; # Use the method to set as well as get if ( @_ ) { $self->{allow_output_charset} = shift; } return self->{allow_output_charset}; }
The OutputCharset
method provides a way to
configure AxKit to send the final transformed content to the client
translated into a specific character encoding. Any value returned by
this method causes AxKit to add a character set OutputTransformer to
the current process. That value will be used to define the character
encoding. Implementations of this method should always examine the
return value from AllowOutputCharset
to ensure
that such translation is possible and allowed for the current
request.
The ErrorStyles
method is expected to return a
list of style definitions used to transform a generated XML backtrace
for the error encountered. If this method returns no style
definitions, the XML backtrace is not generated, and the client
receives the disappointing default 500 Server Error page instead. See
the Section 8.3.1.4 in Section 8.3.1 earlier in this chapter for
the details about the structure of the style definitions that this
method is expected to return.
Returning a defined value from this method indicates the intention to
send gzipped content to the requesting client. The
DoGzip
method determines whether that actually
happens.
Given a defined return value from GzipOutput
,
the DoGzip
method is responsible for examining
the requesting client’s ability to handle gzipped
content. If the client is found incapable, this method should return
0
or undef
in spite of any
value GzipOutput
returns.
Called from the ContentProvider’s
get_styles
method,
GetMatchingProcessors
offers the ability to
alter or define the list of processors applied to the current
resource. Assuming that the default Provider class is used, it is
passed several arguments:
The preferred style name as defined by the
PreferredStyle
method
The preferred media type name as defined by the
PreferredMedia
method
The PUBLIC identifier from the DOCTYPE declaration in the source XML content
The SYSTEM identifier from the DOCTYPE declaration in the source XML
The top-level element name of the source document
A reference to a list of any matching style found while examining the
xml-stylesheet
processing instructions in the
source document
A reference to the ContentProvider instance
If one of the default Providers (or subclasses thereof that do not
implement the get_styles
method) is used, the
list of styles returned from the
GetMatchingProcessors
overrides the styles
extracted from any xml-stylesheet
processing
instructions contained in the source document. The default
ConfigReader gives explicit precedence to these styles by simply
returning the list of styles passed in from the Provider if that list
is not empty:
sub GetMatchingProcessors { my $self = shift; my ($media_name, $style_name, $dtd_public, $dtd_system, $root_name, $styles_list, $provider) = @_; # give preference to the Provider is it found any matching style # in the processing instructions return @$styles_list if @$styles_list; . . . }
See the Section 8.3.1.4 for more details about the structure and properties of the style definitions that this method is expected to return.
Used by the XSP Language module to load the desired tag libraries for
the current scope, the XSPTaglibs
method is
expected to return a list of the Perl package names that implement
those libraries. If the XSP processor is invoked and the modules
returned for this method are not yet loaded into memory, each is
registered and cached into memory on the first request.
The OutputTransformers
method is expected to
return a list of Perl package names that define the OutputTransformer
classes that will be applied to the content on its way to the client.
Each class is called in turn, in the order returned from this method.
The Plugins
method is expected to return a list
of the Perl package names for the plug-in modules to run for the
current resource. These packages are loaded and called in the order
returned from this method. For example, if you are mostly happy with
using the default Apache directive-based configuration but have a
plug-in that you want to be run for every request
after any other plug-ins set up via the
configuration directive, you may subclass the default ConfigReader
and do the following:
sub Plugins { my $self = shift; # Get the list from the default implementation my @plugins = $self->SUPER::Plugins( ); # And append your global plug-in to the end push @plugins, 'Plugin::MyGlobalPlugin'; return @plugins; }
As an example ConfigReader,
let’s create a subclass of the default that allows
each method to be used to set each property
explicitly. (See Example 8-4.) The goal is to
provide access to the ConfigReader interface from plug-ins and other
classes that may want to override certain aspects of the current
configuration for a special case, while falling back to the default
ConfigReader where a given property is not expressly set in the local
instance. Note the addition of a push_style
method that allows style definitions to be added to the local
instance’s list of styles and the localized
GetMatchingProcessors
method that returns those
styles.
Example 8-4. ConfigReader::OpenAPI
package ConfigReader::OpenAPI; use strict; use Devel::Symdump; use Apache::AxKit::ConfigReader; use vars qw( @method_list @ISA ); @ISA = qw( Apache::AxKit::ConfigReader ); BEGIN { @method_list = Devel::Symdump->functions( 'Apache::AxKit::ConfigReader' ); foreach my $method_name (@method_list) { my $full_mname = $method_name; $method_name =~ s/.+:://; next if grep { $method_name eq $_ } qw/ new GetMatchingProcessors dirname basename get_config /; my $full_name = _ _PACKAGE_ _ . "::$method_name"; # Avert your eyes if hacking the symbol table scares you { no strict 'refs'; *{$full_name} = sub { my $self = shift; if ( @_ ) { $self->{$method_name} = shift; } unless ( defined( $self->{$method_name} ) ) { return &$full_mname($self); } if ( wantarray and ref( $self->{$method_name} ) eq 'ARRAY' ) { return @{$self->{$method_name}}; } else { return $self->{$method_name}; } }; } } } sub GetMatchingProcessors { my $self = shift; my ( $medianame, $stylename, $dtd_public, $dtd_system, $root, $styles, $provider ) = @_; return @$styles if @$styles; if ( defined( $self->{style_defs} )) { return @$self->{style_defs}; } else { return $self->SUPER::GetMatchingProcessors($medianame, $stylename, $dtd_public, $dtd_system, $root, $styles, $provider); } } sub push_style { my $self = shift; $self->{style_defs} ||= [ ]; push @{$self->{style_defs}}, @_; } 1;
After this module is installed and configured, each AxKit runtime option can be set directly from within any other component class—the most likely candidate being a plug-in:
# Add your ConfigReader AxConfigReader ConfigReader::OpenAPI # Now, add a plug-in to take advantage of the open interface: AxAddPlugin Plugin::OpenAPI
Here’s a sample plug-in that uses the more open configuration interface:
package Plugin::OpenAPI; use strict; use AxKit; use Apache::Constants qw(OK); use vars qw( $config ); sub handler { my $r = shift; local $config = $AxKit::Cfg; # $config now contains a reference to the # OpenAPI ConfigReader that you can use # set the various runtime options. $config->AllowOutputCharset(1); $config->OutputCharset( 'Shift_JIS' ); # etc.. return OK; } 1;
3.145.188.160