Team LiB   Previous Section   Next Section

5.1 mod_perl

First created by Doug MacEachern in 1996, mod_perl is the main flower of the Apache Perl integration project. It brings the full power of the Perl language into the heart of the Apache HTTP server by linking the Perl runtime library into Apache's modular C language API. This is like being able to turn your Jeep into a jet-powered helicopter for the weekend, and then back again on Monday, at the flick of a switch.[1]

[1] The same trick is repeated, later in this book, in Chapter 8, when we embed Perl into a PL/SQL C library.

Once Apache is up and running with mod_perl, the Perl interpreter engine is up and running too, conveniently preloaded into constant memory. This means there's no restart overhead each time you run a Perl CGI script. The speed improvement brought to any Perl-based web site by the addition of mod_perl, and the scalability implications, are enormous. Important Apache mod_perl links include:

http://perl.apache.org:

Apache Perl integration project home page.

http://www.modperl.com:

Home page of Lincoln Stein and Doug MacEachern's helpful book, Writing Apache Modules with Perl and C (O'Reilly & Associates, 1999).[2]

[2] The official O'Reilly page is http://www.oreilly.com/catalog/wrapmod.

http://www.refcards.com/about/mod_perl.html:

Andrew Ford's online reference cards for mod_perl, also supported by his book, the mod_perl Pocket Reference (O'Reilly & Associates, 2000).[3]

[3] http://www.oreilly.com/catalog/modperlpr

http://theoryx5.uwinnipeg.ca/guide

A good entry point to the University of Winnipeg's excellent pages on mod_perl and CGI scripting. This guide is particularly helpful in explaining the complex issue of porting CGI scripts to Apache::Registry and mod_perl.

http://mathforum.org/epigone/modperl

Ken William's superb mod_perl topics archive.

5.1.1 Installing mod_perl on Unix

Before you can test your mod_perl installation, you must make sure that the Perl LWP.pm module is available. Developed by Gisle Aas, this module provides a "Library for WWW access in Perl"; it consists of a wide range of related Perl modules designed to help simplify Perl Internet client connections. Not only is this module useful for our later mod_perl test, it's invaluable for many Perl Internet requirements. We'll come back to LWP.pm again and again as we discuss Perl and the Web in the next few chapters.

5.1.1.1 LWP-Library for WWW access in Perl

The main focus of LWP is to provide classes and functions allowing the creation of Internet Perl clients. The library also contains modules for more general use, even making it possible to create simple HTTP servers.

Fortunately for us, Gisle Aas has collated all of the related LWP modules into a single download, libwww-perl-5.64.tar.gz (or its latest derivative). However, LWP itself relies upon several other related modules, as detailed in the appropriate installation order, in Table 5-1. If you want to install these by hand, download the latest tarballs and process them in the usual perl Makefile.PL manner. Alternatively, we'll accept a little sneaky automation here. There is a very handy command you can run, which should load everything required for LWP, directly over the Internet, in just one line:

$ perl -MCPAN -e 'install Bundle::LWP'

An even sneakier routine loads the whole of mod_perl and many of its related modules:

$ perl -MCPAN -e 'install Bundle::Apache'

This will load every module you require, including LWP. However, we'll still go through the manual route; this way, we can describe all the bumps in the road and configure everything properly. The CPAN module is a great tool, but it can sometimes be unreliable, as we discussed in Chapter 2, particularly when CPAN modules are not preconfigured by their authors in exactly the way that CPAN is expecting (many Internet modules fall into this category).

Table 5-1. Modules required to install LWP

Perl module

Description/download page

Digest::MD5

Perl interface to the MD5 message digest algorithm[4]

http://www.cpan.org/authors/id/GAAS

HTML::Parser

HTML parser class module for Perl

http://www.cpan.org/authors/id/GAAS

Libnet (e.g., libnet-1.0704.tar.gz)

Many related Perl modules

http://www.cpan.org/authors/id/GBARR

MIME::Base64

Module for encoding and decoding of Base64 strings

http://www.cpan.org/authors/id/GAAS

URI

Uniform Resource Identifiers module

http://www.cpan.org/authors/id/GAAS

HTML::Tagset

Data tables handler useful for parsing HTML

http://www.cpan.org/authors/id/S/SB/SBURKE

LWP (e.g., libwww-perl-5.64.tar.gz)

The complete library for WWW access in Perl

http://www.cpan.org/authors/id/GAAS

[4] To learn more about Ronald L. Rivest's MD5 message digest algorithm, check out: http://theory.lcs.mit.edu/~rivest/homepage.html

Two other CPAN packages that people often use alongside LWP include:

Storable

Persistent data storage used to make HTTP less stateless.

http://www.cpan.org/authors/id/A/AM/AMS

HTML-SimpleParse

A bare bones HTML parser.

http://www.cpan.org/authors/id/KWILLIAMS

5.1.1.2 SSL — Secure Sockets Layer

If you need enhanced security at your site, you may also want to use the popular Secure Sockets Layer (SSL) program. Before you install LWP (or re-install it), be sure to check out the programs and Perl extensions listed in Table 5-2.

Table 5-2. Optional SSL modules for use with LWP

Perl module or C program

Description/download page

OpenSSL

Open Secure Sockets Layer program

http://www.openssl.org

Crypt::SSLeay[5]

OpenSSL Perl glue providing https support to LWP

http://www.cpan.org/authors/id/C/CH/CHAMAS

Net::SSLeay

Perl extension for using OpenSSL and https sockets

http://www.cpan.org/authors/id/SAMPO

IO::Socket::SSL

SSL socket interface class

http://www.cpan.org/authors/id/A/AS/ASPA

[5] SSLeay is named after the original "Secure Sockets Layer work by Eric A. Young."

5.1.1.3 Installing mod_perl

Before we begin our installation of mod_perl, we recommend that for security reasons you shut down all your Apache processes, and then save the entire Apache root structure, perhaps in a tarball, before continuing. We can always revert back to this saved structure later on, should mod_perl prove problematic.

To do the actual mod_perl installation on Unix, first download the latest and greatest goods by visiting Doug MacEachern's CPAN page:

http://www.cpan.org/authors/id/DOUGM

Then follow these steps:

  1. Begin with the time-honored routine:

    $ gzip -d mod_perl-1.26.tar.gz
    $ tar xvf mod_perl-1.26.tar
    $ cd mod_perl-1.26
    $ vi README INSTALL
  2. There exists a bewildering array of options you can use to build mod_perl. You'll find all of them detailed in the INSTALL file. We're just going to go for the simple install of compiling every option available, with the clever EVERYTHING=1 switch:

    $ perl Makefile.PL EVERYTHING=1 APACHE_PREFIX=/usr/local/apache
  3. The first thing Makefile.PL will try to do is find a source directory for Apache within the local vicinity. This should be available from the installation we performed in the previous chapter:

    Configure mod_perl with ../apache_1.3.24/src ? [y] y

    If a local Apache directory is unavailable, you'll be asked to supply one. Answer accordingly:

    Please tell me where I can find your apache src [] <your apache source>

    You'll then be asked if you want to build the httpd executable in the Apache source directory you nominated. We said yes:

    Shall I build httpd in ../apache_1.3.24/src for you? [y] y

    (On some Unix systems, including some Solaris flavors, it may be best to always use fully qualified path names, because of some problems with include paths. If mod_perl fails to build as expected, thoroughly check all the documentation that comes with mod_perl as well as the online resources mentioned at the start of this chapter, particularly the topics archive.)

  4. Lots of information will then appear, but assuming no problems, we can go ahead with the compilation:

    $ make
  5. The make test step is highly recommended if you've loaded LWP. In the following example, we include only a few lines of typical output, but much more than this should appear. Expect some tests to be skipped depending on your platform:

    $ make test
    ... 
    internal/table......ok                                                       
    internal/taint......ok                                                       
    All tests successful, 6 tests skipped.
    Files=34, Tests=390, 23 wallclock secs 
    (18.68 cusr +  1.75 csys = 20.43 CPU)

    If any tests should fail, re-run make test, but this time in verbose mode:

    $ make test TEST_VERBOSE=1
  6. Before installing, go to /usr/local/apache/bin directory and save the old httpd file (just in case):

    $ cd /usr/local/apache/bin
    $ mv httpd httpd.old
  7. We can now do the install:

    $ make install
  8. If you specified it earlier, you should also find a new httpd living under the Apache source directory, which you may have supplied on the Makefile.PL step. This will be approximately four times the size of your old httpd. Copy it to the main Apache executables' bin:

    $ cd ../apache_1.3.24/src
    $ cp httpd /usr/local/apache/bin
    $ cd /usr/local/apache/bin
    $ ls -la httpd*
    -rwxr-xr-x    1 root     root      1497133 Apr  1 15:47 httpd
    -rwxr-xr-x    1 root     root       410220 Apr  1 15:45 httpd.old
  9. As a sanity check, to ensure that we've successfully loaded mod_perl into the httpd binary, try the following command:

    $ ./httpd -l
    Compiled-in modules:
      ...
      mod_auth.c
      mod_setenvif.c
      mod_perl.c      # Bingo!!! :-)

    (If you didn't build Apache and mod_perl yourself, there is a chance that mod_perl will be dynamically loaded (following the DSO build pattern). In this case, it won't show up on httpd -l, which shows only statically compiled-in modules.)

5.1.1.4 Specifying the mod_perl Apache library

After a refreshing rest, we can begin again by writing our first mod_perl server script module, HelloApache.pm. (We'll deal with the conversion of ordinary CGI scripts later.) First of all, we need to establish where our main mod_perl Apache library will be. We suggest that you create a ../lib/perl/Apache directory:

$ cd /usr/local/apache
$ mkdir -p lib/perl/Apache

We now have two options for telling mod_perl where this library will be when Apache starts running. The first is to add the following line somewhere near the top of our httpd.conf configuration file:

PerlSetEnv PERL5LIB /usr/local/apache/lib/perl

However, because this approach adds a little overhead to each HTTP request, we recommend the second option instead. Go to your conf directory and edit a new Perl file:

$ cd /usr/local/apache/conf
$ vi startup.pl
$ chmod 755 startup.pl

Create the Perl script shown in Example 5-1 as your superuser (to ensure later security). Change directives, where appropriate, such as the location of the perl program in the shebang line (notice that we've commented out Apache::DBI, which we'll be covering later):

Example 5-1. startup.pl — Apache mod_perl initialization script
#!/usr/bin/perl
# Set up the include path to get our new lib/perl directory
BEGIN {
   use Apache(  );
   use lib Apache->server_root_relative('lib/perl');
}
  
# Insert the most required modules
  
use Apache::Registry(  );
use Apache::Constants(  );
#use Apache::DBI(  );      # We'll get to this later! :-)
use LWP(  );
use CGI qw(-compile :all);
use CGI::Carp(  );
1;                       # Must finish with a true value

On httpd startup, the preceding Perl script will be run and will therefore load everything important directly into memory (use startup.pl to add other modules later on). It is run by adding the following lines to httpd.conf:

PerlRequire conf/startup.pl
PerlFreshRestart On

PerlFreshRestart On means that on every hit to Apache, its entire collection of compiled modules is dumped and reloaded. PerlFreshRestart Off means that modules are only loaded once, when the Apache child process fires up, for added performance. Having PerlFreshRestart On is a major performance cost but is pretty much essential while we're in development. Developing with PerlFreshRestart Off is a headache, because if you change a module and reload the page, you can't be sure whether you have the new modified version of your module, or some older cached copy that an Apache child still has hanging about.

Here's the bottom line: use PerlFreshRestart On for development, Off for production.

Now we can restart Apache, this time with the added zest of mod_perl:

$ /usr/local/apache/bin/apachectl restart
/usr/local/apache/bin/apachectl start: httpd started

This means we can also write our first new Apache module, to directly access the internal workings of the httpd server.

$ cd /usr/local/apache/lib/perl/Apache
$ vi HelloApache.pm

Now create the test package, as in Example 5-2:

Example 5-2. The HelloApache.pm module
package Apache::HelloApache;
  
use strict;
use Apache::Constants qw(:common);
  
sub handler {
   my $r = shift;
   $r->content_type('text/html');
   $r->send_http_header;
   my $host = $r->get_remote_host;
   $r->print(<<END);
<HTML><HEAD><TITLE>HelloApache</TITLE></HEAD>
<BODY><CENTER>
<H1>Hello $host</H1>
<H2>Okay, perhaps we should have said "Hello World!" but nobody
expects Perl Sith Lords to do the expected! :-)
<H2>
</CENTER></BODY></HTML>
END
   return OK;
}
1;                       # Must finish with a true value

Because this is a real live server upgrade, we need to tell httpd when to access this handler process. Again, edit httpd.conf:

<Location /hello/apache>
   SetHandler perl-script
   PerlHandler Apache::HelloApache
</Location>

Now restart Apache and point your browser to http://localhost/hello/apache:

$ /usr/local/apache/bin/apachectl restart
/usr/local/apache/bin/apachectl restart: httpd restarted

Ladies and Gentlemen, check out Figure 5-1. The mod_perl has landed.

Figure 5-1. Our first Apache Perl module in sparkling form
figs/pdba_0501.gif

Now, you may acknowledge that this is all very nice and agree that mod_perl works much more efficiently than the plain Perl CGI alternative. But you may also have 200 debugged Perl CGI scripts, all of which work brilliantly from a functional point of view, and you may have very little free time available to spend converting these scripts to Apache server modules. So even though your scripts are eating up too much CPU (and management is thinking of Java servlets), you probably have little inclination to plunge into a major conversion effort. What can you do? Read on.

5.1.2 Apache Perl Modules

Because you've embedded the Perl interpreter into the heart of the Apache server, the entire Apache server-side API is available to Perl programmers. The two modules listed here will provide you with the most bang for the buck in terms of managing your current collections of DBA CGI scripts:

Apache::Registry

Fortunately, we can avoid rewriting all of our CGI scripts into Apache server functions like HelloApache. Like King Arthur's cavalry, Apache::Registry comes riding through the mist to our rescue. With Apache::Registry, we get most of the benefits of mod_perl without having to change a single line of our current CGI scripts. We can then eventually choose to port these over to the new modular style in our own good time.

Apache::DBI

Using traditional Perl CGI scripts eats up memory, but there is another major cost as well, especially in conjunction with Perl DBI. That is the continuous creation stream of expensive database connections. The solution can be found in the mysterious connection pool of Apache::DBI.

We'll look at these two key modules in the following sections. All of the popular Apache mod_perl packages are summarized in Table 5-3.

Table 5-3. Main Apache mod_perl modules

mod_perl module

Description

Apache::Registry

Enhances the running of unaltered CGI scripts

Apache::Status

Embedded interpreter providing runtime status

Apache::Embperl

Embeds Perl within HTML

Apache::SSI

Server-side includes, implemented in Perl

Apache::DBI

Transparently maintains persistent DBI connections

Apache::Gateway

Implements an HTTP/1.1 gateway

Apache::GzipChain

Compresses web output on the fly

Apache::Filter

Filters document and script outputs

Apache::Sandwich

Automatically generates page headers and footers

Apache::TransLDAP

Translates URIs via LDAP lookups[6]

Apache::ASP

Implements a port of Active Server Pages to Perl[7]

Apache::AuthenDBI

Authenticates against a database via DBI

Apache::PHLogin

Authenticates against a PH database with the Net::PH module[8]

Apache::DBILogger

Logs requests to a database via Perl DBI

Apache::Session

Provides persistent session management facilities

Apache::Throttle

Content negotiation based on connection speed

[6] URI stands for Uniform Resource Identifiers (see http://www.ics.uci.edu/pub/ietf/uri/ for more on related Internet definitions). LDAP stands for Lightweight Directory Access Protocol (see http://www.openldap.org/ for the OpenLDAP project).

[7] Check out http://www.nodeworks.com/asp/ for more details.

[8] The Ph (Phonebook) Nameserver is a database widely used as an online phonebook server for public organizations. See http://www-dev.cso.uiuc.edu/ph/for more details.

5.1.2.1 Apache::Registry

To take advantage of the performance advantages of mod_perl, you normally must rewrite your Perl CGI scripts in the form of server subroutines. Apache::Registry, which comes automatically with mod_perl, helps avoid this overhead.

As we mentioned, you may already have a large number of working scripts that you use in performing Oracle database administration. There is nothing really wrong with them; the only problem is the overhead of their full execution cycle every time they're requested. This makes them processor-intensive (i.e., slow). You'd rather avoid rewriting them all as mod_perl scripts, but you would like to make them run faster — this is where Apache::Registry comes in. It takes CGI script calls, in the form of http://www.myhost.com/cgi-bin/cgi-script.pl, and evaluates them into server subroutines, thereby turning plain old scripts into much quicker mod_perl objects. These server subroutines remain resident in the Apache server's memory. You will generally find that using Apache::Registry gives you a massive power enhancement.

You will need to check the mod_perl and Apache::Registry documentation, as this shortcut makes certain assumptions about your CGI coding standards. Scripts that you have coded in a quick-and-dirty way may end up failing the evaluation performed by Apache::Registry.[9]

[9] Check out these pages for more information: http://perl.apache.org/ and http://perl.apache.org/dist/cgi_to_mod_perl.html.

By far the most common problem is using uninitialized "my" variables. What Apache::Registry really does is to grab the meat of a script and put it into a handler subroutine, which may fail to recognize uninitialized lexical variables. (See Appendix A, for a discussion of "my" variables.) Therefore, we need to hard-initialize all of our variables (e.g., specify my $foo = 0; instead of just my $foo;), to avoid the most common trap. Check out the following for much more detail:http://theoryx5.uwinnipeg.ca/guide/.

Note that each child process must compile at least once, so early requests may seem slow, but each subsequent request will be dealt with in Apache server memory and will seem very fast indeed. You will particularly notice this effect with large scripts or those with lots of module calls.

Follow these steps to use Apache::Registry:

  1. We need to add a few more lines to our httpd.conf file:

    Alias /perl /usr/local/apache/perl
    <Location /perl>
       SetHandler perl-script
       PerlHandler Apache::Registry
       PerlSendHeader On
       Options +ExecCGI
    </Location>
  2. Now create a corresponding ../perl directory on the web server, into which we move our chosen CGI scripts (obviously, you may simply wish to point the /perl alias, as above, directly towards your current CGI directory):

    $ cd /usr/local/apache
    $ mkdir perl

    We'll use the code in Example 5-3 to demonstrate how Apache::Registry works. Notice that we can use all of the typical CGI environmental variables, such as REMOTE_ADDR.

Example 5-3. The HelloInquisition.pl program
#!/usr/bin/perl
  
use strict;
print "Content-Type: text/html\n\n";
  
print <<END;
<HTML><HEAD><TITLE>HelloInquistion</TITLE></HEAD>
<BODY><CENTER>
<H1>Hello $ENV{REMOTE_ADDR}, are you comfortable? 8-)</H1>
<H2>Nobody ever expects the HelloInquisition.pl Script!</H2>
</CENTER></BODY></HTML>
END

Make the script executable and restart Apache to pick up the httpd.conf change:

$ cd /usr/local/apache/perl
$ chmod 755 *.pl
$ /usr/local/apache/bin/apachectl restart
/usr/local/apache/bin/apachectl restart: httpd restarted

You can see the Perl script output in Figure 5-2.

Figure 5-2. Apache::Registry linking CGI to mod_perl
figs/pdba_0502.gif

Use of the Apache::Registry module helps overcome the performance problem that occurs when the Perl interpreter has to be re-executed every time a Perl script is called. But there is also another source of performance problems with CGI scripts more closely linked to database usage. We'll discuss that in the next section.

5.1.2.2 Apache::DBI

Each time you run a Perl CGI script that accesses a database, that script opens a new connection to the Oracle database at the beginning of the script, and then has to close it again at the end. This happens every single time you run the script, no matter how many thousands of people an hour are browsing the target page. This login process has a substantial overhead associated with it. It creates another performance issue for CGI scripts, one that even Apache::Registry can't overcome: even if the script is always in memory, it still has to open and close database connections.

Edmund Mergl has provided an excellent solution. His Apache::DBI module is an extension to Apache that's written in Perl (and thus requires the presence of mod_perl). Once you load Apache::DBI, it pools, or caches, all of the required database connections into memory, lending them out the same way that ConnectionPool classes do for Java. Whenever this module detects that a CGI script is opening or closing a database connection, it simply steps in and takes over from DBI, handing out and collecting its pooled Oracle connections as necessary, closing and opening them independently of the CGI scripts in current operation. These cached connections are known as persistent connections because the connection to the database is kept persistent between sessions. Apache::DBI does its work entirely in the background, so you'll be aware only that your web site has become much faster and far more scalable — even more important, your database is doing less work!

Unlike Apache::Registry, which comes preloaded with mod_perl, Apache::DBI is a supplementary module available from CPAN.[10] You can obtain it from:

[10] The Apache module Apache::AuthDBI also comes with Apache::DBI, giving you two excellent modules for the price of downloading just one.

http://www.cpan.org/authors/id/MERGL

Follow these steps:

  1. Simply install Apache::DBI the same way you'd install any other regular Perl module (notice that there is no make test step):

    $ gzip -d ApacheDBI-0.88.tar.gz
    $ tar xvf ApacheDBI-0.88.tar
    $ cd ApacheDBI-0.88
    $ vi README 
    $ perl Makefile.PL
    $ make
    $ make install
  2. Revisit startup.pl script and uncomment the earlier call to Apache::DBI:

    $ cd /usr/local/apache/conf
    $ vi startup.pl
      
    #!/usr/bin/perl
    ...
    use Apache::DBI(  ); # Uncomment this non-standard Perl Apache module! 8)
    ...
    1;

    Alternatively, add the following line to httpd.conf:

    PerlModule Apache::DBI
  3. Apache::DBI transparently takes over the following DBI calls within scripts:

    DBI->connect
    DBI->disconnect
  4. By taking over DBI->connect statements, to prevent them from connecting directly to a database each time, Apache::DBI lends scripts a preprepared database connection. It creates and deletes these connections, as necessary, in the background to maintain a pool of replacements. It also replaces the DBI->disconnect statement with a do-nothing statement, as follows:

    sub disconnect {
        my $prefix = "$$ Apache::DBI            ";
        print STDERR "$prefix disconnect (overloaded) \n" 
           if $Apache::DBI::DEBUG > 1;
        1;
    };

Simply move your DBI web scripts to the target ../perl area to gain its benefit.

5.1.2.3 Apache and ORACLE_HOME

Apache generally needs to know where your ORACLE_HOME is in order to get DBD::Oracle to work correctly. The easiest way of specifying any environment variable is to have a line such as the following in httpd.conf:

PerlSetEnv ORACLE_HOME /opt/oracle/product/9.0.1

(We use PerlSetEnv, rather than Apache's usual SetEnv, because it is guaranteed to take effect before all of the mod_perl and Apache handlers run; their Perl additions may later require values such as ORACLE_HOME.)

With persistent database connections now on board, let's give it a whirl. Try out the CGI script in Example 5-4. Notice that there's no explicit use of Apache::DBI within the script. It has been called off the bench in startup.pl, and mod_perl is holding it in memory for us under the floodlights, keeping it there until we send out the blade runners later on to shut down the Apache server daemons.

Example 5-4. WaitsMonitor.pl
#!/usr/bin/perl
  
use strict;
use DBI;
use CGI qw(:standard :netscape);
use CGI::Pretty qw(:html3);
  
# Link to Oracle, this time via Apache::DBI in the background,
# and set up our SQL to get our results.
  
my $url = 'dbi:Oracle:orcl.world';
my $user = 'system';
my $passwd = 'manager';
  
my $dbh = DBI->connect($url, $user, $passwd, {RaiseError=>1, AutoCommit=>0});
  
my $sth = $dbh->prepare('select event Wait_Event, ' .
                        'total_waits Tot_Waits, ' .
                        'time_waited Times_Waited ' .
                        'from v$system_event ' .
                        'where event like \'%file%\' ' .
                        'order by total_waits desc ' ) ;
  
$sth->execute or die "Cannot execute";
  
# Get the fieldnames, and make them into table headers.
my $rs = $sth->{NAME};
my @col_head;
for (@$rs)
{
   push(@col_head, $_);
}
  
# Now get the data dough, and roll out the pastry
my @row;
my @rows;
while (@row = $sth->fetchrow_array)
{
   push(@rows, td(\@row));
}
$dbh->disconnect;
  
# Finished with DBI.  Now we sort out the CGI side of life.
  
my $title = "Welcome back to WaitsMonitor!";
  
# Create the HTML page.  
my $current_time = localtime(  );
print header,
      start_html(-title=>$title, -bgcolor=>'white', -text=>'black'),
      center(h1($title), 
             hr(  ),
             table({border=>'2'},
                   caption($current_time),
                   TR([th(\@col_head), @rows])
                  )),
      end_html;

Make the script executable:

$ cd /usr/local/apache/perl
$ chmod 755 WaitsMonitor.pl

You can see the results of this script's being called in Figure 5-3.

Figure 5-3. Apache::DBI saving us connection time
figs/pdba_0503.gif

5.1.3 Installing mod_perl on Win32

Fortunately for those of us who have just waded through the Unix installation of mod_perl, there is a binary version of mod_perl that was built for Win32 and Perl 5.6 by the heroic Randy Kobes. Installing it is very straightforward:

  1. Now is as good a time as any to load up your favorite optional Apache-related module from ActiveState.com (though it's also possible to do this later on):

    C:\Program Files\Apache Group\Apache\modules>ppm
    PPM interactive shell (2.1.5) - type 'help' for available commands.
    PPM> install ApacheDBI
    Install package 'ApacheDBI?' (y/N): y
    ...
  2. We can also use PPM to install a more independent distribution of mod_perl (available from Canada's University of Winnipeg Department of Theoretical Physics). You will get all of the default Perl modules required by mod_perl with this download, and much more besides. (Notice the use of an HTTP address to pick up the PPD file, which itself points us towards the gzipped file on the web server that contains the necessary files):[11]

    [11] The installation tarball can be downloaded directly from the University of Winnipeg site; if you'd like to view its constituents, check out http://theoryx5.uwinnipeg.ca/ppmpackages.

    PPM> set repository theoryx5
         http://theoryx5.uwinnipeg.ca/cgi-bin/ppmserver.pl?urn:/PPMServer
    PPM > install mod_perl 
    PPM > set save

    The set save step ensures that theoryx5 is available later for PPM downloads.

  3. During the PPM process, a second console screen should pop up, asking you where you'd like to install the necessary mod_perl.so file. We used the directory for modules installed with Apache in Chapter 4:

    Which directory should mod_perl.so be placed in?
       (enter q to quit) [C:/Apache/modules]     
           C:/progra~1/apache~1/apache/modules
  4. The mod_perl.so file is now safely shipped in:

    C:\Program Files\Apache Group\Apache\modules>dir mod_perl*
    MOD_PERL SO        208,896  14/03/02   1:39 mod_perl.so
5.1.3.1 Configuring Apache on Win32

We're nearly there. Before starting Apache on Win32, however, we need to add the following line to the httpd.conf file after all the other LoadModule statements:

LoadModule perl_module modules/mod_perl.so

Do this from the Win32 Start menu as follows:

Start figs/U2192.gif Programs figs/U2192.gif Apache HTTP Server figs/U2192.gif Configure Apache Server figs/U2192.gif Edit the Apache httpd.conf Configuration File

Also make sure you add the following line after the AddModule section:

AddModule mod_perl.c

Keep httpd.conf open at this point, and move onto the next stage.

5.1.3.2 Testing on Win32

As with Unix, we need to load up the Count of Monte Cristo's chest of Apache jewels every time we fire up the server.

  1. Modify the original startup.pl file from Unix and insert it into the C:\Program Files\Apache Group\Apache\conf directory. Note especially the first line, which directs Apache to use ActivePerl's Perl executable:

    #!/perl/bin/perl
    # Set up the include path to get our new lib/perl directory
    BEGIN {
       use Apache(  );
       use lib Apache->server_root_relative('lib/perl');
    }
      
    # Insert our A-Team modules
      
    use Apache::Registry(  );
    use Apache::Constants(  );
    use Apache::DBI(  ); 
    # use LWP(  );                 Uncomment if LWP loaded! :-)
    use CGI qw(-compile :all);
    use CGI::Carp(  );
    1;
  2. On server startup, this will locate our nominated Apache/Perl library and load all of our Apache::* modules into the requisite memory areas.

  3. Now create the actual directories necessary to store the Apache modules; startup.pl will point at these when it's fired up:

    C:\Program Files\Apache Group\Apache\lib>mkdir perl
    C:\Program Files\Apache Group\Apache\lib>cd perl
    C:\Program Files\Apache Group\Apache\lib\perl>mkdir Apache
  4. Now add the following to httpd.conf to ignite this process later on:

    PerlRequire conf/startup.pl
    PerlFreshRestart On

We can now write our first Win32 Perl Apache module.

5.1.3.3 HelloWin32.pm

Move to our new perl\Apache directory:

C:\ cd C:\Program Files\Apache Group\Apache\lib\perl\Apache

We can create our new module, shown in Example 5-5, which does some checking on tablespace fragmentation via a subroutine called from the Apache handler process. (It's easy to forget, but what we're doing here really is quite amazing. We're right in the heart of the Apache server, changing it directly to make it do exactly what we want it to do. Could any other web server give you this kind of flexibility with something as relatively easy to use as Perl?)

Example 5-5. HelloWin32.pm
package Apache::HelloWin32;
  
use strict;
use DBI;
use Apache::Constants qw(:common);
use CGI qw(-compile :all);
  
sub handler {
 
   my $r = shift;
   $r->content_type('text/html');
   $r->send_http_header;
   my $host = $r->get_remote_host;
   my $table = tabspace_frag(  );
   $r->print(<<END);
<HTML><HEAD><TITLE>Hello Win32</TITLE></HEAD><BODY>
<H1>Hello $host - Let's do something half useful</H1><HR>
$table
</BODY></HTML>
END
  
   return OK;
}
sub tabspace_frag {
   my $url = 'dbi:Oracle:orcl';
   my $user = 'system';
   my $passwd = 'manager';
  
   my $dbh = DBI->connect($url, $user, $passwd, {RaiseError=>1});
  
   my $sth = $dbh->prepare( 'SELECT ts.name tspace, ' .
                                   'tf.blocks blocks, ' .
                                   'sum(f.length) free, ' .
                                   'count(*) pieces, ' .
                                   'max(f.length) biggest, ' .
                                   'min(f.length) smallest, ' .
                                   'round(avg(f.length)) average, ' .
                                   'sum(decode(sign(f.length-5), ' .
                                   '-1,f.length,0)) dead ' .
                              'FROM sys.fet$ f, sys.file$ tf, ' .
                                   'sys.ts$ ts ' .
                             'WHERE ts.ts# = f.ts# ' .
                               'AND ts.ts# = tf.ts# ' .
                             'GROUP BY ts.name,tf.blocks');
   $sth->execute;
  
   # Get the fieldnames, and make them into table headers.
   my $rs = $sth->{NAME};
   my @col_head;
   for (@$rs)
   {
      push(@col_head, $_);
   }
   # Now get the data, to fill the table with shortly.
   my @row;
   my @rows;
   while (@row = $sth->fetchrow_array)
   {
      push(@rows, td(\@row));
   }
   $dbh->disconnect;
  
   # Now we sort out CGI and return to handler.
  
   # Create the HTML page.  
   return center(table({border=>'2'},
                       caption("Tablespace Fragmentation"),
                       TR([th(\@col_head), @rows])));
}
1;  # This is a package, therefore truth required

Before we complete our test run, we need to make one final addition to the httpd.conf configuration file before running the server:

<Location /hello/win32>
    SetHandler perl-script
    PerlHandler Apache::HelloWin32
</Location>

We can now set Apache running:

Start-> Programs-> Apache HTTP Server-> Start Apache in Console


You can see the spectacular results in Figure 5-4.

Figure 5-4. HelloWin32.pm attempting to be half useful
figs/pdba_0504.gif
    Team LiB   Previous Section   Next Section