1 --- /dev/null Fri Oct 16 06:02:02 2009 |
|
2 +++ mod_perl-2.0.4/lib/Apache2/Reload.pm Sun Nov 19 15:31:40 2006 |
|
3 @@ -0,0 +1,185 @@ |
|
4 +# Licensed to the Apache Software Foundation (ASF) under one or more |
|
5 +# contributor license agreements. See the NOTICE file distributed with |
|
6 +# this work for additional information regarding copyright ownership. |
|
7 +# The ASF licenses this file to You under the Apache License, Version 2.0 |
|
8 +# (the "License"); you may not use this file except in compliance with |
|
9 +# the License. You may obtain a copy of the License at |
|
10 +# |
|
11 +# http://www.apache.org/licenses/LICENSE-2.0 |
|
12 +# |
|
13 +# Unless required by applicable law or agreed to in writing, software |
|
14 +# distributed under the License is distributed on an "AS IS" BASIS, |
|
15 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
16 +# See the License for the specific language governing permissions and |
|
17 +# limitations under the License. |
|
18 +# |
|
19 +package Apache2::Reload; |
|
20 + |
|
21 +use strict; |
|
22 +use warnings FATAL => 'all'; |
|
23 + |
|
24 +use mod_perl2; |
|
25 + |
|
26 +our $VERSION = '0.09'; |
|
27 + |
|
28 +use Apache2::Const -compile => qw(OK); |
|
29 + |
|
30 +use Apache2::Connection; |
|
31 +use Apache2::ServerUtil; |
|
32 +use Apache2::RequestUtil; |
|
33 + |
|
34 +use ModPerl::Util (); |
|
35 + |
|
36 +use vars qw(%INCS %Stat $TouchTime); |
|
37 + |
|
38 +%Stat = ($INC{"Apache2/Reload.pm"} => time); |
|
39 + |
|
40 +$TouchTime = time; |
|
41 + |
|
42 +sub import { |
|
43 + my $class = shift; |
|
44 + my ($package, $file) = (caller)[0,1]; |
|
45 + |
|
46 + $class->register_module($package, $file); |
|
47 +} |
|
48 + |
|
49 +sub package_to_module { |
|
50 + my $package = shift; |
|
51 + $package =~ s/::/\//g; |
|
52 + $package .= ".pm"; |
|
53 + return $package; |
|
54 +} |
|
55 + |
|
56 +sub module_to_package { |
|
57 + my $module = shift; |
|
58 + $module =~ s/\//::/g; |
|
59 + $module =~ s/\.pm$//g; |
|
60 + return $module; |
|
61 +} |
|
62 + |
|
63 +sub register_module { |
|
64 + my ($class, $package, $file) = @_; |
|
65 + my $module = package_to_module($package); |
|
66 + |
|
67 + if ($file) { |
|
68 + $INCS{$module} = $file; |
|
69 + } |
|
70 + else { |
|
71 + $file = $INC{$module}; |
|
72 + return unless $file; |
|
73 + $INCS{$module} = $file; |
|
74 + } |
|
75 +} |
|
76 + |
|
77 +sub unregister_module { |
|
78 + my ($class, $package) = @_; |
|
79 + my $module = package_to_module($package); |
|
80 + delete $INCS{$module}; |
|
81 +} |
|
82 + |
|
83 +# the first argument is: |
|
84 +# $c if invoked as 'PerlPreConnectionHandler' |
|
85 +# $r if invoked as 'PerlInitHandler' |
|
86 +sub handler { |
|
87 + my $o = shift; |
|
88 + $o = $o->base_server if ref($o) eq 'Apache2::Connection'; |
|
89 + |
|
90 + my $DEBUG = ref($o) && (lc($o->dir_config("ReloadDebug") || '') eq 'on'); |
|
91 + |
|
92 + my $TouchFile = ref($o) && $o->dir_config("ReloadTouchFile"); |
|
93 + |
|
94 + my $ConstantRedefineWarnings = ref($o) && |
|
95 + (lc($o->dir_config("ReloadConstantRedefineWarnings") || '') eq 'off') |
|
96 + ? 0 : 1; |
|
97 + |
|
98 + my $TouchModules; |
|
99 + |
|
100 + if ($TouchFile) { |
|
101 + warn "Checking mtime of $TouchFile\n" if $DEBUG; |
|
102 + my $touch_mtime = (stat $TouchFile)[9] || return Apache2::Const::OK; |
|
103 + return Apache2::Const::OK unless $touch_mtime > $TouchTime; |
|
104 + $TouchTime = $touch_mtime; |
|
105 + open my $fh, $TouchFile or die "Can't open '$TouchFile': $!"; |
|
106 + $TouchModules = <$fh>; |
|
107 + chomp $TouchModules if $TouchModules; |
|
108 + } |
|
109 + |
|
110 + if (ref($o) && (lc($o->dir_config("ReloadAll") || 'on') eq 'on')) { |
|
111 + *Apache2::Reload::INCS = \%INC; |
|
112 + } |
|
113 + else { |
|
114 + *Apache2::Reload::INCS = \%INCS; |
|
115 + my $ExtraList = |
|
116 + $TouchModules || |
|
117 + (ref($o) && $o->dir_config("ReloadModules")) || |
|
118 + ''; |
|
119 + my @extra = split /\s+/, $ExtraList; |
|
120 + foreach (@extra) { |
|
121 + if (/(.*)::\*$/) { |
|
122 + my $prefix = $1; |
|
123 + $prefix =~ s/::/\//g; |
|
124 + foreach my $match (keys %INC) { |
|
125 + if ($match =~ /^\Q$prefix\E/) { |
|
126 + $Apache2::Reload::INCS{$match} = $INC{$match}; |
|
127 + } |
|
128 + } |
|
129 + } |
|
130 + else { |
|
131 + Apache2::Reload->register_module($_); |
|
132 + } |
|
133 + } |
|
134 + } |
|
135 + |
|
136 + my $ReloadDirs = ref($o) && $o->dir_config("ReloadDirectories"); |
|
137 + my @watch_dirs = split(/\s+/, $ReloadDirs||''); |
|
138 + |
|
139 + my @changed; |
|
140 + foreach my $key (sort { $a cmp $b } keys %Apache2::Reload::INCS) { |
|
141 + my $file = $Apache2::Reload::INCS{$key}; |
|
142 + |
|
143 + next unless defined $file; |
|
144 + next if @watch_dirs && !grep { $file =~ /^$_/ } @watch_dirs; |
|
145 + warn "Apache2::Reload: Checking mtime of $key\n" if $DEBUG; |
|
146 + |
|
147 + my $mtime = (stat $file)[9]; |
|
148 + |
|
149 + unless (defined($mtime) && $mtime) { |
|
150 + for (@INC) { |
|
151 + $mtime = (stat "$_/$file")[9]; |
|
152 + last if defined($mtime) && $mtime; |
|
153 + } |
|
154 + } |
|
155 + |
|
156 + warn("Apache2::Reload: Can't locate $file\n"), next |
|
157 + unless defined $mtime and $mtime; |
|
158 + |
|
159 + unless (defined $Stat{$file}) { |
|
160 + $Stat{$file} = $^T; |
|
161 + } |
|
162 + |
|
163 + if ($mtime > $Stat{$file}) { |
|
164 + push @changed, $key; |
|
165 + } |
|
166 + $Stat{$file} = $mtime; |
|
167 + } |
|
168 + |
|
169 + #First, let's unload all changed modules |
|
170 + foreach my $module (@changed) { |
|
171 + my $package = module_to_package($module); |
|
172 + ModPerl::Util::unload_package($package); |
|
173 + } |
|
174 + |
|
175 + #Then, let's reload them all, so that module dependencies can satisfy |
|
176 + #themselves in the correct order. |
|
177 + foreach my $module (@changed) { |
|
178 + my $package = module_to_package($module); |
|
179 + require $module; |
|
180 + warn("Apache2::Reload: process $$ reloading $package from $module\n") |
|
181 + if $DEBUG; |
|
182 + } |
|
183 + |
|
184 + return Apache2::Const::OK; |
|
185 +} |
|
186 + |
|
187 +1; |
|
188 +__END__ |
|
189 --- /dev/null Fri Oct 16 06:02:02 2009 |
|
190 +++ mod_perl-2.0.4/docs/api/Apache2/Reload.pod Sun Nov 19 15:32:13 2006 |
|
191 @@ -1,0 +1,380 @@ |
|
192 +=head1 NAME |
|
193 + |
|
194 +Apache2::Reload - Reload Perl Modules when Changed on Disk |
|
195 + |
|
196 +=head1 Synopsis |
|
197 + |
|
198 + # Monitor and reload all modules in %INC: |
|
199 + # httpd.conf: |
|
200 + PerlModule Apache2::Reload |
|
201 + PerlInitHandler Apache2::Reload |
|
202 + |
|
203 + # when working with protocols and connection filters |
|
204 + # PerlPreConnectionHandler Apache2::Reload |
|
205 + |
|
206 + # Reload groups of modules: |
|
207 + # httpd.conf: |
|
208 + PerlModule Apache2::Reload |
|
209 + PerlInitHandler Apache2::Reload |
|
210 + PerlSetVar ReloadAll Off |
|
211 + PerlSetVar ReloadModules "ModPerl::* Apache2::*" |
|
212 + #PerlSetVar ReloadDebug On |
|
213 + |
|
214 + # Reload a single module from within itself: |
|
215 + package My::Apache2::Module; |
|
216 + use Apache2::Reload; |
|
217 + sub handler { ... } |
|
218 + 1; |
|
219 + |
|
220 +=head1 Description |
|
221 + |
|
222 +C<Apache2::Reload> reloads modules that change on the disk. |
|
223 + |
|
224 +When Perl pulls a file via C<require>, it stores the filename in the |
|
225 +global hash C<%INC>. The next time Perl tries to C<require> the same |
|
226 +file, it sees the file in C<%INC> and does not reload from disk. This |
|
227 +module's handler can be configured to iterate over the modules in |
|
228 +C<%INC> and reload those that have changed on disk or only specific |
|
229 +modules that have registered themselves with C<Apache2::Reload>. It can |
|
230 +also do the check for modified modules, when a special touch-file has |
|
231 +been modified. |
|
232 + |
|
233 +Note that C<Apache2::Reload> operates on the current context of |
|
234 +C<@INC>. Which means, when called as a C<Perl*Handler> it will not |
|
235 +see C<@INC> paths added or removed by C<ModPerl::Registry> scripts, as |
|
236 +the value of C<@INC> is saved on server startup and restored to that |
|
237 +value after each request. In other words, if you want |
|
238 +C<Apache2::Reload> to work with modules that live in custom C<@INC> |
|
239 +paths, you should modify C<@INC> when the server is started. Besides, |
|
240 +C<'use lib'> in the startup script, you can also set the C<PERL5LIB> |
|
241 +variable in the httpd's environment to include any non-standard 'lib' |
|
242 +directories that you choose. For example, to accomplish that you can |
|
243 +include a line: |
|
244 + |
|
245 + PERL5LIB=/home/httpd/perl/extra; export PERL5LIB |
|
246 + |
|
247 +in the script that starts Apache. Alternatively, you can set this |
|
248 +environment variable in I<httpd.conf>: |
|
249 + |
|
250 + PerlSetEnv PERL5LIB /home/httpd/perl/extra |
|
251 + |
|
252 +=head2 Monitor All Modules in C<%INC> |
|
253 + |
|
254 +To monitor and reload all modules in C<%INC> at the beginning of |
|
255 +request's processing, simply add the following configuration to your |
|
256 +I<httpd.conf>: |
|
257 + |
|
258 + PerlModule Apache2::Reload |
|
259 + PerlInitHandler Apache2::Reload |
|
260 + |
|
261 +When working with connection filters and protocol modules |
|
262 +C<Apache2::Reload> should be invoked in the pre_connection stage: |
|
263 + |
|
264 + PerlPreConnectionHandler Apache2::Reload |
|
265 + |
|
266 +See also the discussion on |
|
267 +C<L<PerlPreConnectionHandler|docs::2.0::user::handlers::protocols/PerlPreConnectionHandler>>. |
|
268 + |
|
269 +=head2 Register Modules Implicitly |
|
270 + |
|
271 +To only reload modules that have registered with C<Apache2::Reload>, |
|
272 +add the following to the I<httpd.conf>: |
|
273 + |
|
274 + PerlModule Apache2::Reload |
|
275 + PerlInitHandler Apache2::Reload |
|
276 + PerlSetVar ReloadAll Off |
|
277 + # ReloadAll defaults to On |
|
278 + |
|
279 +Then any modules with the line: |
|
280 + |
|
281 + use Apache2::Reload; |
|
282 + |
|
283 +Will be reloaded when they change. |
|
284 + |
|
285 +=head2 Register Modules Explicitly |
|
286 + |
|
287 +You can also register modules explicitly in your I<httpd.conf> file |
|
288 +that you want to be reloaded on change: |
|
289 + |
|
290 + PerlModule Apache2::Reload |
|
291 + PerlInitHandler Apache2::Reload |
|
292 + PerlSetVar ReloadAll Off |
|
293 + PerlSetVar ReloadModules "My::Foo My::Bar Foo::Bar::Test" |
|
294 + |
|
295 +Note that these are split on whitespace, but the module list B<must> |
|
296 +be in quotes, otherwise Apache tries to parse the parameter list. |
|
297 + |
|
298 +The C<*> wild character can be used to register groups of files under |
|
299 +the same namespace. For example the setting: |
|
300 + |
|
301 + PerlSetVar ReloadModules "ModPerl::* Apache2::*" |
|
302 + |
|
303 +will monitor all modules under the namespaces C<ModPerl::> and |
|
304 +C<Apache2::>. |
|
305 + |
|
306 +=head2 Monitor Only Certain Sub Directories |
|
307 + |
|
308 +To reload modules only in certain directories (and their |
|
309 +subdirectories) add the following to the I<httpd.conf>: |
|
310 + |
|
311 + PerlModule Apache2::Reload |
|
312 + PerlInitHandler Apache2::Reload |
|
313 + PerlSetVar ReloadDirectories "/tmp/project1 /tmp/project2" |
|
314 + |
|
315 +You can further narrow the list of modules to be reloaded from the |
|
316 +chosen directories with C<ReloadModules> as in: |
|
317 + |
|
318 + PerlModule Apache2::Reload |
|
319 + PerlInitHandler Apache2::Reload |
|
320 + PerlSetVar ReloadDirectories "/tmp/project1 /tmp/project2" |
|
321 + PerlSetVar ReloadAll Off |
|
322 + PerlSetVar ReloadModules "MyApache2::*" |
|
323 + |
|
324 +In this configuration example only modules from the namespace |
|
325 +C<MyApache2::> found in the directories I</tmp/project1/> and |
|
326 +I</tmp/project2/> (and their subdirectories) will be reloaded. |
|
327 + |
|
328 +=head2 Special "Touch" File |
|
329 + |
|
330 +You can also declare a file, which when gets C<touch(1)>ed, causes the |
|
331 +reloads to be performed. For example if you set: |
|
332 + |
|
333 + PerlSetVar ReloadTouchFile /tmp/reload_modules |
|
334 + |
|
335 +and don't C<touch(1)> the file I</tmp/reload_modules>, the reloads |
|
336 +won't happen until you go to the command line and type: |
|
337 + |
|
338 + % touch /tmp/reload_modules |
|
339 + |
|
340 +When you do that, the modules that have been changed, will be |
|
341 +magically reloaded on the next request. This option works with any |
|
342 +mode described before. |
|
343 + |
|
344 +=head2 Unregistering a module |
|
345 + |
|
346 +In some cases, it might be necessary to explicitely stop reloading |
|
347 +a module. |
|
348 + |
|
349 + Apache2::Reload->unregister_module('Some::Module'); |
|
350 + |
|
351 +But be carefull, since unregistering a module in this way will only |
|
352 +do so for the current interpreter. This feature should be used with |
|
353 +care. |
|
354 + |
|
355 +=head1 Performance Issues |
|
356 + |
|
357 +This module is perfectly suited for a development environment. Though |
|
358 +it's possible that you would like to use it in a production |
|
359 +environment, since with C<Apache2::Reload> you don't have to restart |
|
360 +the server in order to reload changed modules during software |
|
361 +updates. Though this convenience comes at a price: |
|
362 + |
|
363 +=over |
|
364 + |
|
365 +=item * |
|
366 + |
|
367 +If the "touch" file feature is used, C<Apache2::Reload> has to stat(2) |
|
368 +the touch file on each request, which adds a slight but most likely |
|
369 +insignificant overhead to response times. Otherwise C<Apache2::Reload> |
|
370 +will stat(2) each registered module or even worse--all modules in |
|
371 +C<%INC>, which will significantly slow everything down. |
|
372 + |
|
373 +=item * |
|
374 + |
|
375 +Once the child process reloads the modules, the memory used by these |
|
376 +modules is not shared with the parent process anymore. Therefore the |
|
377 +memory consumption may grow significantly. |
|
378 + |
|
379 +=back |
|
380 + |
|
381 +Therefore doing a full server stop and restart is probably a better |
|
382 +solution. |
|
383 + |
|
384 +=head1 Debug |
|
385 + |
|
386 +If you aren't sure whether the modules that are supposed to be |
|
387 +reloaded, are actually getting reloaded, turn the debug mode on: |
|
388 + |
|
389 + PerlSetVar ReloadDebug On |
|
390 + |
|
391 +=head1 Caveats |
|
392 + |
|
393 +=head2 Problems With Reloading Modules Which Do Not Declare Their Package Name |
|
394 + |
|
395 +If you modify modules, which don't declare their C<package>, and rely on |
|
396 +C<Apache2::Reload> to reload them, you may encounter problems: i.e., |
|
397 +it'll appear as if the module wasn't reloaded when in fact it |
|
398 +was. This happens because when C<Apache2::Reload> C<require()>s such a |
|
399 +module all the global symbols end up in the C<Apache2::Reload> |
|
400 +namespace! So the module does get reloaded and you see the compile |
|
401 +time errors if there are any, but the symbols don't get imported to |
|
402 +the right namespace. Therefore the old version of the code is running. |
|
403 + |
|
404 + |
|
405 +=head2 Failing to Find a File to Reload |
|
406 + |
|
407 +C<Apache2::Reload> uses C<%INC> to find the files on the filesystem. If |
|
408 +an entry for a certain filepath in C<%INC> is relative, |
|
409 +C<Apache2::Reload> will use C<@INC> to try to resolve that relative |
|
410 +path. Now remember that mod_perl freezes the value of C<@INC> at the |
|
411 +server startup, and you can modify it only for the duration of one |
|
412 +request when you need to load some module which is not in on of the |
|
413 +C<@INC> directories. So a module gets loaded, and registered in |
|
414 +C<%INC> with a relative path. Now when C<Apache2::Reload> tries to find |
|
415 +that module to check whether it has been modified, it can't find since |
|
416 +its directory is not in C<@INC>. So C<Apache2::Reload> will silently |
|
417 +skip that module. |
|
418 + |
|
419 +You can enable the C<Debug|/Debug> mode to see what C<Apache2::Reload> |
|
420 +does behind the scenes. |
|
421 + |
|
422 + |
|
423 + |
|
424 +=head2 Problems with Scripts Running with Registry Handlers that Cache the Code |
|
425 + |
|
426 +The following problem is relevant only to registry handlers that cache |
|
427 +the compiled script. For example it concerns |
|
428 +C<L<ModPerl::Registry|docs::2.0::api::ModPerl::Registry>> but not |
|
429 +C<L<ModPerl::PerlRun|docs::2.0::api::ModPerl::PerlRun>>. |
|
430 + |
|
431 +=head3 The Problem |
|
432 + |
|
433 +Let's say that there is a module C<My::Utils>: |
|
434 + |
|
435 + #file:My/Utils.pm |
|
436 + #---------------- |
|
437 + package My::Utils; |
|
438 + BEGIN { warn __PACKAGE__ , " was reloaded\n" } |
|
439 + use base qw(Exporter); |
|
440 + @EXPORT = qw(colour); |
|
441 + sub colour { "white" } |
|
442 + 1; |
|
443 + |
|
444 +And a registry script F<test.pl>: |
|
445 + |
|
446 + #file:test.pl |
|
447 + #------------ |
|
448 + use My::Utils; |
|
449 + print "Content-type: text/plain\n\n"; |
|
450 + print "the color is " . colour(); |
|
451 + |
|
452 +Assuming that the server is running in a single mode, we request the |
|
453 +script for the first time and we get the response: |
|
454 + |
|
455 + the color is white |
|
456 + |
|
457 +Now we change F<My/Utils.pm>: |
|
458 + |
|
459 + - sub colour { "white" } |
|
460 + + sub colour { "red" } |
|
461 + |
|
462 +And issue the request again. C<Apache2::Reload> does its job and we can |
|
463 +see that C<My::Utils> was reloaded (look in the I<error_log> |
|
464 +file). However the script still returns: |
|
465 + |
|
466 + the color is white |
|
467 + |
|
468 +=head3 The Explanation |
|
469 + |
|
470 +Even though F<My/Utils.pm> was reloaded, C<ModPerl::Registry>'s cached |
|
471 +code won't run 'C<use My::Utils;>' again (since it happens only once, |
|
472 +i.e. during the compile time). Therefore the script doesn't know that |
|
473 +the subroutine reference has been changed. |
|
474 + |
|
475 +This is easy to verify. Let's change the script to be: |
|
476 + |
|
477 + #file:test.pl |
|
478 + #------------ |
|
479 + use My::Utils; |
|
480 + print "Content-type: text/plain\n\n"; |
|
481 + my $sub_int = \&colour; |
|
482 + my $sub_ext = \&My::Utils::colour; |
|
483 + print "int $sub_int\n"; |
|
484 + print "ext $sub_ext\n"; |
|
485 + |
|
486 +Issue a request, you will see something similar to: |
|
487 + |
|
488 + int CODE(0x8510af8) |
|
489 + ext CODE(0x8510af8) |
|
490 + |
|
491 +As you can see both point to the same CODE reference (meaning that |
|
492 +it's the same symbol). After modifying F<My/Utils.pm> again: |
|
493 + |
|
494 + - sub colour { "red" } |
|
495 + + sub colour { "blue" } |
|
496 + |
|
497 +and calling the script on the secondnd time, we get: |
|
498 + |
|
499 + int CODE(0x8510af8) |
|
500 + ext CODE(0x851112c) |
|
501 + |
|
502 +You can see that the internal CODE reference is not the same as the |
|
503 +external one. |
|
504 + |
|
505 +=head3 The Solution |
|
506 + |
|
507 +There are two solutions to this problem: |
|
508 + |
|
509 +Solution 1: replace C<use()> with an explicit C<require()> + |
|
510 +C<import()>. |
|
511 + |
|
512 + - use My::Utils; |
|
513 + + require My::Utils; My::Utils->import(); |
|
514 + |
|
515 +now the changed functions will be reimported on every request. |
|
516 + |
|
517 +Solution 2: remember to touch the script itself every time you change |
|
518 +the module that it requires. |
|
519 + |
|
520 +=head1 Threaded MPM and Multiple Perl Interpreters |
|
521 + |
|
522 +If you use C<Apache2::Reload> with a threaded MPM and multiple Perl |
|
523 +interpreters, the modules will be reloaded by each interpreter as they |
|
524 +are used, not every interpreters at once. Similar to mod_perl 1.0 |
|
525 +where each child has its own Perl interpreter, the modules are |
|
526 +reloaded as each child is hit with a request. |
|
527 + |
|
528 +If a module is loaded at startup, the syntax tree of each subroutine |
|
529 +is shared between interpreters (big win), but each subroutine has its |
|
530 +own padlist (where lexical my variables are stored). Once |
|
531 +C<Apache2::Reload> reloads a module, this sharing goes away and each |
|
532 +Perl interpreter will have its own copy of the syntax tree for the |
|
533 +reloaded subroutines. |
|
534 + |
|
535 + |
|
536 +=head1 Pseudo-hashes |
|
537 + |
|
538 +The short summary of this is: Don't use pseudo-hashes. They are |
|
539 +deprecated since Perl 5.8 and are removed in 5.9. |
|
540 + |
|
541 +Use an array with constant indexes. Its faster in the general case, |
|
542 +its more guaranteed, and generally, it works. |
|
543 + |
|
544 +The long summary is that some work has been done to get this module |
|
545 +working with modules that use pseudo-hashes, but it's still broken in |
|
546 +the case of a single module that contains multiple packages that all |
|
547 +use pseudo-hashes. |
|
548 + |
|
549 +So don't do that. |
|
550 + |
|
551 + |
|
552 + |
|
553 + |
|
554 +=head1 Copyright |
|
555 + |
|
556 +mod_perl 2.0 and its core modules are copyrighted under |
|
557 +The Apache Software License, Version 2.0. |
|
558 + |
|
559 + |
|
560 +=head1 Authors |
|
561 + |
|
562 +Matt Sergeant, [email protected] |
|
563 + |
|
564 +Stas Bekman (porting to mod_perl 2.0) |
|
565 + |
|
566 +A few concepts borrowed from C<Stonehenge::Reload> by Randal Schwartz |
|
567 +and C<Apache::StatINC> (mod_perl 1.x) by Doug MacEachern and Ask |
|
568 +Bjoern Hansen. |
|
569 + |
|
570 +=cut |
|
571 + |
|