Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Re: Managing a long running server side process using CGI

by GrandFather (Saint)
on Jan 31, 2007 at 10:41 UTC ( [id://597520] : note . print w/replies, xml ) Need Help??


in reply to Managing a long running server side process using CGI

After reading through the various previous replies and the linked material I put together the following two test applications. The CGI app is heavily based on the sample code in merlyn's LinuxMag column Watching long processes through CGI (pointed to by McDarren++), with amendments so that it works under Windows (but no longer under *nix, although making both work in this "framework" ought be trivial). In the real application the CGI app would manage the monitored app by writing to a second file.

The managing app:

#!Perl -w use strict; use CGI::Pretty qw(:standard :cgi-lib); use CGI::Carp qw(fatalsToBrowser); # Remove for production code use File::Cache; $CGI::DISABLE_UPLOADS = 1; # Disable uploads $CGI::POST_MAX = 10240; # Maximum number of bytes per post $| = 1; # Unbuffered output if (param('Spawn')) { # setup monitoring page then spawn the monitored process my $session = get_session_id(); my $cache = get_cache_handle(); $cache->set($session, "wait ..."); # no data yet Delete_all(); # parent redirects browser to monitor session param('session', $session); print redirect (self_url()); close STDOUT; # Rest of this block alters in *nix context to spawn monitored pro +cess use Win32::Process; my $job; Win32::Process::Create ( $job, 'c:/Perl/bin/perl.exe', "perl.exe spawned.pl $session", 0, NORMAL_PRIORITY_CLASS | DETACHED_PROCESS, '.' ); exit 0; # all done } elsif (my $session = param('session')) { # display monitored data my $cache = get_cache_handle(); my $data = $cache->get($session); if (! $data) { # something is wrong showError ("Cache data not available"); exit 0; } my $headStr = $data eq 'Completed' ? '' : "<meta http-equiv=refres +h content=5>"; print header(); print start_html (-title => "Spawn Results", -head => [$headStr]); print h1("Spawn Results"); print pre(escapeHTML($data)); print end_html; } else { # display spawn form print header(), start_html("Spawn"), h1("Spawn"); print start_form(); print submit('Spawn', 'spawn'); my %params = Vars (); for my $param (keys %params) { print br ("$param -> $params{$param}"); } print end_form(), end_html(); } exit 0; sub showError { print header(), start_html("SpawnError"), h1("Spawn Error"); print p (shift); my %params = Vars (); for my $param (keys %params) { print br ("$param -> $params{$param}"); } print end_html(); } sub get_cache_handle { File::Cache->new ({ namespace => 'Spawn', username => 'nobody', default_expires_in => '30 minutes', auto_purge_interval => '4 hours', }); } sub get_session_id { require Digest::MD5; Digest::MD5::md5_hex(Digest::MD5::md5_hex(time().{}.rand().$$)); }

The monitored app:

#!Perl -w use strict; use File::Cache; my $session = shift; my $cache = get_cache_handle(); $cache->set($session, "wibble ..."); # no data yet my $end = time () + 20; my $count = 0; while (time () < $end) { $cache->set ($session, "Count: $count\n"); ++$count; sleep (1); } $cache->set ($session, "Completed"); exit 0; # all done sub get_cache_handle { File::Cache->new ({ namespace => 'Spawn', username => 'nobody', default_expires_in => '30 minutes', auto_purge_interval => '4 hours', }); }

The code was tested using a local Apache server and seems to provide exactly the types of interaction I am looking for.

Are there any glaring oversights?


DWIM is Perl's answer to Gödel

Replies are listed 'Best First'.
Re^2: Managing a long running server side process using CGI
by GrandFather (Saint) on Feb 26, 2007 at 10:49 UTC

    Using File::Cache seemed to produce strange behaviour which I attribute to file locking or sharing issues and race conditions between the two processes. I've updated the code to use CGI::Session instead which not only seems to have fixed the problem, but also removes the need to generate an ID explicitely.

    Not however the use of flush () in the main loop of the long process to ensure the data is flushed through to the monitoring process.

    #!Perl -w use strict; use CGI::Pretty qw(:standard :cgi-lib); use CGI::Carp qw(fatalsToBrowser); # Remove for production code use CGI::Session; $CGI::DISABLE_UPLOADS = 1; # Disable uploads $CGI::POST_MAX = 10240; # Maximum number of bytes per post $| = 1; # Unbuffered output if (param('Spawn')) { # setup monitoring page then spawn the monitored process my $cache = CGI::Session->new (); my $session = $cache->id(); $cache->param ('status', "wait ..."); # no data yet Delete_all(); # parent redirects browser to monitor session param('session', $session); print redirect (self_url()); close STDOUT; # Rest of this block alters in *nix context to spawn monitored pro +cess use Win32::Process; my $job; Win32::Process::Create ( $job, 'c:/Perl/bin/perl.exe', "perl.exe spawned.pl \"$session\"", 0, NORMAL_PRIORITY_CLASS | DETACHED_PROCESS, '.' ); exit 0; # all done } elsif (my $session = param('session')) { # display monitored data my $cache = CGI::Session->new ($session); my $data = $cache->param ('status'); if (! $data) { # something is wrong showError ("Cache data not available"); exit 0; } my $headStr = $data eq 'Completed' ? '' : "<meta http-equiv=refres +h content=5>"; print header(); print start_html (-title => "Spawn Results", -head => [$headStr]); print h1("Spawn Results"); print pre(escapeHTML($data)); print end_html; } else { # display spawn form print header(), start_html("Spawn"), h1("Spawn"); print start_form(); print submit('Spawn', 'spawn'); my %params = Vars (); for my $param (keys %params) { print br ("$param -> $params{$param}"); } print end_form(), end_html(); } exit 0; sub showError { print header(), start_html("SpawnError"), h1("Spawn Error"); print p (shift); my %params = Vars (); for my $param (keys %params) { print br ("$param -> $params{$param}"); } print end_html(); }

    The monitored (long running) process:

    #!Perl -w use strict; use CGI::Session; my $session = shift; my $cache = CGI::Session->load ($session); $cache->param('status', "configuring ..."); # no data yet my $end = time () + 20; my $count = 0; while (time () < $end) { $cache->param ('status', "Count: $count\n"); $cache->flush (); ++$count; sleep (1); } $cache->param ('status', "Completed"); $cache->flush (); exit 0; # all done

    DWIM is Perl's answer to Gödel
Re^2: Managing a long running server side process using CGI
by Anonymous Monk on Nov 22, 2015 at 06:52 UTC
    Hello, I needed to add print end_html; after line print redirect ( self_url() );