8.5 Custom ConfigReaders


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 bestall 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 

8.5.1 ConfigReader API

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 definitionsevery 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. get_config( )

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; } StyleMap( )

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; } CacheDir( )

This method defines the directory that AxKit uses to store the cache files for the current request. ContentProviderClass( )

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';     } } StyleProviderClass( )

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. DependencyChecks( )

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 or undef from this method. PreferredStyle( )

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; } PreferredMedia( )

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 itjust 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; } 

If all you need to do is pragmatically control the preferred style or media name for the current request, consider using a custom plug-in instead of a ConfigReader. CacheModule( )

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. DebugLevel( )

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( ); } LogDeclines( )

A defined return value from the LogDeclines configures AxKit to provide additional information in cases in which a Declined exception has been thrown. HandleDirs( )

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. IgnoreStylePI( )

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. AllowOutputCharset( )

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}; } OutputCharset( )

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. ErrorStyles( )

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 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. GzipOutput( )

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. DoGzip( )

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 or undef in spite of any value GzipOutput returns. GetMatchingProcessors( )

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 for more details about the structure and properties of the style definitions that this method is expected to return. XSPTaglibs( )

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. OutputTransformers( )

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. Plugins( )

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 classthe 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; 

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