Practical mod_perlPractical mod_perlSearch this book

Chapter 25. Programming for mod_perl 2.0

Contents:

Migrating to and Programming with mod_perl 2.0
New Apache Phases and Corresponding Perl*Handlers
I/O Filtering

In this chapter, we discuss how to migrate services from mod_perl 1.0 to 2.0, and how to make the new services based on mod_perl 2.0 backward compatible with mod_perl 1.0 (if possible). We also cover all the new Perl*Handlers in mod_perl 2.0.

25.1. Migrating to and Programming with mod_perl 2.0

In mod_perl 2.0, several configuration directives were renamed or removed. Several APIs also were changed, renamed, removed, or moved to new packages. Certain functions, while staying exactly the same as in mod_perl 1.0, now reside in different packages. Before using them, you need to find and load the new packages.

Since mod_perl 2.0 hasn't yet been released as of this writing, it's possible that certain things will change after the book is published. If something doesn't work as explained here, please refer to the documentation in the mod_perl distribution or the online version at http://perl.apache.org/docs/2.0/ for the updated documentation.

25.1.1. The Shortest Migration Path

mod_perl 2.0 provides two backward-compatibility layers: one for the configuration files and the other for the code. If you are concerned about preserving backward compatibility with mod_perl 1.0, or are just experimenting with mod_perl 2.0 while continuing to run mod_perl 1.0 on your production server, simply enable the code-compatibility layer by adding:

use Apache2;
use Apache::compat;

at the top of your startup file. Backward compatibility of the configuration is enabled by default.

25.1.2. Migrating Configuration Files

To migrate the configuration files to mod_perl 2.0 syntax, you may need to make certain adjustments. Several configuration directives are deprecated in 2.0 but are still available for backward compatibility with mod_perl 1.0. If you don't need backward compatibility, consider using the directives that have replaced them.

25.1.3. Code Porting

mod_perl 2.0 is trying hard to be backward compatible with mod_perl 1.0. However, some things (mostly APIs) have changed. To gain complete compatibility with 1.0 while running under 2.0, you should load the compatibility module as early as possible:

use Apache::compat;

at server startup. Unless there are forgotten things or bugs, your code should work without any changes under the 2.0 series.

However, if you don't have a good reason to keep 1.0 compatibility, you should try to remove the compatibility layer and adjust your code to work under 2.0 without it. This will improve performance. The online mod_perl documentation includes a document (http://perl.apache.org/docs/2.0/user/porting/compat.html) that explains what APIs have changed and what new APIs should be used instead.

If you have mod_perl 1.0 and 2.0 installed on the same system and the two use the same Perl libraries directory (e.g., /usr/lib/perl5), to use mod_perl 2.0 make sure to first load the Apache2 module, which will perform the necessary adjustments to @INC:

use Apache2; # if you have 1.0 and 2.0 installed
use Apache::compat;

So if before loading Apache2.pm the @INC array consisted of:

/usr/lib/perl5/5.8.0/i686-linux-thread-multi
/usr/lib/perl5/5.8.0
/usr/lib/perl5/site_perl/5.8.0/i686-linux-thread-multi
/usr/lib/perl5/site_perl/5.8.0
/usr/lib/perl5/site_perl
.

it will now look like this:

/usr/lib/perl5/site_perl/5.8.0/i686-linux-thread-multi/Apache2
/usr/lib/perl5/5.8.0/i686-linux-thread-multi
/usr/lib/perl5/5.8.0
/usr/lib/perl5/site_perl/5.8.0/i686-linux-thread-multi
/usr/lib/perl5/site_perl/5.8.0
/usr/lib/perl5/site_perl
.

Notice that a new directory was appended to the search path. If, for example, the code attempts to load Apache::Server and there are two versions of this module under /usr/lib/perl5/site_perl/:

5.8.0/i686-linux-thread-multi/Apache/Server.pm
  5.8.0/i686-linux-thread-multi/Apache2/Apache/Server.pm

the mod_perl 2.0 version will be loaded first, because the directory 5.8.0/i686-linux-thread-multi/Apache2 comes before the directory 5.8.0/i686-linux-thread-multi in @INC.

Finally, mod_perl 2.0 has all its methods spread across many modules. To use these methods, you first have to load the modules containing them. The ModPerl::MethodLookup module can be used to figure out what modules need to be loaded. For example, if you try to use:

$r->construct_url( );

and mod_perl complains that it can't find the construct_url( ) method, you can ask ModPerl::MethodLookup:

panic% perl -MApache2 -MModPerl::MethodLookup -e print_method construct_url

This will print:

to use method 'construct_url' add:
        use Apache::URI ( );

Another useful feature provided by ModPerl::MethodLookup is the preload_all_modules( ) function, which preloads all mod_perl 2.0 modules. This is useful when you start to port your mod_perl 1.0 code (though preferrably avoided in the production environment to save memory). You can simply add the following snippet to your startup.pl file:

use ModPerl::MethodLookup;
ModPerl::MethodLookup::preload_all_modules( );

25.1.4. ModPerl::Registry Family

In mod_perl 2.0, Apache::Registry and friends (Apache::PerlRun, Apache::RegistryNG, etc.) have migrated into the ModPerl:: namespace. The new family is based on the idea of Apache::RegistryNG from mod_perl 1.0, where you can customize pretty much all the functionality by providing your own hooks. The functionality of the Apache::Registry, Apache::RegistryBB, and Apache::PerlRun modules hasn't changed from the user's perspective, except for the namespace. All these modules are now derived from the ModPerl::RegistryCooker class. So if you want to change the functionality of any of the existing subclasses, or you want to "cook" your own registry module, it can be done easily. Refer to the ModPerl::RegistryCooker manpage for more information.

Here is a typical registry section configuration in mod_perl 2.0:

Alias /perl/ /home/httpd/perl/
<Location /perl>
    SetHandler perl-script
    PerlResponseHandler ModPerl::Registry
    Options +ExecCGI
    PerlOptions +ParseHeaders
</Location>

As we explained earlier, the ParseHeaders option is needed if the headers are being sent via print( ) (i.e., without using the mod_perl API) and comes as a replacement for the PerlSendHeader option in mod_perl 1.0.

Example 25-1 shows a simple registry script that prints the environment variables.

Example 25-1. print_env.pl

print "Content-type: text/plain\n\n";
for (sort keys %ENV){
    print "$_ => $ENV{$_}\n";
}

Save the file in /home/httpd/perl/print_env.pl and make it executable:

panic% chmod 0700 /home/stas/modperl/mod_perl_rules1.pl

Now issue a request to http://localhost/perl/print_env.pl, and you should see all the environment variables printed out.

One currently outstanding issue with the registry family is the issue with chdir( ). mod_perl 1.0 registry modules always performed cdhir( )s to the directory of the script, so scripts could require modules relative to the directory of the script. Since mod_perl 2.0 may run in a threaded environment, the registry scripts can no longer call chdir( ), because when one thread performs a chdir( ) it affects the whole process—all other threads will see that new directory when calling Cwd::cwd( ), which will wreak havoc. As of this writing, the registry modules can't handle this problem (they simply don't chdir( ) to the script's directory); however, a satisfying solution will be provided by the time mod_perl 2.0 is released.

25.1.5. Method Handlers

In mod_perl 1.0, method handlers had to be specified by using the ($$) prototype:

package Eagle;
@ISA = qw(Bird);

sub handler ($$) {
    my($class, $r) = @_;
    ...;
}

Starting with Perl Version 5.6, you can use subroutine attributes, and that's what mod_perl 2.0 does instead of conventional prototypes:

package Eagle;
@ISA = qw(Bird);

sub handler : method {
    my($class, $r) = @_;
    ...;
}

See the attributes manpage.

mod_perl 2.0 doesn't support the ($$) prototypes, mainly because several callbacks in 2.0 have more arguments than $r, so the ($$) prototype doesn't make sense any more. Therefore, if you want your code to work with both mod_perl generations, you should use the subroutine attributes.

25.1.6. Apache::StatINC Replacement

Apache::StatINC has been replaced by Apache::Reload, which works for both mod_perl generations. To migrate to Apache::Reload, simply replace:

PerlInitHandler Apache::StatINC

with:

PerlInitHandler Apache::Reload

Apache::Reload also provides some extra functionality, covered in the module's manpage.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.