CCL Home Page Valid CSS! Valid XHTML 1.0 Transitional

Up Directory CCL mod_perl and Apache on Fedora 6 (FC6)

Edutainment with mod_perl2 on Fedora Core 6 (FC6)

*** UNDER CONSTRUCTION ***

The reasons why I wrote this thing are primarily selfish (Can you believe it?). I used a lot a previous version of mod_perl (1.x) and decided to try the new version and refresh my memory. At the same time, once I do it, I can put my notes on the Web, and gain popular admiration, fame, and gratitude. People will love me and bards will sing ballades... Nice...
Now the mod_perl2 seems stable after transition from Apache 1.x to 2.x. It is time to use it. This is not a text for gurus but for those who want to start using mod_perl2 pronto and learn later. This page uses Ajax (it saved me some time, since I did not have to retype all the examples in the HTML text you look at). If you want to look at the JavaScript that is running this thing, please click [ Here ] .

What is mod_perl?

mod_perl is a better way of running perl CGI scripts under Apache web server and much more. I stress: much more!!! but let us start from a simplified picture. Traditionally, when you use perl for creating dynamic Web pages (e.g., to process web forms) you put them into some cgi-bin directory and make it known to Apache that scripts are there via its config file (e.g., under standard distribution of FC6 the config file is /etc/httpd/conf/httpd.conf). Then, whenever the script is requested, the Apache creates a new process and runs the script for you. This is a nice thing... Even if your script messes up and has errors, the Web server will just say: Error and the process that was running your script will die and does no harm to the Web server process, i.e., the Apache (unless you did something really stupid...). There is a price to pay for this safety, however... It is damned slow... Why? since each time someone requests the page, new process has to be created, the perl compiler has to run to compile the perl script together with all perl modules that it uses, and only then the script runs and prints your Hello World! or Hello John.. Thank you for your input and credit card number.

The mod_perl gets around this slowness in a number of ways. First, mod_perl usually precompiles the scripts at the time of Apache start-up (or when the script is run for the first time) and keeps the executables in memory. When the CGI script is requested, it does not have to be recompiled. Second, the new process does not have to be created, since the perl runs within Apache. Needless to say, for scripts that do not do much computations the time needed for setting up the separate process and compiling the script is much, much longer than what script actually does once it runs. Such scripts run in mod_perl often hundreds times faster. Moreover, you do not have latency of process creation, reading perl compiler from the disk, and then reading the script from the disk. If you never serve more than one page a minute, it may not matter much (though your page will show faster with mode_perl), but if you have to serve a few pages a second or more at peak hours, your web server performance improvement with mod_perl will be stunning compared to traditional CGI. Moreover, the mod_perl provides you with a better way of running server side scripts than a traditional CGI approach by using handlers. More about it in a moment (or two...).

So... What is the catch? There is very little catch for the perl programmer. In practice, to make your old CGI perl scripts work under mod_perl you need to make sure that they are well written (what you should do anyhow), i.e., do not have memory leaks (this is usually not a problem, since perl has great memory management), do not use global variables that may conflict with other scripts (note... all your scripts are all compiled and sit in the memory side by side!), close the files that scripts open, etc. Just use sound programming practices. For this reason always put use strict; on top of the scripts and use the use warnings and use diagnostics when you debug your scripts. The other catch is that if your scripts repeat a lot of the same code, you will bloat the Apache process with unnecessary repeats. The way out of it is to put the overlapping code in modules and make scripts lean. But you would do it anyhow for maintainability, wouldn't you? Of course, when you want to use the mod_perl to its full extent, there is big catch... You need to learn the thing. But it will be well worth it, since you will create better, faster, and easier to maintain web applications.

Checking if mod_perl is ready for you.

There are plenty of excellent documents on the mod_perl Web site (http://perl.apache.org) but for the novice they may be somewhat overwhelming (I am trying to convince you to use mod_perl and make you believe that it is a piece of cake, here...). Moreover, most of these documents are in the form of a reference rather than a tutorial. They also assume that you need to install and compile the mod_perl. Since FC6 comes with mod_perl, you do not have to start from scratch, so bear with me a bit longer before you decide to go to the SOURCE and become an expert. I will put more links towards the end, but I want you to try mod_perl first. Log in as root, run X (since you will need a browser, e.g., Firefox), open a terminal and check if you have Apache installed. Obviously you need perl. Type the following and check: yum list perl
If you get something like:

url=files/ta1.txt

you are fine. If you do not have perl installed, you need to get it installed:
yum install perl
and take a break if you are on slow Internet.

Then check if you have full Apache installation:
yum list installed 'httpd*'
and see if you get

url=files/ta2.txt

If you do not have some packages, just use yum to install them, e.g., yum install 'httpd*'
will install Apache development environment (and you will need it). You are also well advised to download the Apache related perl modules as:
yum install 'perl-Apache*' 'mod_perl*'
By default Apache is not enabled in Fedora. To make it start on boot-up edit the Apache start-up script (httpd in the /etc/rc.d/init.d directory by changing the line:
# chkconfig: - 85 15
to
# chkconfig: 345 85 15
and make necessary boot-up links by typing the following commands:
cd /etc/rc.d/init.d
chkconfig --del httpd
chkconfig --add httpd
chkconfig --list httpd
and start the Apache web server with:
/etc/rc.d/init.d/httpd start
Now... If you are using the computer that is not connected to the network, or does not have a DNS entry open the browser on your screen with the URL: http://localhost or http://127.0.0.1 but if your computer has a name in a DNS, just call it by name, for example: http://www.example.com. You should get a startup, default Apache page. The default directories for Apache under default Fedora installation are:

  • Configuration directories: /etc/httpd
  • Apache scripts and pages: /var/www
  • Apache logs: /var/log/httpd. When something does not work do a tail /var/log/httpd/error_log to learn what happened. And if you need more than 10 lines (say 30) do: tail -30 /var/log/httpd/error_log
  • Apache modules and libraries: /usr/lib/httpd

Before you touch ANYTHING it is a good idea to backup all configuration files before changing them. Of course, you can roll out subversion or CVS but for these simple exercises, I would suggest to put a simple script bk in the /usr/local/bin directory.

url=files/bk.txt

and change its permissions to executable:
chmod 755 /usr/local/bin/bk
Then, whenever you want to make a change (that you may regret later) to a file, you just run the bk script on a file and you can later backtrack. For example:
bk /etc/httpd/conf/httpd.conf
will make a time stamped copy of the main Apache configuration file, while the
bk /etc/httpd/conf.d/perl.conf
will save the original apache mod_perl configuration include file so you can recover when your Apache does not even want to start, simply by copying the backup over the modified file as, e.g.:
cd /etc/httpd/conf.d
cp -p perl.conf.061116160142 perl.conf

Let's use mod_perl on something simple

Let us create some scripts... Create a directory: /var/www/perl if it does not yet exist and place there a simple script testing.pl file:

url=files/testing_pl.txt

Remember about
chmod 755 /var/www/perl/testing.pl
though for mod_perl you do not really need to do it (execute permissions are not needed). However, we will need it to run the script as standalone. Running it in a regular UNIX shell as:
/var/www/perl/testing.pl
should spew something like:

url=files/ta5.txt

that makes sense. Indeed, the routine was called in a package main and in the 4th line of the file /var/www/perl/testing.pl. Now let us see what happens when we run it inside Apache's mod_perl. Make changes to the mod_perl configuration file by editing the file /etc/httpd/conf.d/perl.con (DID YOU MAKE A BACKUP? OR YOU WANT TO HEAR: Didn't I tell you?) and uncomment the following fragment:

url=files/fragment_1.txt

Then restart Apache
/etc/rc.d/init.d/httpd restart
and see what your browser is telling you (the URL is: http://localhost/perl/testing.pl or http://127.0.0.1/perl/testing.pl or use your host name as, for example: http://www.example.com/perl/testing.pl). You should get something like:

rows=20;cols=75;url=files/ta7.txt

Wow.... So much junk from so little script. Obviously, the script is no longer called normally... What happened behind the scene is that your script was converted into a subroutine and included within a package that was autogenerated by mod_perl, processed by eval and it now sits in the memory together with other code of Apache. Your script executes now much faster than with the traditional CGI approach since it runs from within Apache process and does not have to be compiled again, and again, and again... Of course, you can also copy this script to the directory: /var/www/cgi-bin/ (the default directory for CGI script as preconfigured in the virgin installation of FC6) and request testing.pl as a regular CGI script by using the URL http://localhost/cgi-bin/testing.pl (or whatever...). Did you get something like this below?:

url=files/ta8.txt

What it means is that in the CGI approach, it is a plain old way where perl interpreter is running the script in a separate process from the file: /var/www/cgi-bin/testing.pl. You should now appreciate the genius of people who developed the mod_perl. This thing is written in C and is really a masterpiece. It allows you to use all Apache internals in your own perl modules and even reconfigure the Apache from within your perl code when it runs.

mod_perl status

The mod_perl comes with the utility that allows you to look at loaded modules, environment and many other things. By default, it is commented out in the /etc/httpd/conf.d/perl.conf for good reasons. It should not really be accessible to anyone but the webmaster or otherwise the bad guys may crack your system knowing this info and will take your software away from you. The easiest way to protect it is to allow access only from the local machine (127.0.0.1) or from the static IP address(s) that you absolutely trust. Of course you can make it secure with secure authentication, but let us not wander away too much. I uncommented the bottom portion of the perl.conf file (for a while...) and made some small modifications. You need to adopt it to your particular situation.

url=files/fragment_2.txt

After restarting Apache:
/etc/rc.d/init.d/httpd restart
I pointed my browser at http://127.0.0.1/perl-status (or whatever...) and saw a menu. You can browse it and explore it. It has a lot of information about what is running within mod_perl. You will see loaded modules (including YOUR modules), environment, and dependencies. Later on, when you start doing a real thing, you will need it for debugging and optimization.

Buggy scripts

Now let us see what happens with Apache and mod_perl when we have a buggy perl script. Crate a script junk.pl that has a syntax error and save it in the /var/www/perl directory. For example:

url=files/junk_pl.txt

Make it executable (chmod 755 /var/www/perl/junk.pl). and run it from within shell
/var/www/perl/junk.pl
and you get something like:

url=files/ta11.txt

When you request the script via a browser (http://localhost/perl/junk.pl or whatever) you will get the infamous screen:

url=files/ta12.txt

You should now look into the error log, either using your favorite text editor or just displaying the end of the error_log as:
tail /var/log/httpd/error_log
that will give you the last ten lines or, if it is a loooong error, tell the tail how many bottom lines you want, e.g., 50:
tail -50 /var/log/httpd/error_log
In any case, we find the error there as:

url=files/ta13.txt

and there is no surprise here. You can also have a more detailed error description by adding the diagnostics pragma in your script:

url=files/junk_pl_1.txt

When you run your script in the shell
/var/www/perl/junk.pl
you will get a lot of literature to read due to the diagnostics pragma:

url=files/ta15.txt

However, when you request your
http://www.example.com/perl/junk.pl (or whatever...) messy script, you will get the same Apache error screen in the browser as for the case without diagnostics pragma. Surprisingly, when tailing your /var/log/httpd/error_log you will see the same error information as if the diagnostics pragma was absent (though the line number changed since you added two lines). So why is it so? Because your mod_perl converted your puny junk.pl into a subroutine and removed the use diagnostics;. You can configure mod_perl to provide more debug info but we will get to it later. Note that if you copy the junk.pl script to the /var/www/cgi-bin directory and then request it through the CGI URL (http://127.0.0.1/cgi-bin/junk.pl) your pragmas will be respected and you will get the long diagnostics trace in the /var/log/httpd/error_log and an additional line about: Premature end of script headers: junk.pl, though most likely your browser screen will be blank (the document did not produced any output to STDOUT).

Now, let us fix the /var/www/perl/junk.pl script by adding the damned EOF, removing use diagnostics; and making a $date a lexical variable by prepending it with my (the strict pragma requires this).

url=files/junk_pl_2.txt

Now even junk will work when you point your browser to http://www.example.com/perl/junk.pl (or whatever). My advice is... Whenever you can, try to first run your script under UNIX shell with use diagnostics; but NEVER AS root OR apache. I had put it in red for a reason. The scripts often use some Apache environment variables for directories and files. These are not usually set (or not set correctly) if you run the script in UNIX shell. This can mean a disaster!!!. Imagine a line:
unlink("$ENV{SCRIPT_FILE_NAME}/etc/passwd");
and note that in the shell $ENV{'SCRIPT_FILE_NAME'} is not defined under UNIX shell, but in Apache it points to the path of the script being executed. You just lost your UNIX password file if you ran this script as root. For this reason, for syntax checks only, run your script with the -c option of perl interpreter:
perl -c myscript.pl
i.e., compile but do not execute. If your script's syntax is OK, it will just say:
myscript.pl syntax OK
but it you have syntax errors, it will tell you all about them. Check man perlrun for more information for perl command line options and get into a routine to use also the -w option that prints warnings, e.g. as:
perl -c -w myscript.pl
As an exercise in exploring the difference in environment variables when script is run in UNIX shell and under mod_perl in Apache, put the following script (testenv.pl) in the directory /var/www/perl:

url=files/testenv_pl.txt

and run it in the shell (after you did chmod 755 testmore.pl as:
/var/www/perl/testenv.pl
and then use your browser (http://127.0.0.1/perl/testenv.pl) and list environment variables that are seen by the script in Apache's mod_perl and compare.

The real mod_perl -- handlers

The examples of scripts above were of the CGI on steroids variety. The mode_perl provides a much better approach to serving dynamic content through the handler approach. This facilitates implementation of the Model-View-Controller (MVC) pattern that is very popular and well established for serving Web content. This approach requires us to create a perl module that contains a handler subroutine (actually, you can call it whatever, but handler is a default). We then have to tell Apache how to use it via appropriate settings in the /etc/httpd/conf.d/perl.conf configuration file. Let us create a simple module first. Create a directory for your mod_perl related modules as:
mkdir /var/www/mod_perl
mkdir /var/www/mod_perl/Tests
cd /var/www/mod_perl/Tests
and create a simple perl module there as MyFirstModule.pm

rows=25;url=files/MyFirstModule_pm.txt

Add the following lines

url=files/ta19.txt

to the /etc/httpd/conf.d/perl.conf file and create a short start-up script at as /var/www/mod_perl/startup.pl:

url=files/ta20.txt

that loads on Apache start-up and prepends the path to the location of our modules to the @INC array of perl. You can easily verify this by clicking on the Environment and PerlRequire'd Files links of the http://127.0.0.1/perl-status (or whatever {;-)}  ) page. This way mod_perl can find the Tests::MyFirstModule package. Now, fire your browser with the following URL: http://localhost/MyFirstModule and watch what is displayed (the big fonts and fancies are not shown below):

rows=20;cols=70;url=files/ta21.txt

Enter something for First and Last Name and keep clicking on [Submit] button and pay attention to the Global Counter Value and the value of Access Counter (but have a look at other things too). If you did not touch the default configuration file of Apache (/etc/httpd/conf/httpd.conf) you should say (repeat after me:) Gosh!!! They should be the same and they are different!!! (unless you of course know this stuff in and out and just wasting your time reading this junk). To figure out what is happening we need to DEBUG.
Before we go any further note (please!!!) an important difference between the CGI way of requesting scripts and mod_perl handler method. It does not matter if your URL is
http://localhost/MyFirstModule or
http://localhost/MyFirstModule/some/path/following/it.bin or even
http://localhost/MyFirstModule/1+2. You will be going to the same handler. Try it with various paths. Be creative!!!. In this case, if you kept /MyFirstModule/ at the beginning, you will get the same page. The only difference is in the environment variables that are reported to the handler. Your ENV{PATH_INFO} and ENV{REQUEST_URI} (and others, but those are the important ones) will report the actual stuff that followed your /MyFirstModule/. You now grasp the potential... You can use the handler as a dispatcher and call appropriate subs in response to different stuff. You will have all your code neatly packed in one package and can really put related things together and avoid duplication. Think about it, relax, and dream about the opportunities...

Few thoughts on debugging perl and mod_perl

As you will have read references given by me later (you will read them and become a guru and get popular respect and follow-up), there are many ways to debug perl and mod_perl. My primitive philosophy is, however, that you should not write bad code with bugs in the first place and should not use debuggers, unless someone else screwed up. How to do it?

  • At the time you write your code use all the restrictions available, i.e., use strict;, use warnings and use diagnostics so you can catch obvious mistakes early. Once you are confident that things work, you can remove the diagnostics and sometimes warnings to speed things up, or save disk space by shrinking log files. If I have to relax restrictions, I do it for the smallest blocks possible (where it is absolutely necessary) and watch these parts closely.
     
  • Use a lot of junk.pl scripts. That is, you write a few lines but before you put them into something big, you put them into some junk.pl script and test it, and test it... until you are 100% sure that they work. It is so easy to mess up a number of parentheses or forget to backslash something in the regular expressions. It is much easier to find a bug in a 10 line script than in a 1000 lines. You see... Perl (at least at this point... the perl6, Parrot, or whatever-they-will-call-it is another story) is a weakly typed language. You can assign refs to strings, strings to numbers, and pass whatever you want as parameter to a subroutine. This is GREAT!!! And this is FLEXIBLE!!! You can do incredibly smart things with perl but also incredibly stupid. And it will let you. So (I, personally) like the one-liners that in other languages take few pages, and I appreciate all the ways to calls subroutines with commas and no parentheses, etc. But I personally try to write perl code as it was C (whenever I can). Yes, it is longer, but by putting parentheses for function calls and operations, I do not have to watch precedence and association priorities and worry if something is a scalar or a list. At the end, my code may look like I have learned perl only this morning or from the book: Learn perl in 5 minutes, but my code works, and other people can read it and guess what it is doing without spending days admiring the ingenuity and trickery and encouraging me to win the Obfuscated Perl Contest.
     
  • Once your lines work, you put them into a sub and test it on all possible values and see what happens. No modules or classes yet. If you need to simulate global variables (e.g., from the encompassing block, the our or EXPORTed from other packages/modules) put the mock-ups outside the block or a routine and pretend that they are the real thing and test...
     
  • Finally, you have a sub that can be put into a module. And even if you already written a module and want to just test a single subroutine in it, use pod and just cut all stuff outside your routine with the =begin comment and the =end comment pairs (remember about blank lines around the =end comment. Then test it again with junk scripts that use or require the module. Note that perl was originally a procedural language and is still very good for this. And not everything is an object. If you want to create a class called Utilities you probably missed a point of OOD. If you have only METHODS, but no STATE (attributes), you do not have an OBJECT!!!. Use OOD when you have to... It is much slower than calling subroutines by explicit names. blessing is another layer of indirection, and since it goes through hashing, it is expensive (sorry, pseudohashes are gone for now...). Do not force OOD. Use it when you need it and not because it is cool. You may be better off with modules that collect related routines than a class that has no setters or getters (or setters-getters as it is often in perl). Even when you are writing a class you can still test your methods one-by-one by putting them into a junk.pl (there are fancy new names for this technique, I know... But we were using it 40 years ago since Algol and FORTRAN II and it still works...).
     
  • Follow the established perl style, though I myself often like to be individual. The style is there for a reason since it evolved. It means that style worked and this is why people use it. When you write your own code look at the similar code written by gurus and imitate. In this case, it is a virtue. There is a lot of excellent modules in CPAN archives. Check if something is there before discovering the wheel. Obviously, not everything is there, and we still need to write code (which is good, of course).
     
  • Writing a good pod documentation is very important for the finished product. But... While you need lots of documentation initially, like: requirements, use cases, sequence diagrams, class diagrams, and whatever, before you start writing your code, it does not make sense to make neat man pages and pod sections at the construction phase. Why? Because you will end up with documentation of what you wanted to write rather than what you actually wrote (after fifty modifications and the change requirements). However, comments at the end of line of code, or just before the line of code make sense, since when you replace the code, you also replace the comments. So, before you finish your code, put your comment at the end of (or above) any non-obvious line that is not self explanatory. Do not use plain # characters to start your comments but something unique that will never appear in the actual code or quoted text. For example: #!# blah, blah.
     
  • Use long and descriptive variable names. $n=10 is no good unless you are writing the N! routine again. The $n_employees is much better. Note... You can always obfuscate your perl code with a simple perl script before releasing it, if you are concerned with your job security. For example, pipe the MyFirstModule.pm (but make a backup of it with bk, remember?) code given above through the following short perl script (obfuscate.pl). You can cook up such a script in a minute:
    url=files/obfuscate_pl.txt
    If you are careful with the order and the names of your variables (longer variable names first!), the script resulting from:
    cat MyFirstModuleOrig.pm | ./obfuscate.pl > MyFirstModule.pm
    (that is shown below) will do the same thing as the original one (Oh.. Well... The debug information about variable names will change but the users do not see it anyhow -- try it... it works):
    rows=20;url=files/MyFirstModule_obfuscated_pm.txt
    Or course, I was kidding... This thing would get you fired. What I really wanted you to see is that comments, indentation, and long, descriptive variable names help you (and others who come after you fell...) to understand the code and debug it. Those who use emacs will see that I was using the perl-mode to write the MyFirstScript.pm (the cperl-mode is better, but I hate the pink).
     
  • If you want to write errors to STDERR (i.e,. to the /var/log/httpd/error_log file when using mod_perl) use Carp since it comes with perl (simply type man Carp or
    find /usr/lib/perl5 -name Carp.pm -exec cat {} \; | \
        pod2man | nroff -man | less
    if you want to be fancy) and read the docs. There are plenty of useful ones!!! The advantage of Carp is that it tells you where you actually croaked, not only that you died in the middle of nowhere. We all know that we will eventually die but knowing where is really exciting.
     
  • The old and proven technique of debugging is to write stuff into some debug file. It is especially important for mod_perl since you will not see your error messages easily in your terminal window, and you cannot really use STDOUT since it will break your HTML and JavaScript. In the next section we will add the MyFirstLog class to our entertainment system so you can send messages to a log file and look at what was your code doing and what were the values of your variables.
     
  • Perl debugger (you can run your script with a perl -d, i.e., with a -d option) will not help you much if you do not know what you are looking for and use it without focusing on some particular spot in your script. You will get MBytes of output (much longer than your original script) and maybe even less chance to find what is wrong. It works great when you step through a small script. But if your error happens after few thousands instructions, debugger will not help much. And beside... In mod_perl you are not in control how perl compiler is called, but you can do a lot with the junk.pl scripts.
     

Debugging is a matter of personal preference. I will list some great references about debugging later. Note however that some of them are outdated and deal with the mod_perl1 and some specific approaches need to be adopted to the mod_perl2. Moreover, some modules are not yet ported to the new mod_perl.

Dumping values into a file

If you have a simple perl script that you run from a terminal and you want to debug it, just send your message to a STDERR, STDOUT, or some file with print. However, when you debug the code that runs inside some other process (in case of mod_perl it runs inside Apache) you need to write debug stuff to the file. Moreover, since there may be several processes running simultaneously, and you have no control when they fire up, you usually need file locking, i.e., when one process writes to the end of the log file, the other process waits. There are many great loggers in CPAN that will do it for you. But since it is edutainment, I rolled out Yet Another Logger Utility so you can easily modify this piece of software without the fear that some other software on your system depends on it (as nobody of sound mind would ever use such a thing).They would use the Apache2::Log module that comes with mod_perl. The MyFirstLog.pm is listed below:

rows=20;url=files/MyFirstLog_pm.txt

Get it by clicking on the link above the textarea, save it as /var/www/mod_perl/Tests/MyFirstLog.pm, and read usage instructions embedded in this module by using the pod perl scripts that come with perl. For plain text version use:
pod2text MyFirstLog.pm | less
To have it in a man page style use:
pod2man MyFirstLog.pm | nroff -man | less
or even create an HTML version:
pod2html MyFirstLog.pm > /var/www/html/MyFirstLog.html
and look at it by going to http://localhost/MyFirstLog.html (or whatever...). Now... Let us add the logging to our MyFirstModule to see what the heck is happening with the counters. Before we do it, let us use a trick and write the log file under the DocumentRoot of our Apache, so we can view it in the browser and use [Reload] or [Refresh] button to see the latest version of it. The small problem is that the directories under DocumentRoot (i.e., /var/www/html, as it is set by default in the virgin /etc/httpd/conf/httpd.conf Apache config file -- you did not mess with it, did you?) are owned by root while the Apache runs by default as user apache. So let us make sure that Apache can write to some directory under DocumentRoot.
mkdir /var/www/html/mylog
chown apache:apache /var/www/html/mylog
Then we will add another handler to our mod_perl config file that correspond to the modified module.

url=files/ta25.txt

In the directory /var/www/mod_perl/Tests we will create a copy of the MyFirstModule.pm
cp MyFirstModule.pm My2ndModule.pm
and edit it to arrive at something like this:

rows=25;url=files/My2ndModule_pm.txt

Basically we replaced all occurrences of MyFirstModule with My2ndModule. Then we instantiated the logger object as:

my $log = Tests::MyFirstLog->new(file => '/var/www/html/mylog/log.txt',
                                 level => 'INFO');

so it will write messages to the /var/www/html/mylog/log.txt file that can be looked at with a browser by going to http://localhost/mylog/log.txt. Finally, we told the logger object to dump values to the log:

    $log->save_message('INFO', "Process Effective UID = $<\n".
                       "      global_counter = $global_counter\n".
                       "      ActionCounter = $ActionCounter\n".
                       "      FirstName = $FirstName\n".
                       "      LastName = $LastName\n");

every time the handler is called for duty by Apache (i.e., when your browser requests the http://localhost/My2ndModule). Now, we need to restart our Apache:
/etc/rc.d/init.d/httpd restart
and the fun can commence. We will first (important!!!) open the browser with a link http://localhost/My2ndModule. Then we will open a File --> New Window in the browser and ask it to show http://localhost/mylog/log.txt (or whatever...).The log should say something like:

at Dec  9 13:36:54 2006 [INFO] from: /usr/sbin/httpd [PID=18537]
   Package Tests::My2ndModule, File: /var/www/mod_perl/Tests/My2ndModule.pm,
Line: 57 
  ---> Process Effective UID = 48
      global_counter = 0
      ActionCounter = 0
      FirstName = 
      LastName = 

So far no spooks... Then we will be doing the following about 40 times:

  • Click on [SUBMIT] button in the page: http://localhost/My2ndModule (you entered the First and Last name, didn't you?)
  • Click on [Reload] (or [Refresh] button as it is called in MS-speak) and watch what has been added to the bottom of the log file.

What you get in the log file may vary, but I got the following:

rows=25;cols=75;url=files/ta27.txt

If you are a newbie, your first reaction when you look at the first few entries (especially the [PID=xxxxx] entry) should be: They are even worst than statisticians and lying through their teeth... mod_perl creates a new process for each request like the good old CGI. So what is the use for it? But when you scroll more to the value of 7 for the ActionCounter you will see that the Process Id that should be long dead ([PID=18537]) comes back from the grave (or wherever the dead processes go). The explanation comes from checking what Apaches are running on your machine. Do the following on your machine:


ps -ef | grep httpd | grep -v grep

root     18535     1  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18537 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18538 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18539 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18540 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18541 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18542 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18543 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd
apache   18544 18535  0 13:36 ?        00:00:00 /usr/sbin/httpd

In the listing above you see that there is one Apache that runs as a root (PID=18535) and has a parent process id of 1 (i.e., it was born by init at boot-up), and then you see 8 copies of /usr/sbin/httpd that run as user apache and are the children of the PID=18535 process. The root Apache process spawns the children to serve the requests and acts as a dispatcher (this is actually a tip of the iceberg... search Google for Apache documentation on MPM. Luckily, in FC6, the default is MPM prefork and Apache2 behaves like Apache1, i.e., these are children not threads. This is slower, but mod_perl does not yet take advantage of the Multi-Processing Modules (MPMs)). In this case, it grants them work in the round robin fashion, and more precisely, to the next child that is not busy with processing the request. In my case the test machine was totally idle and the result would be different if it was loaded with processing other HTTP requests. What you learned from this futile exercise is important:

  • There will be several copies of your mod_perl module running at the same time since Apache forks children (as cruel as it is) and you do not know which child will get your request (so do not send adult material).
  • That global variables do not make sense in such case and if you want to preserve state between different requests, you need to use some other mechanism.

This document is not yet finished and under constraction. Come back in a few weeks and check for new stuff. Obviously, it is growing too long so maybe when I am tired of writing more, I will split it into manageable pieces.

Suggested reading -- this will keep you busy for a while.

I can only suggest what I read (or at least paged through). There are books on mod_perl and you can find them on Amazon.com by searching for mod_perl. The must read (IMBO - In My Biased Opinion) is the Practical mod_perl by Stas Bekman and Eric Cholet published by O'Reilly in 2003. The problem is that the book mostly deals with the previous version of mod_perl, i.e., mod_perl1 that works with Apache 1.x. I have my paper copy (I am an old guy and like paper...) and you can buy the new and used ones from the Amazon.com. But the good news is that the whole book is available for free on the Internet: http://modperlbook.org/. It is a treasure chest of approaches, ideas, and solutions. Watch for the next book: mod_perl 2 User's Guide: http://www.onyxneon.com/. You should also look at the collection of PDF slides from the Stas Bekman talks at: http://stason.org/talks/
The mod_perl is a part of Apache Foundation and has its own web site: http://perl.apache.org. For the entry point to the mod_perl2 documentation go to: http://perl.apache.org/docs/2.0/user/index.html. There are chapters on

In short: tons of good stuff. I also found other info on the Web:

Jan Labanowski, Ph.D.
Computational Chemistry List, Ltd
jkl AT ccl DOT net

Modified: Mon Sep 17 00:52:26 2007 GMT
Page accessed 48449 times since Wed Nov 29 21:57:59 2006 GMT