NAME

Apache2::Controller - framework for Apache2 handler apps

VERSION

Version 1.000.010 - FIRST RELEASE

SYNOPSIS

For Apache2 config file setup see Apache2::Controller::Dispatch, which sets a PerlResponseHandler of Apache::Controller, which then instantiates your controller object and calls the chosen method for the uri.

package MyApp::C::Foo;
use strict;
use warnings FATAL => 'all';

# base Apache2::Request is optional, it would limit your
# choice of method names.  request object is in $self->{r}
# if you do not choose to use it to get access to the 
# Apache2 methods via $self.

use base qw( 
   Apache2::Controller 
   Apache2::Request
);

# important to have Apache2::Controller FIRST in bases

use Apache2::Const -compile => qw( :http );
sub allowed_methods {qw( default bar baz )}

# suppose '/foo' is the uri path dispatched to this controller
# and your dispatch uses L<Apache2::Controller::Dispatch::Simple>

# http://myapp.xyz/foo/
sub default {
    my ($self) = @_;
    $self->content_type('text/plain');
    $self->print("Hello, world!\n");
    return Apache2::Const::HTTP_OK;
}

# http://myapp.xyz/foo/bar/biz/schnozz
sub bar {
    my ($self, @path_args) = @_;             
    # @path_args is:
    #      qw( biz schnozz )
    #      @{ $self->{path_args} }
    #      @{ $self->pnotes->{path_args} }

    $self->content_type('text/html');
    $self->print(q{ <p>"WE ARE ALL KOSH"</p> });
    return Apache2::Const::HTTP_OK;
}

# http://myapp.xyz/foo/baz
sub baz {
    my ($self) = @_;

    return Apache2::Const::HTTP_BAD_REQUEST 
       if $self->param('goo');         # inherits Apache2::Request

    return Apache2::Const::HTTP_FORBIDDEN 
       if $self->param('boz') ne 'noz';

    $self->content_type('text/plain'); # inherits Apache2::RequestRec
    $self->sendfile('/etc/passwd');    # inherits Apache2::RequestIO

    return Apache2::Const::HTTP_OK;
}

1;

You could implement a pretty nice REST interface, or any other kind of HTTP-based API, by returning the appropriate HTTP status codes. See "status" in Apache2::Controller::Refcard for a list.

See Apache2::Controller::Render::Template for an additional base for your controller class to render HTML with Template Toolkit.

DESCRIPTION

Apache2::Controller is a lightweight controller framework for object-oriented applications designed to run only under mod_perl children in high-performance Apache2 handler modules. It features URL dispatch with flexible configuration, auth plugins, a cookie tracker for Apache::Session, liberty for any storage models that work under mod_perl, rendering using Template Toolkit or direct printing with Apache, and base inheritance configuration allowing you to construct your applications as you need, without trying to be all things to all people or assimilate the world. It is intended as a framework for new applications specialized as Apache2 handlers, not as a means to absorb existing applications or to create portable code.

Apache2::Controller instantiates the Apache2::Request object and puts it in $self->{r}. If you want access to the methods directly via $self, simply use Apache2::Request as a base and it will auto-delegate all the methods. See "SUBCLASSING Apache2::Request" in Apache2::Request.

Apache2::Controller::Upload early in your use base list, which will add the methods from Apache2::Upload when the Apache2::Request object gets created. Apache2::Controller::Uploads is a second base module for controller modules to inherit from to allow file uploads and provide various handy file conversion routines, if you have the appropriate binaries installed.

For using other Apache2::Controller extension methods, use another base class like Apache2::Controller::Render::Template, provides an easy way to use Template Toolkit by default to render pages, selecting templates from a directory structure that corresponds to your controller URI's.

Individual controller methods can specify plain text or other content types and print directly through inherited Apache2::RequestIO methods.

Instead of abstracting Rube Goldberg devices around the Apache2 mod_perl methods, it stays out of your way and lets you use any and all of them directly through $self as you see fit, if you use Apache2::Request as a second base. But you don't have to do that, if you don't want potential namespace conflicts with your uri's. For example, if you do use Apache2::Request as a base, you couldn't have a uri 'params' or 'connection', for example, if you want to be able to use those Apache2 family methods.

Use Apache2::Controller::Dispatch from your Apache2 config file to send various URI requests to your page view modules. See the CONFIGURATION section below. This features a standard mechanism for uri dispatch in Apache2::Controller::Dispatch::Simple that does not try to figure out what modules are available, but simply requires you to provide a hash that maps from uri paths to controller modules. Or, dispatch plugins can be created to implement the dispatcher's find_controller() method in some other way, like with a TRIE for big sites or using other algorithms.

Apache2::Controller is the base module for each controller module. Your controller modules then contain a list of the method names which are allowed as uri paths under the controller. Instead of implementing a complex scheme of subroutine attributes, you maintain a list, which also acts as your documentation in one place within the controller. This frees you to structure your controller module as you want to, with whatever other methods you choose to put in there.

DISPATCH OF URI TO CONTROLLER

You do not put Apache2::Controller or your subclass into the Apache2 server configuration. Instead you make a subclass of Apache2::Controller::Dispatch and use that as a PerlInitHandler. It will map a URI to an appropriate Apache2::Controller subclass object and method and will use <$r-push_handlers()>> if successful to push Apache2::Controller onto the modperl handler stack.

See Apache2::Controller::Dispatch for more information and different types of URI dispatching. Some simple types are bundled, some of which depend on the allowed_methods() subroutine and some which do not. You can also implement your own dispatch subclass which does things your way, moves allowed uris around depending on context in the request, or whatever.

OTHER REQUEST PHASE HANDLERS

Configure other handlers in your config file to set things up before your Apache2::Controller runs.

Most of these handlers use Apache2::Controller::NonResponseBase as a base for the object, which usually does not need to instantiate the Apache2::Request object, because they usually run before the response phase, so you usually don't want to parse and cache the body if you want to use input filters. If your subclass methods of non-response Apache2::Controller components need access to the Apache2::RequestRec object <$r>, it is always in <$self-{r}>>.

Some other request phase handlers register later-stage handlers, for example to save the session or rollback the database transaction with PerlLogHandler's after the connection output is complete.

The controller handler always returns your set HTTP status code or OK (0) to Apache. In general you return the status code that you want to set, or return OK. I don't know what happens if you return DONE or DECLINED. See Apache2::Const and Apache2::Controller::Refcard.

Add handlers in your config file with your own modules which use base to inherit from these classes as you need them:

PerlHeaderParserHandler Apache2::Controller::Session

<$r-pnotes->{session}>> fed from and stored to an Apache::Session tied hash. Pushes a PerlLogHandler to save the session after the main controller returns OK.

See Apache2::Controller::Session and Apache2::Controller::Session::Cookie.

PerlAuthenHandler Apache2::Controller::Authen::OpenID

Implements OpenID logins and redirects to your specified login controller by changing the dispatch selection on the fly.

See Apache2::Controller::Authen::OpenID.

As for Access and Authz phases of AAA, you should probably roll your own. This framework isn't going to dictate the means of your data storage or how you organize your users. See the mod_perl manual.

Apache2::Controller RESPONSE PHASE HANDLER

Apache2::Controller is set as the PerlResponseHandler if the dispatch class finds a valid module and method for the request. Your controller uses

SUBCLASS OF Apache2::Request

Most of the time you will want to use Apache2::Request as a second base. If you do this, then your controller inherits the Apache2::RequestRec methods with (some) modperl2 request extension libraries loaded during construction. So you can call <$self-$methodname>> for any of the methods associated with Apache2::Request, Apache2::RequestRec and some of their friends. Watch the log for warnings about redefined subroutines, or <use warnings FATAL = 'all'>> to keep yourself on the right track.

To use a simplified example:

package MyApp::C::SomeURIController;
use base qw( 
    Apache2::Controller 
    Apache2::Request 
);

sub set_shipping_address {
    my ($self) = @_;

    # $self->param() is Apache::Request param():
    my ($shipto, $addr, $zip) 
        = map {
            my $detaint = "detaint_$_";
            $self->$detaint( $self->param($_) )
                || return Apache2::Const::SERVER_ERROR;
        } qw( shipto addr zip );
    $self->content_type('text/plain');
    $self->print('Your package is on its way.');
    return Apache2::Const::HTTP_OK
}

sub detaint_shipto {   # ...
sub detaint_addr   {   # ...
sub detaint_zip    {   # ...

At any rate, your Apache2::Controller child object normally subclasses itself into Apache2::Request which magically delegates all those methods to the internal hash value $self->{r}, which is the actual Apache2::Request object. See Apache2::Request about those gory details.

Whether you call $self->$apache2_request_method or $self->{r}->$apache2_request_method matters not, you still ask the same object, so you might as well use $self->... to make it look clean.

But, if you don't want to pollute your namespace of potential URI handlers with the Apache2::* family method namespace, don't use Apache2::Request as a base. $self->{r} is still an Apache2::Request object.

The rest of this manual assumes that you do use Apache2::Request as a base in your controller, and refers to its methods via $self. Just keep in mind that you don't have to, but can access those methods via $self->{r}.

RETURN VALUES

Your controller methods should use eval { } if necessary and act accordingly, set the right things for Apache2::RequestRec and return the right HTTP constant. See ":http" in Apache2::Const and Apache2::Controller::Refcard.

In the event of an error, if you wish, use Apache2::Controller::X and throw one with field 'status' set to a valid HTTP return code. This lets you implement nice error templates if your controller uses Apache2::Controller::Render::Template as a base. See ERRORS below.

Success in the controller method normally should just return the appropriate HTTP status code. You can return HTTP_OK (200) if that is what you mean, or it is the default status if you return OK (0).

Or, if you do $self->status( Apache2::Const::HTTP_SOMETHING ) and then just return, Apache2::Controller will not override the set status.

See Apache2::Controller::Refcard for a list of HTTP return constants and corresponding numbers and messages.

REDIRECTS, ETC.

For external redirects, set $self->err_headers_out->add(Location => 'http://foo.bar') and return $self->status(Apache2::Const::REDIRECT).

Keep in mind that if you use other request phase processors that push a PerlLogHandler like Apache2::Controller::DBI::Connector or Apache2::Controller::Session, those will still run, but normally will not save changes or commit transactions if you set or return an http status outside of the HTTP_OK range.

You should also not fiddle with the connection by causing Apache2 to close it prematurely, else these handlers may not run synchronously before another request is received that may have depended on their behavior.

ERRORS

If you decide to set an error status code, you can print your own content and return that status code.

If you want to use error templates, barf Apache2::Controller::X objects. These print a stack trace to the error log at the WARN level of Log::Log4perl from this module's namespace. If errors crop up from other A2C request phase handlers, try setting WARN log level for Apache2::Controller::NonResponseBase or Apache2::Controller::NonResponseRequest.

Also see Apache2::Controller::Render::Template.

You can use or subclass Apache2::Controller::X, to use a2cx(), or you can throw your own exception objects, or just die(), or croak(), or set $self->status, headers etc., possibly printing content, or return the appropriate status from your controller method.

See Apache2::Controller::X for help on throwing exceptions with HTTP status, data dumps, etc.

If your code does break, die or throw an exception, this is caught by Apache2::Controller. If your controller module implements an error() method, for instance by use of the base Apache2::Controller::Render::Template which looks in template_dir/errors/ for the appropriately named error template, then $handler-error()> will be called passing the $EVAL_ERROR or exception as the first argument.

package MyApp::C::Foo;
use YAML::Syck;
# ...
sub error {
    my ($self, $X) = @_;
    $self->status( Apache2::Const::HTTP_BAD_REQUEST );
    $self->content_type('text/plain');
    $self->print("Take this job and shove it!\n", "\n", $X, "\n");
    if ($X->isa('Apache2::Controller::X')) {
       # usually you wouldn't show gory details to the user...
       $self->print(Dump($X->dump)) if $X->dump;
       $self->print($X->trace) if $X->trace;  
    }
}

Of course, all exceptions are sent to the error log using Log::Log4perl DEBUG() before the handler completes, and any refusal status greater or equal to 400 (HTTP_BAD_REQUEST) will be written to the access log with Apache2::Log log_reason() using the first few characters of the error.

See "ERRORS" in Apache2::Controller::Session for how to control whether or not a session is saved. Usually it is automatically saved. If you don't want it saved when you have an error in your controller, you'll have to use <eval> and then set <$self-notes->{a2c_connection_aborted}>> before you return the error code.

error() does not have to roll back DBI handles if you use Apache2::Controller::DBI::Connector, as this is rolled back automatically in the PerlLogHandler phase if you don't commit the transaction.

CONTROLLER CLOSURES

Apache2::Controller's package space structure lets you take advantage of closures that access variables in your controller subclass package space, which are cached by modperl in child processes across independent web requests. Be careful with that and use Devel::Size to keep memory usage down. I have no idea how this would work under threaded mpm.

CONTENT TYPE

Your controller should set content type with $self->content_type() to something specific if you need that. Otherwise it will let mod_perl set it to whatever it chooses when you start to print. This is usually text/html.

LOGGING

Apache2::Controller uses Log::Log4perl. See that module for information on how to set up a format file or statement. For example, in a perl startup script called at Apache2 start time, do something like:

use Log::Log4perl; 
    log4perl.rootLogger=DEBUG, LogFile
    log4perl.appender.LogFile=Log::Log4perl::Appender::File
    log4perl.appender.LogFile.filename=/var/log/mysite_error_log
    log4perl.appender.LogFile.layout=PatternLayout
    log4perl.appender.LogFile.layout.ConversionPattern=%M [%L]: %m%n
};
Log::Log4perl->init(\$logconf);

These settings will be cloned to every modperl child on fork.

MVC

Apache2::Controller provides the controller, mainly. Apache2::Controller::Render::Template is one example of a view that can be used as a second base with use base in your controller module. As for the Model part of Model-View-Controller, Apache2::Controller leaves that entirely up to you and does not force you to wrap anything in an abstraction class.

The handler() subroutine is in your base class and your controller modules will be running from memory in the mod_perl child interpreter. So, you can use package namespace effectively to store data that will persist in the mod_perl child across requests.

LOAD BALANCING

A2C does not have to load all site modules for every page handler, which could help with load-balancing highly optimized handlers for specific URI's while having a universal application installer.

Picture if you will, a programming utopia in which all engineers are respected, highly paid and content, and managers make correct decisions to rely on open-source software.

You deploy the same Apache, the same CPAN modules and your whole application package to every server, and attach a url-generating subroutine to the Template stash that puts in a different hostname when the URI is one of your load-intensive functions.

<a href="[% myurl('/easy') %]">easy</a> "http://pony.x.y/easy"

<a href="[% myurl('/hard') %]">hard</a> "http://packhorse.x.y/hard"

Web designers can be taught to use this function myurl(), but system admins maintain the map that it loads to figure out what servers to use.

Then the Apache2 config files on those packhorse servers would pre-load only the subclassed controllers that you needed, and redirect all other uri requests to the pony servers.

FUNCTIONS

a2c_new

$handler = MyApp::C::ControllerSubclass->a2c_new( Apache2::RequestRec object )

This is called by handler() to create the Apache2::Controller object via the module chosen by your Apache2::Controller::Dispatch subclass.

We use a2c_new instead of the conventional new because, in case you want to suck in the Apache2::Request methods with that module's automagic, then you don't get confused about how <SUPER::> behaves. Otherwise you get into a mess of keeping track of the order of bases so you don't call Apache2::Request->new() by accident, which breaks everything.

subclassing a2c_new()

To set params for the Apache2::Request object, you have to subclass a2c_new().

package MyApp::ControllerBase;
use base qw( Apache2::Controller Apache2::Request );

sub a2c_new {
    my ($class, $r) = @_;
    return SUPER::new(
        $class, $r,
        POST_MAX => 65_535,
        TEMP_DIR => '/dev/shm',
    );
    # $self is already blessed in the class hierarchy
}

package MyApp::Controller::SomeURI;
use base qw( MyApp::ControllerBase );
sub allowed_methods qw( uri_one uri_two );
sub uri_one { # ...

If you need to do the same stuff every time a request starts, you can override the constructor through a class hierarchy.

package MyApp::ControllerBase;
use base qw( Apache2::Controller Apache2::Request );

sub new {
    my ($class, $r, @apr_override_args) = @_;
    my $self = SUPER::new(
        $class, $r,
        POST_MAX => 65_535,
        TEMP_DIR => '/dev/shm',
        @apr_override_args,
    );

    # $self is already blessed in the class hierarchy

    # do request-startup stuff common to all controller modules

    return $self;
}

package MyApp::Controller::SomeURI;
use base qw( MyApp::ControllerBase );
sub allowed_methods qw( uri_one uri_two );
sub new {
    my ($class, $r) = @_;

    my $self = SUPER::a2c_new(
       $class, $r,
    );

    # no need to bless, A2C blesses into the child class

    # do request-startup stuff for this specific controller

    return $self;
}

sub uri_one {
    my ($self) = @_;
    $self->content_type('image/gif');
    # ...
    return Apache2::Const::HTTP_OK;
}
sub uri_two { # ...

Similarly, to do something always at the end of every request, from within the dispatched PerlResponseHandler:

package MyApp::Controller::SomeURI;
use Devel::Size;
use Log::Log4perl qw(:easy);
my $MAX = 40 * 1024 * 1024;
sub DESTROY {
    my ($self) = @_;
    my $size = total_size($self);  # whoo baby!
    INFO("size of $self->{class} is bigger than $MAX!") if $size > $MAX;
    return; # self is destroyed
}

See "USING INHERITANCE" below for more tips.

METHODS

Methods are also extended by other modules in the A2C family. See Apache2::Controller::Methods.

handler

# called from Apache, your subclass pushed to PerlResponseHandler
# by your A2C dispatch handler:
MyApp::Controller::Foo->handler( Apache2::RequestRec object )

The handler is pushed from an Apache2::Controller::Dispatch subclass and via your dispatched subclass of Apache2::Controller. It should not be set in the config file. It looks for the controller module name in $r->notes->{controller} and for the method name in $r->notes->{method}.

Errors are intercepted and if the handler object was created and implements an $handler->error($exception) method then the exception will be passed as the argument.

An HTTP status code of HTTP_BAD_REQUEST or greater will cause log_reason to be called with a truncated error string and the uri for recording in the access log.

USING INHERITANCE

There is no need for a predefined sequence of start-up or clean-up routines that Apache2::Controller would have to check for in your controller module.

Instead, you use inheritance to streamline your code and share common pieces, like in "subclassing a2c_new( )" above.

If your methods need to do cleanup after finishing, for example, they should add a line to call a shared cleanup method.

package MyApp::Cleanup;
sub cleanup {
    my ($self) = @_;
    # ...
}

package MyApp::C::Foo;
use base qw( Apache2::Controller Apache2::Request MyApp::Cleanup );
sub allowed_methods {qw( foo bar )}

sub foo {
    # ...
    $self->cleanup();
    return;
}
# or...
sub bar {
    # ...
    $self->push_handler(PerlCleanupHandler => sub {
        # ...
    });
    return;
}

Or better yet...

package MyApp::Cleanup;
sub DESTROY {
    my ($self) = @_;
    # ...
}

package MyApp::C::Foo;
use base qw( Apache2::Controller MyApp::Cleanup );
sub allowed_methods {qw( foo bar )}

sub foo {
   # ...
   return;
}

sub bar {
   # ...
   return;
}

There is no need for a predefined method sequence that tries to run for each request, because Apache2 already provides a robust abstraction of the request lifecycle with many stages for which you can register handler subroutines. If you can wrap your head around it, inheritance provides many solutions to problems for which elaborate measures are commonly re-invented. For example if you wanted cleanup done the same way every time without having to remember that $self->cleanup() line for each new method, overload the constructor as per "subclassing a2c_new( )" above and register a PerlCleanupHandler for every request instead, or use a base with a DESTROY method.

Otherwise the framework ends up doing a lot of work every time to ask, "did they implement this? did they implement that?" and that gets in your way, or you have to write those routines every time even if they don't do anything, or whatever. Bleah. Implement what you want to implement from the controller methods. The framework won't provide you with any more structure.

EXAMPLES

Browse the source package from CPAN and check out t/lib/* and t/conf/extra.conf.last.in.

RELATED MODULES

Apache2::Controller::Directives

Apache2::Controller::Methods

Apache2::Controller::X

Apache2::Controller::Dispatch

Apache2::Controller::Uploads

Apache2::Controller::Session

Apache2::Controller::DBI::Connector

Apache2::Controller::Auth::OpenID

Apache2::Controller::Refcard

Apache2::Controller::Funk

SEE ALSO

Apache2::RequestRec and friends

Apache2::Request

Apache2::AuthenOpenID

http://perl.apache.org

http://perl.apache.org/docs/2.0/user/handlers/http.html

THANKS

Many thanks to David Ihnen, Adam Prime, André Warnier and all the great people on the modperl mailing list.

Special thanks to Nobuo Danjou for Apache2::AuthenOpenID which edumacated me on how the OpenID authen module should work.

Of course, thanks to the many mod_perl and Apache authors and all the CPAN authors whose modules this depends on. Wow! This stuff is so cool!

AUTHOR

Mark Hedges, hedges +(a t)- scriptdolphin.org

COPYRIGHT AND LICENSE

Copyright 2008 Mark Hedges. CPAN: markle

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

1 POD Error

The following errors were encountered while parsing the POD:

Around line 933:

Non-ASCII character seen before =encoding in 'André'. Assuming UTF-8