You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@perl.apache.org by Fred Moyer <fr...@taperfriendlymusic.org> on 2007/12/06 21:59:24 UTC

[patch] Apache::Reload work from Apachecon

Greetings,

I have attached the work completed for Apache::Reload up to this point. 
  A great deal of progress was made at ApacheCon thanks to help from 
Gozer, Geoff, and others.

There are some files that I will need to 'svn mv' from mp core and then 
modify, but this diff is the bulk of what I plan to commit.

Comments welcome.  One note, because Apache2::Reload is v0.9 in core, 
and Apache::Reload is v0.8, I have marked this version 0.10 to avoid any 
issues installing, see Changes for details.

If all goes well I hope to have this released to CPAN by the end of the 
month.

- Fred


Index: RELEASE
===================================================================
--- RELEASE	(revision 558376)
+++ RELEASE	(working copy)
@@ -40,8 +40,8 @@

    a. edit ./Changes:
       - find lib -type f -name "*.pm" | \
-         xargs perl -pi -e 's,0.08-dev,0.08-rc1,g'
-     - perl -pi -e 's,0.08-dev,0.08-rc1,g' Changes
+         xargs perl -pi -e 's,0.10-dev,0.10-rc1,g'
+     - perl -pi -e 's,0.10-dev,0.10-rc1,g' Changes

       - don't commit these (see dev@ archives)

@@ -60,7 +60,7 @@
       o dev/perl.apache.org
       o modperl/perl.apache.org

-  Subject: [RELEASE CANDIDATE] Apache-Reload 0.08 RC\d+
+  Subject: [RELEASE CANDIDATE] Apache-Reload 0.10 RC\d+

     (or maybe longer to give most people a chance to catch up). no need
     to tag this package
@@ -95,7 +95,7 @@
  5. Announce the package

    a. post ... to the modperl, announce lists
-  Subject: [ANNOUNCE] Apache-Reload 0.08
+  Subject: [ANNOUNCE] Apache-Reload 0.10
       include
    - MD5 sig (as it comes from CPAN upload announce).
    - the latest Changes
@@ -107,7 +107,7 @@
    b. edit ./Changes:
       - start a new item with incremented version + '-dev'

-  =item 0.92-dev
+  =item 0.11-dev

    c. bump up version numbers in this file to make it easier to do the
       next release.
@@ -115,4 +115,4 @@
       $ perl -pi -e 's/(\d+)\.(\d+)/join(".", $1, $2+1)/eg' RELEASE

    d. commit Changes
-     % svn ci -m "start 0.92-dev cycle" Changes RELEASE 
lib/Apache/Reload.pm
+     % svn ci -m "start 0.11-dev cycle" Changes RELEASE 
lib/Apache/Reload.pm
Index: t/conf/extra.last.conf.in
===================================================================
--- t/conf/extra.last.conf.in	(revision 0)
+++ t/conf/extra.last.conf.in	(revision 0)
@@ -0,0 +1,22 @@
+<IfModule mod_perl.c>
+    <IfDefine APACHE2>
+        PerlModule Apache2::Reload
+        PerlModule Apache2::TestReload
+    </IfDefine>
+    <IfDefine APACHE1>
+        PerlModule Apache::Reload
+        PerlModule Apache::TestReload
+    </IfDefine>
+    </IfDefine>
+    <Location /reload>
+        SetHandler perl-script
+        <IfDefine APACHE2>
+            PerlInitHandler Apache2::Reload
+            PerlResponseHandler Apache2::TestReload
+        </IfDefine>
+        <IfDefine APACHE1>
+            PerlInitHandler Apache::Reload
+            PerlHandler Apache::TestReload
+        </IfDefine>
+    </Location>
+</IfModule>
Index: t/lib/Apache/TestReload.pm
===================================================================
--- t/lib/Apache/TestReload.pm	(revision 0)
+++ t/lib/Apache/TestReload.pm	(revision 0)
@@ -0,0 +1,44 @@
+package Apache::TestReload;
+
+use strict;
+use warnings FATAL => 'all';
+
+#use ModPerl::Util ();
+use Apache::Constants qw(:common);
+
+my $package = 'Reload::Test';
+
+our $pass = 0;
+
+sub handler {
+    my $r = shift;
+    $pass++;
+    $r->send_http_header('text/plain');
+    if ((defined ($r->args)) && ($r->args eq 'last')) {
+        #Apache2::Reload->unregister_module($package);
+        #ModPerl::Util::unload_package($package);
+        $pass = 0;
+        $r->print("unregistered OK");
+        return OK;
+    }
+
+    eval "require $package";
+
+    Reload::Test::run($r);
+
+    return OK;
+}
+
+# This one shouldn't be touched
+package Reload::Test::SubPackage;
+
+sub subpackage {
+    if ($Apache::TestReload::pass == '2') {
+        return 'SUBPACKAGE';
+    }
+    else {
+        return 'subpackage';
+    }
+}
+
+1;
Index: t/lib/Apache2/TestReload.pm
===================================================================
--- t/lib/Apache2/TestReload.pm	(revision 0)
+++ t/lib/Apache2/TestReload.pm	(revision 0)
@@ -0,0 +1,45 @@
+package Apache2::TestReload;
+
+use strict;
+use warnings FATAL => 'all';
+
+use ModPerl::Util ();
+use Apache2::RequestRec ();
+use Apache2::Const -compile => qw(OK);
+use Apache2::RequestIO ();
+
+my $package = 'Reload::Test';
+
+our $pass = 0;
+
+sub handler {
+    my $r = shift;
+    $pass++;
+    if ($r->args eq 'last') {
+        Apache2::Reload->unregister_module($package);
+        ModPerl::Util::unload_package($package);
+        $pass = 0;
+        $r->print("unregistered OK");
+        return Apache2::Const::OK;
+    }
+
+    eval "require $package";
+
+    Reload::Test::run($r);
+
+    return Apache2::Const::OK;
+}
+
+# This one shouldn't be touched
+package Reload::Test::SubPackage;
+
+sub subpackage {
+    if ($Apache2::TestReload::pass == '2') {
+        return 'SUBPACKAGE';
+    }
+    else {
+        return 'subpackage';
+    }
+}
+
+1;
Index: t/reload.t
===================================================================
--- t/reload.t	(revision 0)
+++ t/reload.t	(revision 0)
@@ -0,0 +1,70 @@
+use strict;
+use warnings FATAL => 'all';
+
+use Apache::Test;
+use Apache::TestUtil;
+use Apache::TestRequest;
+use File::Spec::Functions qw(catfile tmpdir);
+
+Apache::TestRequest::user_agent(keep_alive => 1);
+
+plan tests => 3, need 'HTML::HeadParser';
+
+my $test_file = catfile qw(Reload Test.pm);
+
+my $location = '/reload';
+
+my @tests = qw(const prototype simple subpackage);
+
+my $header = join '', <DATA>;
+
+my $initial = <<'EOF';
+sub simple { 'simple' }
+use constant const => 'const';
+sub prototype($) { 'prototype' }
+sub promised;
+EOF
+
+my $modified = <<'EOF';
+sub simple { 'SIMPLE' }
+use constant const => 'CONST';
+sub prototype($$) { 'PROTOTYPE' }
+EOF
+
+t_write_test_lib($test_file, $header, $initial);
+
+{
+    my $expected = join '', map { "$_:$_\n" } sort @tests;
+    my $received = GET $location;
+    ok t_cmp($received->content, $expected, 'Initial');
+}
+
+t_write_test_lib($test_file, $header, $modified);
+
+{
+    my $expected = join '', map { "$_:" . uc($_) . "\n" } sort @tests;
+    my $received = GET $location;
+    ok t_cmp($received->content, $expected, 'Reload');
+}
+
+{
+    my $expected = "unregistered OK";
+    my $received = GET "$location?last";
+    ok t_cmp($received->content, $expected, 'Unregister');
+}
+
+__DATA__
+package Reload::Test;
+
+our @methods = qw(const prototype simple subpackage);
+
+sub subpackage { return Reload::Test::SubPackage::subpackage() }
+
+sub run {
+    my $r = shift;
+    foreach my $m (sort @methods) {
+        $r->print($m, ':', __PACKAGE__->$m(), "\n");
+    }
+}
+
+1;
Index: lib/Apache/Reload.pm
===================================================================
--- lib/Apache/Reload.pm	(revision 558376)
+++ lib/Apache/Reload.pm	(working copy)
@@ -17,7 +17,7 @@

  use strict;

-$Apache::Reload::VERSION = '0.08';
+$Apache::Reload::VERSION = '0.10';

  use vars qw(%INCS %Stat $TouchTime %UndefFields);

@@ -262,6 +262,10 @@

  Matt Sergeant, matt@sergeant.org

+=head1 MAINTAINERS
+
+the mod_perl developers, dev@perl.apache.org
+
  =head1 SEE ALSO

  Apache::StatINC, Stonehenge::Reload
Index: lib/Apache2/Reload.pm
===================================================================
--- lib/Apache2/Reload.pm	(revision 0)
+++ lib/Apache2/Reload.pm	(revision 0)
@@ -0,0 +1,297 @@
+# 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.10';
+
+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__
+
+=head1 NAME
+
+Apache2::Reload - Reload changed modules
+
+=head1 SYNOPSIS
+
+In httpd.conf:
+
+  PerlInitHandler Apache2::Reload
+  PerlSetVar ReloadAll Off
+
+Then your module:
+
+  package My::Apache::Module;
+
+  use Apache2::Reload;
+
+  sub handler { ... }
+
+  1;
+
+=head1 DESCRIPTION
+
+This module is two things. First it is an adaptation of Randal
+Schwartz's Stonehenge::Reload module that attempts to be a little
+more intuitive and makes the usage easier. Stonehenge::Reload was
+written by Randal to make specific modules reload themselves when
+they changed. Unlike Apache::StatINC, Stonehenge::Reload only checked
+the change time of modules that registered themselves with
+Stonehenge::Reload, thus reducing stat() calls. Apache::Reload also
+offers the exact same functionality as Apache::StatINC, and is thus
+designed to be a drop-in replacement. Apache::Reload only checks modules
+that register themselves with Apache::Reload if you explicitly turn off
+the StatINC emulation method (see below). Like Apache::StatINC,
+Apache::Reload must be installed as an Init Handler.
+
+=head2 StatINC Replacement
+
+To use as a StatINC replacement, simply add the following configuration
+to your httpd.conf:
+
+  PerlInitHandler Apache::Reload
+
+=head2 Register Modules Implicitly
+
+To only reload modules that have registered with Apache::Reload,
+add the following to the httpd.conf:
+
+  PerlInitHandler Apache::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 httpd.conf file that
+you want to be reloaded on change:
+
+  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.
+
+=head2 Special "Touch" File
+
+You can also set a file that you can touch() that causes the reloads to be
+performed. If you set this, and don't touch() the file, the reloads don't
+happen. This can be a great boon in a live environment:
+
+  PerlSetVar ReloadTouchFile /tmp/reload_modules
+
+Now when you're happy with your changes, simply go to the command line and
+type:
+
+  touch /tmp/reload_modules
+
+And your modules will be magically reloaded on the next request. This 
option
+works in both StatINC emulation mode and the registered modules mode.
+
+=head1 PSUEDOHASHES
+
+The short summary of this is: Don't use psuedohashes. Use an array with
+constant indexes. Its faster in the general case, its more guaranteed, and
+generally, it works.
+
+The long summary is that I've done some work to get this working with
+modules that use psuedo hashes, but its still broken in the case of a
+single module that contains multiple packages that all use psuedohashes.
+
+So don't do that.
+
+=head1 AUTHOR
+
+Matt Sergeant, matt@sergeant.org
+
+=head1 MAINTAINERS
+
+the mod_perl developers, dev@perl.apache.org
+
+=head1 SEE ALSO
+
+Apache::StatINC, Stonehenge::Reload
+
+=cut
Index: MANIFEST
===================================================================
--- MANIFEST	(revision 558376)
+++ MANIFEST	(working copy)
@@ -3,4 +3,9 @@
  README
  LICENSE
  lib/Apache/Reload.pm
+lib/Apache2/Reload.pm
+t/reload.t
+t/lib/Apache/TestReload.pm
+t/lib/Apache2/TestReload.pm
  RELEASE
+Changes
Index: Makefile.PL
===================================================================
--- Makefile.PL	(revision 558376)
+++ Makefile.PL	(working copy)
@@ -38,9 +38,9 @@
      require ModPerl::MM;
      ModPerl::MM::WriteMakefile(
                                 %common_opts,
-                               VERSION_FROM    => "lib/Apache/Reload.pm",
-                               NAME            => "Apache::Reload",
-                               ABSTRACT_FROM   => 'lib/Apache/Reload.pm',
+                               VERSION_FROM    => "lib/Apache2/Reload.pm",
+                               NAME            => "Apache2::Reload",
+                               ABSTRACT_FROM   => 'lib/Apache2/Reload.pm',
                                );
  }

Index: Changes
===================================================================
--- Changes	(revision 558376)
+++ Changes	(working copy)
@@ -6,8 +6,18 @@

  =over 1

-=item 0.08-dev
+=item 0.10-dev

+Apache::Reload and Apache2::Reload bundled for CPAN release
+This release incorporates unreleased changes in 0.08 and 0.09
+[Fred Moyer <fr...@redhotpenguin.com>]
+
+=item 0.09
+
+Apache2::Reload was part of mod_perl2 core
+
+=item 0.08
+
  Remove modified modules before reloading them
  [Javier Ureuen Val]

Index: README
===================================================================
--- README	(revision 558376)
+++ README	(working copy)
@@ -101,6 +101,10 @@
  AUTHOR
      Matt Sergeant, matt@sergeant.org

+MAINTAINERS
+
+the mod_perl developers, dev@perl.apache.org
+
  SEE ALSO
      Apache::StatINC, Stonehenge::Reload


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@perl.apache.org
For additional commands, e-mail: dev-help@perl.apache.org


Re: [patch] Apache::Reload work from Apachecon

Posted by Fred Moyer <fr...@taperfriendlymusic.org>.
Philip M. Gollucci wrote:
>> Comments welcome.  One note, because Apache2::Reload is v0.9 in core, 
>> and Apache::Reload is v0.8, I have marked this version 0.10 to avoid 
>> any issues installing, see Changes for details.
> You did the same thing I did a while ago, .1 < .8 or .9.  You are 
> looking for .99 or something.

Ah it turns out I was wrong in my explanation.  It was 0.08 and 0.09, 
not 0.8 and 0.9.

I've made the necessary svn mv commands to bring the necessary files 
over from the mod_perl2 repo to Apache::Reload.  Same patches as before, 
except I'm using docs/api/Apache2/Reload.pod as the pod for Apache2::Reload.

I'll commit this stuff in the next few days unless anyone has any other 
comments.

original thread:

http://marc.info/?l=apache-modperl-dev&m=119697479518157&w=2

current svn:

phred@pooky ~/dev/svn/modperl/mod_perl-2.0 $ svn status
X      docs
D      t/response/TestModules/reload.pm
D      t/modules/reload.t
D      lib/Apache2/Reload.pm

Performing status on external item at 'docs'

phred@pooky ~/dev/svn/modperl/Apache-Reload/trunk $ svn status
M      RELEASE
A      t/conf
A      t/conf/extra.last.conf.in
A      t/lib
A      t/lib/Apache
A      t/lib/Apache/TestReload.pm
A      t/lib/Apache2
A  +   t/lib/Apache2/TestReload.pm
A  +   t/reload.t
M      lib/Apache/Reload.pm
A      lib/Apache2
A  +   lib/Apache2/Reload.pm
M      MANIFEST
M      Makefile.PL
M      Changes
M      README


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@perl.apache.org
For additional commands, e-mail: dev-help@perl.apache.org


Re: [patch] Apache::Reload work from Apachecon

Posted by Fred Moyer <fr...@taperfriendlymusic.org>.
Philip M. Gollucci wrote:
>> Comments welcome.  One note, because Apache2::Reload is v0.9 in core, 
>> and Apache::Reload is v0.8, I have marked this version 0.10 to avoid 
>> any issues installing, see Changes for details.
> You did the same thing I did a while ago, .1 < .8 or .9.  You are 
> looking for .99 or something.

Ah thanks for the spot, I will fix that :)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@perl.apache.org
For additional commands, e-mail: dev-help@perl.apache.org


Re: [patch] Apache::Reload work from Apachecon

Posted by "Philip M. Gollucci" <pg...@p6m7g8.com>.
> Comments welcome.  One note, because Apache2::Reload is v0.9 in core, and 
> Apache::Reload is v0.8, I have marked this version 0.10 to avoid any issues 
> installing, see Changes for details.
You did the same thing I did a while ago, .1 < .8 or .9.  You are looking 
for .99 or something.

-- 
------------------------------------------------------------------------
Philip M. Gollucci (philip@ridecharge.com)
o:703.549.2050x206
Senior System Admin - Riderway, Inc.
http://riderway.com / http://ridecharge.com
1024D/EC88A0BF 0DE5 C55C 6BF3 B235 2DAB  B89E 1324 9B4F EC88 A0BF

Work like you don't need the money,
love like you'll never get hurt,
and dance like nobody's watching.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@perl.apache.org
For additional commands, e-mail: dev-help@perl.apache.org