/usr/local/CPAN/PITA/PITA/Guest/Server.pm
package PITA::Guest::Server;
use 5.008;
use strict;
use Params::Util 1.00 ();
use POE::Wheel::Run 1.299 ();
use POE::Declare::HTTP::Server 0.03 ();
use PITA::Guest::Server::HTTP ();
our $VERSION = '0.50';
use POE::Declare 0.53 {
Hostname => 'Param',
Port => 'Param',
Program => 'Param',
Uploads => 'Param',
Mirrors => 'Param',
StartupEvent => 'Message',
ShutdownEvent => 'Message',
pinged => 'Attribute',
uploaded => 'Attribute',
mirrored => 'Attribute',
status => 'Internal',
http => 'Internal',
child => 'Internal',
};
use constant {
STOPPED => 1,
STARTING => 1,
RUNNING => 1,
STOPPING => 1,
};
######################################################################
# Constructor and Accessors
sub new {
my $self = shift->SUPER::new(@_);
# Set up tracking variables
$self->{status} = STOPPED;
$self->{pinged} = 0;
$self->{mirrored} = [ ];
$self->{uploaded} = [ ];
# Check params
unless ( Params::Util::_ARRAY($self->Program) ) {
die "Missing or invalid 'Program' param";
}
# Create the web server
$self->{http} = PITA::Guest::Server::HTTP->new(
Hostname => $self->Hostname,
Port => $self->Port,
Mirrors => $self->Mirrors,
StartupEvent => $self->lookback('http_startup_event'),
StartupError => $self->lookback('http_startup_error'),
ShutdownEvent => $self->lookback('http_shutdown_event'),
PingEvent => $self->lookback('http_ping'),
MirrorEvent => $self->lookback('http_mirror'),
UploadEvent => $self->lookback('http_upload'),
);
return $self;
}
######################################################################
# Main Methods
# Sort of half-assed Process compatibility for testing purposes
sub run {
$_[0]->start;
POE::Kernel->run;
return 1;
}
sub start {
my $self = shift;
unless ( $self->spawned ) {
$self->spawn;
$self->post('startup');
}
return 1;
}
sub stop {
my $self = shift;
if ( $self->spawned ) {
$self->post('shutdown');
}
return 1;
}
######################################################################
# Event Methods
sub startup : Event {
# Kick off the blanket startup timeout
$_[SELF]->{status} = STARTING;
$_[SELF]->startup_timeout_start;
$_[SELF]->post('http_startup');
}
sub http_startup : Event {
$_[SELF]->{http}->start;
}
sub http_startup_event : Event {
$_[SELF]->post('child_startup');
}
sub http_startup_error : Event {
die "Failed to start the web server";
}
sub http_shutdown_event : Event {
# Nothing to do?
}
sub http_ping : Event {
$_[SELF]->startup_timeout_stop;
$_[SELF]->activity_timeout_start;
$_[SELF]->{status} = RUNNING;
$_[SELF]->{pinged} = 1;
$_[SELF]->StartupEvent;
}
sub http_mirror : Event {
$_[SELF]->activity_timeout_start;
push @{$_[SELF]->{mirrored}}, [ $_[ARG1], $_[ARG2], $_[ARG3] ];
}
sub http_upload : Event {
$_[SELF]->activity_timeout_start;
push @{$_[SELF]->{uploaded}}, [ $_[ARG1], $_[ARG2] ];
# Do we have everything?
unless ( grep { not defined $_ } values %{$_[SELF]} ) {
$_[SELF]->{status} = STOPPING;
$_[SELF]->activity_timeout_stop;
$_[SELF]->shutdown_timeout_start;
}
}
sub child_startup : Event {
# Spawn the program
$_[SELF]->{child} = POE::Wheel::Run->new(
Program => $_[SELF]->Program,
StdoutEvent => 'child_stdout',
StderrEvent => 'child_stderr',
CloseEvent => 'child_close',
);
# Trap signals from the child as well.
# NOTE: This needs to be brought under the management of POE::Declare.
$_[KERNEL]->sig_child( $_[SELF]->{child}->PID => 'child_signal' );
}
sub child_stdout : Event {
# Do nothing for now
# print STDERR "# CHILD STDOUT $_[ARG0]\n";
}
sub child_stderr : Event {
# Do nothing for now
# print STDERR "# CHILD STDERR $_[ARG0]\n";
}
sub child_close : Event {
# print STDERR "# CHILD CLOSE\n";
if ( $_[SELF]->{child} ) {
# Wait for a little to give the child time to SIGCHILD us
$_[SELF]->child_signal_timeout_start;
}
}
sub child_signal : Event {
# print STDERR "# CHILD SIGCHILD $_[ARG2]\n";
$_[SELF]->child_signal_timeout_stop;
if ( $_[SELF]->{child} ) {
$_[SELF]->post('shutdown');
}
}
sub child_signal_timeout : Timeout(5) {
if ( $_[SELF]->{child} ) {
$_[SELF]->post('shutdown');
}
}
sub startup_timeout : Timeout(30) {
$_[SELF]->post('shutdown');
}
sub activity_timeout : Timeout(3600) {
$_[SELF]->post('shutdown');
}
sub shutdown_timeout : Timeout(60) {
$_[SELF]->post('shutdown');
}
sub shutdown : Event {
$_[SELF]->finish;
$_[SELF]->ShutdownEvent(
$_[SELF]->pinged,
$_[SELF]->mirrored,
$_[SELF]->uploaded,
);
$_[SELF]->{status} = STOPPED;
}
######################################################################
# Support Methods
sub finish {
my $self = shift;
# Clean up our children
if ( $self->{child} ) {
$self->{child}->kill(9);
$self->{child} = undef;
}
if ( $self->{http}->spawned ) {
$self->{http}->call('shutdown');
}
# Call parent method to clean out other things
$self->SUPER::finish(@_);
}
compile;