components/apache2-modules/mod_perl/patches/Reload.patch
author Rich Burridge <rich.burridge@oracle.com>
Tue, 01 Nov 2011 12:44:05 -0700
changeset 558 f30a27eeab26
parent 278 77b380ba9d84
permissions -rw-r--r--
7098256 libxml2-26 doesn't deliver .pyc files

--- /dev/null	Fri Oct 16 06:02:02 2009
+++ mod_perl-2.0.4/lib/Apache2/Reload.pm	Sun Nov 19 15:31:40 2006
@@ -0,0 +1,185 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+package Apache2::Reload;
+
+use strict;
+use warnings FATAL => 'all';
+
+use mod_perl2;
+
+our $VERSION = '0.09';
+
+use Apache2::Const -compile => qw(OK);
+
+use Apache2::Connection;
+use Apache2::ServerUtil;
+use Apache2::RequestUtil;
+
+use ModPerl::Util ();
+
+use vars qw(%INCS %Stat $TouchTime);
+
+%Stat = ($INC{"Apache2/Reload.pm"} => time);
+
+$TouchTime = time;
+
+sub import {
+    my $class = shift;
+    my ($package, $file) = (caller)[0,1];
+
+    $class->register_module($package, $file);
+}
+
+sub package_to_module {
+    my $package = shift;
+    $package =~ s/::/\//g;
+    $package .= ".pm";
+    return $package;
+}
+
+sub module_to_package {
+    my $module = shift;
+    $module =~ s/\//::/g;
+    $module =~ s/\.pm$//g;
+    return $module;
+}
+
+sub register_module {
+    my ($class, $package, $file) = @_;
+    my $module = package_to_module($package);
+
+    if ($file) {
+        $INCS{$module} = $file;
+    }
+    else {
+        $file = $INC{$module};
+        return unless $file;
+        $INCS{$module} = $file;
+    }
+}
+
+sub unregister_module {
+    my ($class, $package) = @_;
+    my $module = package_to_module($package);
+    delete $INCS{$module};
+}
+
+# the first argument is:
+# $c if invoked as 'PerlPreConnectionHandler'
+# $r if invoked as 'PerlInitHandler'
+sub handler {
+    my $o = shift;
+    $o = $o->base_server if ref($o) eq 'Apache2::Connection';
+
+    my $DEBUG = ref($o) && (lc($o->dir_config("ReloadDebug") || '') eq 'on');
+
+    my $TouchFile = ref($o) && $o->dir_config("ReloadTouchFile");
+
+    my $ConstantRedefineWarnings = ref($o) && 
+        (lc($o->dir_config("ReloadConstantRedefineWarnings") || '') eq 'off') 
+            ? 0 : 1;
+
+    my $TouchModules;
+
+    if ($TouchFile) {
+        warn "Checking mtime of $TouchFile\n" if $DEBUG;
+        my $touch_mtime = (stat $TouchFile)[9] || return Apache2::Const::OK;
+        return Apache2::Const::OK unless $touch_mtime > $TouchTime;
+        $TouchTime = $touch_mtime;
+        open my $fh, $TouchFile or die "Can't open '$TouchFile': $!";
+        $TouchModules = <$fh>;
+        chomp $TouchModules if $TouchModules;
+    }
+
+    if (ref($o) && (lc($o->dir_config("ReloadAll") || 'on') eq 'on')) {
+        *Apache2::Reload::INCS = \%INC;
+    }
+    else {
+        *Apache2::Reload::INCS = \%INCS;
+        my $ExtraList = 
+                $TouchModules || 
+                (ref($o) && $o->dir_config("ReloadModules")) || 
+                '';
+        my @extra = split /\s+/, $ExtraList;
+        foreach (@extra) {
+            if (/(.*)::\*$/) {
+                my $prefix = $1;
+                $prefix =~ s/::/\//g;
+                foreach my $match (keys %INC) {
+                    if ($match =~ /^\Q$prefix\E/) {
+                        $Apache2::Reload::INCS{$match} = $INC{$match};
+                    }
+                }
+            }
+            else {
+                Apache2::Reload->register_module($_);
+            }
+        }
+    }
+
+    my $ReloadDirs = ref($o) && $o->dir_config("ReloadDirectories");
+    my @watch_dirs = split(/\s+/, $ReloadDirs||'');
+    
+    my @changed;
+    foreach my $key (sort { $a cmp $b } keys %Apache2::Reload::INCS) {
+        my $file = $Apache2::Reload::INCS{$key};
+
+        next unless defined $file;
+        next if @watch_dirs && !grep { $file =~ /^$_/ } @watch_dirs;
+        warn "Apache2::Reload: Checking mtime of $key\n" if $DEBUG;
+
+        my $mtime = (stat $file)[9];
+
+        unless (defined($mtime) && $mtime) {
+            for (@INC) {
+                $mtime = (stat "$_/$file")[9];
+                last if defined($mtime) && $mtime;
+            }
+        }
+
+        warn("Apache2::Reload: Can't locate $file\n"), next
+            unless defined $mtime and $mtime;
+
+        unless (defined $Stat{$file}) {
+            $Stat{$file} = $^T;
+        }
+
+        if ($mtime > $Stat{$file}) {
+            push @changed, $key;
+        }
+        $Stat{$file} = $mtime;
+    }
+    
+    #First, let's unload all changed modules
+    foreach my $module (@changed) {
+        my $package = module_to_package($module);
+        ModPerl::Util::unload_package($package);
+    }
+    
+    #Then, let's reload them all, so that module dependencies can satisfy
+    #themselves in the correct order.
+    foreach my $module (@changed) {
+        my $package = module_to_package($module);
+        require $module;
+        warn("Apache2::Reload: process $$ reloading $package from $module\n")
+            if $DEBUG;
+    }
+
+    return Apache2::Const::OK;
+}
+
+1;
+__END__
--- /dev/null	Fri Oct 16 06:02:02 2009
+++ mod_perl-2.0.4/docs/api/Apache2/Reload.pod	Sun Nov 19 15:32:13 2006
@@ -1,0 +1,380 @@
+=head1 NAME
+
+Apache2::Reload - Reload Perl Modules when Changed on Disk
+
+=head1 Synopsis
+
+  # Monitor and reload all modules in %INC:
+  # httpd.conf:
+  PerlModule Apache2::Reload
+  PerlInitHandler Apache2::Reload
+
+  # when working with protocols and connection filters
+  # PerlPreConnectionHandler Apache2::Reload
+
+  # Reload groups of modules:
+  # httpd.conf:
+  PerlModule Apache2::Reload
+  PerlInitHandler Apache2::Reload
+  PerlSetVar ReloadAll Off
+  PerlSetVar ReloadModules "ModPerl::* Apache2::*"
+  #PerlSetVar ReloadDebug On
+  
+  # Reload a single module from within itself:
+  package My::Apache2::Module;
+  use Apache2::Reload;
+  sub handler { ... }
+  1;
+
+=head1 Description
+
+C<Apache2::Reload> reloads modules that change on the disk.
+
+When Perl pulls a file via C<require>, it stores the filename in the
+global hash C<%INC>.  The next time Perl tries to C<require> the same
+file, it sees the file in C<%INC> and does not reload from disk.  This
+module's handler can be configured to iterate over the modules in
+C<%INC> and reload those that have changed on disk or only specific
+modules that have registered themselves with C<Apache2::Reload>. It can
+also do the check for modified modules, when a special touch-file has
+been modified.
+
+Note that C<Apache2::Reload> operates on the current context of
+C<@INC>.  Which means, when called as a C<Perl*Handler> it will not
+see C<@INC> paths added or removed by C<ModPerl::Registry> scripts, as
+the value of C<@INC> is saved on server startup and restored to that
+value after each request.  In other words, if you want
+C<Apache2::Reload> to work with modules that live in custom C<@INC>
+paths, you should modify C<@INC> when the server is started.  Besides,
+C<'use lib'> in the startup script, you can also set the C<PERL5LIB>
+variable in the httpd's environment to include any non-standard 'lib'
+directories that you choose.  For example, to accomplish that you can
+include a line:
+
+  PERL5LIB=/home/httpd/perl/extra; export PERL5LIB
+
+in the script that starts Apache. Alternatively, you can set this
+environment variable in I<httpd.conf>:
+
+  PerlSetEnv PERL5LIB /home/httpd/perl/extra
+
+=head2 Monitor All Modules in C<%INC>
+
+To monitor and reload all modules in C<%INC> at the beginning of
+request's processing, simply add the following configuration to your
+I<httpd.conf>:
+
+  PerlModule Apache2::Reload
+  PerlInitHandler Apache2::Reload
+
+When working with connection filters and protocol modules
+C<Apache2::Reload> should be invoked in the pre_connection stage:
+
+  PerlPreConnectionHandler Apache2::Reload
+
+See also the discussion on
+C<L<PerlPreConnectionHandler|docs::2.0::user::handlers::protocols/PerlPreConnectionHandler>>.
+
+=head2 Register Modules Implicitly
+
+To only reload modules that have registered with C<Apache2::Reload>,
+add the following to the I<httpd.conf>:
+
+  PerlModule Apache2::Reload
+  PerlInitHandler Apache2::Reload
+  PerlSetVar ReloadAll Off
+  # ReloadAll defaults to On
+
+Then any modules with the line:
+
+  use Apache2::Reload;
+
+Will be reloaded when they change.
+
+=head2 Register Modules Explicitly
+
+You can also register modules explicitly in your I<httpd.conf> file
+that you want to be reloaded on change:
+
+  PerlModule Apache2::Reload
+  PerlInitHandler Apache2::Reload
+  PerlSetVar ReloadAll Off
+  PerlSetVar ReloadModules "My::Foo My::Bar Foo::Bar::Test"
+
+Note that these are split on whitespace, but the module list B<must>
+be in quotes, otherwise Apache tries to parse the parameter list.
+
+The C<*> wild character can be used to register groups of files under
+the same namespace. For example the setting:
+
+  PerlSetVar ReloadModules "ModPerl::* Apache2::*"
+
+will monitor all modules under the namespaces C<ModPerl::> and
+C<Apache2::>.
+
+=head2 Monitor Only Certain Sub Directories
+
+To reload modules only in certain directories (and their
+subdirectories) add the following to the I<httpd.conf>:
+
+  PerlModule Apache2::Reload
+  PerlInitHandler Apache2::Reload
+  PerlSetVar ReloadDirectories "/tmp/project1 /tmp/project2"
+
+You can further narrow the list of modules to be reloaded from the
+chosen directories with C<ReloadModules> as in:
+
+  PerlModule Apache2::Reload
+  PerlInitHandler Apache2::Reload
+  PerlSetVar ReloadDirectories "/tmp/project1 /tmp/project2"
+  PerlSetVar ReloadAll Off
+  PerlSetVar ReloadModules "MyApache2::*"
+
+In this configuration example only modules from the namespace
+C<MyApache2::> found in the directories I</tmp/project1/> and
+I</tmp/project2/> (and their subdirectories) will be reloaded.
+
+=head2 Special "Touch" File
+
+You can also declare a file, which when gets C<touch(1)>ed, causes the
+reloads to be performed. For example if you set:
+
+  PerlSetVar ReloadTouchFile /tmp/reload_modules
+
+and don't C<touch(1)> the file I</tmp/reload_modules>, the reloads
+won't happen until you go to the command line and type:
+
+  % touch /tmp/reload_modules
+
+When you do that, the modules that have been changed, will be
+magically reloaded on the next request. This option works with any
+mode described before.
+
+=head2 Unregistering a module
+
+In some cases, it might be necessary to explicitely stop reloading
+a module.
+
+  Apache2::Reload->unregister_module('Some::Module');
+
+But be carefull, since unregistering a module in this way will only
+do so for the current interpreter. This feature should be used with
+care.
+
+=head1 Performance Issues
+
+This module is perfectly suited for a development environment. Though
+it's possible that you would like to use it in a production
+environment, since with C<Apache2::Reload> you don't have to restart
+the server in order to reload changed modules during software
+updates. Though this convenience comes at a price:
+
+=over
+
+=item *
+
+If the "touch" file feature is used, C<Apache2::Reload> has to stat(2)
+the touch file on each request, which adds a slight but most likely
+insignificant overhead to response times. Otherwise C<Apache2::Reload>
+will stat(2) each registered module or even worse--all modules in
+C<%INC>, which will significantly slow everything down.
+
+=item *
+
+Once the child process reloads the modules, the memory used by these
+modules is not shared with the parent process anymore. Therefore the
+memory consumption may grow significantly.
+
+=back
+
+Therefore doing a full server stop and restart is probably a better
+solution.
+
+=head1 Debug
+
+If you aren't sure whether the modules that are supposed to be
+reloaded, are actually getting reloaded, turn the debug mode on:
+
+  PerlSetVar ReloadDebug On
+
+=head1 Caveats
+
+=head2 Problems With Reloading Modules Which Do Not Declare Their Package Name
+
+If you modify modules, which don't declare their C<package>, and rely on
+C<Apache2::Reload> to reload them, you may encounter problems: i.e.,
+it'll appear as if the module wasn't reloaded when in fact it
+was. This happens because when C<Apache2::Reload> C<require()>s such a
+module all the global symbols end up in the C<Apache2::Reload>
+namespace!  So the module does get reloaded and you see the compile
+time errors if there are any, but the symbols don't get imported to
+the right namespace. Therefore the old version of the code is running.
+
+
+=head2 Failing to Find a File to Reload
+
+C<Apache2::Reload> uses C<%INC> to find the files on the filesystem. If
+an entry for a certain filepath in C<%INC> is relative,
+C<Apache2::Reload> will use C<@INC> to try to resolve that relative
+path. Now remember that mod_perl freezes the value of C<@INC> at the
+server startup, and you can modify it only for the duration of one
+request when you need to load some module which is not in on of the
+C<@INC> directories. So a module gets loaded, and registered in
+C<%INC> with a relative path. Now when C<Apache2::Reload> tries to find
+that module to check whether it has been modified, it can't find since
+its directory is not in C<@INC>. So C<Apache2::Reload> will silently
+skip that module.
+
+You can enable the C<Debug|/Debug> mode to see what C<Apache2::Reload>
+does behind the scenes.
+
+
+
+=head2 Problems with Scripts Running with Registry Handlers that Cache the Code
+
+The following problem is relevant only to registry handlers that cache
+the compiled script. For example it concerns
+C<L<ModPerl::Registry|docs::2.0::api::ModPerl::Registry>> but not
+C<L<ModPerl::PerlRun|docs::2.0::api::ModPerl::PerlRun>>.
+
+=head3 The Problem
+
+Let's say that there is a module C<My::Utils>:
+
+  #file:My/Utils.pm
+  #----------------
+  package My::Utils;
+  BEGIN { warn __PACKAGE__ , " was reloaded\n" }
+  use base qw(Exporter);
+  @EXPORT = qw(colour);
+  sub colour { "white" }
+  1;
+
+And a registry script F<test.pl>:
+
+  #file:test.pl
+  #------------
+  use My::Utils;
+  print "Content-type: text/plain\n\n";
+  print "the color is " . colour();
+
+Assuming that the server is running in a single mode, we request the
+script for the first time and we get the response:
+
+  the color is white
+
+Now we change F<My/Utils.pm>:
+
+  -  sub colour { "white" }
+  +  sub colour { "red" }
+
+And issue the request again. C<Apache2::Reload> does its job and we can
+see that C<My::Utils> was reloaded (look in the I<error_log>
+file). However the script still returns:
+
+  the color is white
+
+=head3 The Explanation
+
+Even though F<My/Utils.pm> was reloaded, C<ModPerl::Registry>'s cached
+code won't run 'C<use My::Utils;>' again (since it happens only once,
+i.e. during the compile time). Therefore the script doesn't know that
+the subroutine reference has been changed.
+
+This is easy to verify. Let's change the script to be:
+
+  #file:test.pl
+  #------------
+  use My::Utils;
+  print "Content-type: text/plain\n\n";
+  my $sub_int = \&colour;
+  my $sub_ext = \&My::Utils::colour;
+  print "int $sub_int\n";
+  print "ext $sub_ext\n";
+
+Issue a request, you will see something similar to:
+
+  int CODE(0x8510af8)
+  ext CODE(0x8510af8)
+
+As you can see both point to the same CODE reference (meaning that
+it's the same symbol). After modifying F<My/Utils.pm> again:
+
+  -  sub colour { "red" }
+  +  sub colour { "blue" }
+
+and calling the script on the secondnd time, we get:
+
+  int CODE(0x8510af8)
+  ext CODE(0x851112c)
+
+You can see that the internal CODE reference is not the same as the
+external one.
+
+=head3 The Solution
+
+There are two solutions to this problem:
+
+Solution 1: replace C<use()> with an explicit C<require()> +
+C<import()>.
+
+ - use My::Utils;
+ + require My::Utils; My::Utils->import();
+
+now the changed functions will be reimported on every request.
+
+Solution 2: remember to touch the script itself every time you change
+the module that it requires.
+
+=head1 Threaded MPM and Multiple Perl Interpreters
+
+If you use C<Apache2::Reload> with a threaded MPM and multiple Perl
+interpreters, the modules will be reloaded by each interpreter as they
+are used, not every interpreters at once.  Similar to mod_perl 1.0
+where each child has its own Perl interpreter, the modules are
+reloaded as each child is hit with a request.
+
+If a module is loaded at startup, the syntax tree of each subroutine
+is shared between interpreters (big win), but each subroutine has its
+own padlist (where lexical my variables are stored).  Once
+C<Apache2::Reload> reloads a module, this sharing goes away and each
+Perl interpreter will have its own copy of the syntax tree for the
+reloaded subroutines.
+
+
+=head1 Pseudo-hashes
+
+The short summary of this is: Don't use pseudo-hashes. They are
+deprecated since Perl 5.8 and are removed in 5.9.
+
+Use an array with constant indexes. Its faster in the general case,
+its more guaranteed, and generally, it works.
+
+The long summary is that some work has been done to get this module
+working with modules that use pseudo-hashes, but it's still broken in
+the case of a single module that contains multiple packages that all
+use pseudo-hashes.
+
+So don't do that.
+
+
+
+
+=head1 Copyright
+
+mod_perl 2.0 and its core modules are copyrighted under
+The Apache Software License, Version 2.0.
+
+
+=head1 Authors
+
+Matt Sergeant, [email protected]
+
+Stas Bekman (porting to mod_perl 2.0)
+
+A few concepts borrowed from C<Stonehenge::Reload> by Randal Schwartz
+and C<Apache::StatINC> (mod_perl 1.x) by Doug MacEachern and Ask
+Bjoern Hansen.
+
+=cut
+