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.2.1. PerlHandler
PerlHandler has been replaced with
PerlResponseHandler.
25.1.2.2. PerlSendHeader
PerlSendHeader has been replaced with the
PerlOptions
+/-ParseHeaders directive:
PerlSendHeader On => PerlOptions +ParseHeaders
PerlSendHeader Off => PerlOptions -ParseHeaders
25.1.2.3. PerlSetupEnv
PerlSetupEnv has been replaced with the
PerlOptions
+/-SetupEnv directive:
PerlSetupEnv On => PerlOptions +SetupEnv
PerlSetupEnv Off => PerlOptions -SetupEnv
25.1.2.4. PerlTaintCheck
Taint mode can now be turned on with:
PerlSwitches -T
As with standard Perl, taint mode is disabled by default. Once
enabled, taint mode cannot be turned off.
25.1.2.5. PerlWarn
Warnings now can be enabled globally with:
PerlSwitches -w
25.1.2.6. PerlFreshRestart
PerlFreshRestart is a mod_perl 1.0 legacy option
and doesn't exist in mod_perl 2.0. A full tear-down
and startup of interpreters is done on restart.
If you need to use the same httpd.conf file for
1.0 and 2.0, use:
<IfDefine !MODPERL2>
PerlFreshRestart On
</IfDefine>
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.