57ca54a09c068951fd6b863838a170ddfba21c4a
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Engine / HTTP / Restarter.pm
1 package Catalyst::Engine::HTTP::Restarter;
2 use Moose;
3 use Moose::Util qw/find_meta/;
4 use namespace::clean -except => 'meta';
5
6 extends 'Catalyst::Engine::HTTP';
7
8 use Catalyst::Engine::HTTP::Restarter::Watcher;
9
10 around run => sub {
11     my $orig = shift;
12     my ( $self, $class, $port, $host, $options ) = @_;
13
14     $options ||= {};
15
16     # Setup restarter
17     unless ( my $restarter = fork ) {
18
19         # Prepare
20         close STDIN;
21         close STDOUT;
22
23         $self->_make_components_mutable($class);
24
25         my $watcher = Catalyst::Engine::HTTP::Restarter::Watcher->new(
26             directory => ( 
27                 $options->{restart_directory} || 
28                 File::Spec->catdir( $FindBin::Bin, '..' )
29             ),
30             follow_symlinks => $options->{follow_symlinks},
31             regex     => $options->{restart_regex},
32             delay     => $options->{restart_delay},
33         );
34
35         $host ||= '127.0.0.1';
36         while (1) {
37
38             # poll for changed files
39             my @changed_files = $watcher->watch();
40
41             # check if our parent process has died
42             exit if $^O ne 'MSWin32' and getppid == 1;
43
44             # Restart if any files have changed
45             if (@changed_files) {
46                 my $files = join ', ', @changed_files;
47                 print STDERR qq/File(s) "$files" modified, restarting\n\n/;
48
49                 require IO::Socket::INET;
50                 require HTTP::Headers;
51                 require HTTP::Request;
52
53                 my $client = IO::Socket::INET->new(
54                     PeerAddr => $host,
55                     PeerPort => $port
56                   )
57                   or die "Can't create client socket (is server running?): ",
58                   $!;
59
60                 # build the Kill request
61                 my $req =
62                   HTTP::Request->new( 'RESTART', '/',
63                     HTTP::Headers->new( 'Connection' => 'close' ) );
64                 $req->protocol('HTTP/1.0');
65
66                 $client->send( $req->as_string )
67                   or die "Can't send restart instruction: ", $!;
68                 $client->close();
69                 exit;
70             }
71         }
72     }
73
74     return $self->$orig( $class, $port, $host, $options );
75 };
76
77 # Naive way of trying to avoid Moose blowing up when you re-require components
78 # which have been made immutable.
79 sub _make_components_mutable {
80     my ($self, $class) = @_;
81
82     my @metas = map { find_meta(@_) } ($class, map { blessed($_) } values %{ $class->components });
83
84     foreach my $meta (@metas) {
85         $meta->make_mutable if $meta->is_immutable;
86     }
87 }
88
89 1;
90 __END__
91
92 =head1 NAME
93
94 Catalyst::Engine::HTTP::Restarter - Catalyst Auto-Restarting HTTP Engine
95
96 =head1 SYNOPSIS
97
98     script/myapp_server.pl -restart
99
100 =head1 DESCRIPTION
101
102 The Restarter engine will monitor files in your application for changes
103 and restart the server when any changes are detected.
104
105 =head1 METHODS
106
107 =head2 run
108
109 =head1 SEE ALSO
110
111 L<Catalyst>, L<Catalyst::Engine::HTTP>, L<Catalyst::Engine::CGI>,
112 L<Catalyst::Engine>.
113
114 =head1 AUTHORS
115
116 Catalyst Contributors, see Catalyst.pm
117
118 =head1 THANKS
119
120 Many parts are ripped out of C<HTTP::Server::Simple> by Jesse Vincent.
121
122 =head1 COPYRIGHT
123
124 This program is free software, you can redistribute it and/or modify it under
125 the same terms as Perl itself.
126
127 =cut