The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w
#
# Calling a SADI service for testing purposes.
#
# $Id: sadi-testing-service.pl,v 1.12 2010-01-28 16:30:50 ubuntu Exp $
# Contact: Edward Kawas <edward.kawas+sadi@gmail.com>
# -----------------------------------------------------------

BEGIN {

    # some command-line options
    use Getopt::Std;
    use vars qw/ $opt_h $opt_d $opt_v $opt_l $opt_e $opt_g $opt_p $opt_n $opt_t /;
    getopts('hdvl:e:g:pnt');

    # usage
    if ( $opt_h or ( not $opt_e and not $opt_g and scalar @ARGV == 0) ) {
        print STDOUT <<'END_OF_USAGE';
Calling SADI services remotely or locally.

   Usage: 
       # calling a local module representing a service
       [-vd] -l <lib-location>] <package-name> [<input-file>]

       # 'POST'ing to a service
       [-vd] -l <lib-location>] [-p] -e <service-url> <input-file>

       # 'GET'ting to a service
       [-vd] -l <lib-location>] [-p] -g <service-url>

       # calling a real service, using HTTP
       -e <service-url> [<input-file>]

       # calling a real service to obtain service interface
       -g <service-url>

    <package-name> is a full name of a called module (service)
        e.g. Service::HelloSadiWorld

    -l <lib-location>
        A directory where is called service stored.
        Default: Perl-SADI/services   

    -e <service-url>
        A SADI service url
        (e.g. http://localhost/cgi-bin/HelloSadiWorld)

    -g <cgi-service-url>
        A SADI service url
        (e.g. http://localhost/cgi-bin/HelloSadiWorld)

    <input-file>
        A SADI RDF/XML file with input data for the service.
        Default: an empty SADI request

    -p ... if the service is asynchronous, keep checking the
           service for a result.

    -n ... send data as n3

    -t ... request data as n3

    -v ... verbose
    -d ... debug
    -h ... help
END_OF_USAGE
        exit(0);
    }

    if ($opt_e) {
        # calling a real service, using cgi
        eval "use HTTP::Request; 1;"
          or die "$@\n";
        eval "use LWP::UserAgent; 1;"
          or die "$@\n";
    } elsif ($opt_g) {
        # calling a real service, using cgi
        eval "use HTTP::Request; 1;"
          or die "$@\n";
        eval "use LWP::UserAgent; 1;"
          or die "$@\n";
    } else {
        # calling a local service module, without HTTP
        eval "use SADI::Base; 1;";

        # take the lib location from the config file
        require lib;
        lib->import( SADI::Config->param("generators.impl.outdir") );
        require lib;
        lib->import( SADI::Config->param("generators.outdir") );
        unshift( @INC, $opt_l ) if $opt_l;
        $LOG->level('INFO')  if $opt_v;
        $LOG->level('DEBUG') if $opt_d;
    }
    if ($opt_n) {
        # can we output n3?
        eval "use RDF::Notation3; 1;"
          or die "$@\n";
    }

}

use strict;
use Carp;
sub _empty_input {
    eval "use SADI::Utils; 1;" or die "$@\n";
    return SADI::Utils::empty_rdf();
}


# --- what service to call
my $module = shift unless $opt_e or $opt_g;    # eg. Service::Mabuhay, or just Mabuhay
my $service;
( $service = $module ) =~ s/.*::// unless $opt_e or $opt_g;

# --- call the service
if ($opt_e) {

    # calling a real service, using HTTP Post
    my $req = HTTP::Request->new( POST => $opt_e );
    if ($opt_n) {
    	$req->content_type('text/rdf+n3');	
    }
    if ($opt_t) {
    	$req->header(Accept => "text/rdf+n3");
    }
    my $ua = LWP::UserAgent->new;
    my $input = '';
    if ( @ARGV > 0 ) {
        my $data = shift;    # a file name
        open INPUT, "<$data"
          or die "Cannot read '$data': $!\n";
        while (<INPUT>) { $input .= $_; }
        close INPUT;
    } else {
        $input = _empty_input;
    }
    print "\nSending to $opt_e the following:\n$input\n" if $opt_d or $opt_v;
    $req->content_type('application/rdf+xml');
    $req->content("$input");
    my $response = $ua->request($req); 
    print "\n" . $response->as_string . "\n";
    if ($opt_p) {
        if ($response->status_line =~ m/202|302/ 
          or ($response->header('pragma') 
          and $response->header('pragma') =~ m/sadi-please-wait/)) {
            print "\nAsynchronous service detected ... Going to attempt to poll it!\n";
            &_poll_until_done($response->content);
        }
    }
    print "\nDone!\n";

} elsif ($opt_g) {
    # calling a real SADI service, using HTTP Get
    my $ua = LWP::UserAgent->new;
    my $req = HTTP::Request->new( GET => $opt_g );
    if ($opt_n) {
    	# set content type in case we are not just asking for the signature
        $req->content_type('text/rdf+n3');  
    }
    if ($opt_t) {
        $req->header(Accept => "text/rdf+n3");
    }
    my $response = $ua->request($req);
    print "\n" . $response->as_string . "\n";
    if ($opt_p) {
        if ($response->status_line =~ m/202|302/
         or ($response->header('pragma') 
         and $response->header('pragma') =~ m/sadi-please-wait/)
         or ($response->header('Retry-After'))) {
            print "\nAsynchronous service detected ... Going to attempt to poll service!\n" if $opt_d or $opt_v;
            &_poll_until_done($response->content);
        }
    }
    print "\nDone!\n";
    exit;
} else {

    # calling a local service module, without HTTP
    my $data;
    if ( @ARGV > 0 ) {
        $data = shift;    # a file name
    } else {
        use File::Temp qw( tempfile );
        my $fh;
        ( $fh, $data ) = tempfile( UNLINK => 1 );
        print $fh _empty_input();
        close $fh;
    }
    eval "require $module" or croak $@;
    eval {
        my $target = new $module;
        print $target->$service($data), "\n";
    } or croak $@;
}

sub _poll_until_done {
    my $content = shift;
    require RDF::Core::Model;
    require RDF::Core::Storage::Memory;
    require RDF::Core::Model::Parser;
    require RDF::Core::Resource;
    require RDF::Notation3::RDFCore;

    my $storage = new RDF::Core::Storage::Memory;
    my $model = new RDF::Core::Model (Storage => $storage);
    
    if ($opt_t) {
    	# response is n3
    	my $rdf_n3 = RDF::Notation3::RDFCore->new();
        $rdf_n3->set_storage($storage);
        eval{$model = $rdf_n3->parse_string($content);};
    } else {
    	# response is rdf/xml
	    my %options = (Model => $model,
	              Source => $content,
	              SourceType => 'string',
	              BaseURI => "http://www.foo.com/",
	    );
        my $parser = new RDF::Core::Model::Parser(%options);
        $parser->parse;
    }
    # extract all of the polling urls
    my %urls;
    my $enumerator = $model->getStmts(undef, new RDF::Core::Resource('http://www.w3.org/2000/01/rdf-schema#isDefinedBy') , undef);
    my $statement = $enumerator->getFirst;
    while (defined $statement) {
        my $url = $statement->getObject;
        $urls{$url->getURI} = 1;
        $statement = $enumerator->getNext
    }
    $enumerator->close;
    # free memory
    $model=undef; $storage = undef; $enumerator = undef;
    
    my $sleep_time = 15;
    # now keep polling our unique urls ...
    while (scalar(keys(%urls)) > 0) {
        foreach (keys %urls) {
            print "   polling $_\n" if $opt_d or $opt_v;
            my $ua = LWP::UserAgent->new;
            my $req = HTTP::Request->new( GET => $_ );
            if ($opt_n) {
		        $req->content_type('text/rdf+n3');  
		    }
		    if ($opt_t) {
		        $req->header(Accept => "text/rdf+n3, */*;q=0.1");
		    }
            my $response = $ua->request($req);
            unless ($response->status_line =~ m/202|302/ 
                or ($response->header('pragma') 
                and $response->header('pragma') =~ m/sadi-please-wait/)
                or ($response->header('Retry-After'))) {
                print "\n", $response->as_string, "\n";
                delete $urls{$_};
            }
        }
        next if scalar(keys(%urls)) == 0;
        # should sleep as long as sadi-please-wait says to ... but
        if (($sleep_time * 1.5 ) < 300) {
            # progressively sleep longer as to not piss off servers
            $sleep_time = int ($sleep_time*1.5);
        }
        print "   waiting $sleep_time seconds to poll again ...\n" if $opt_d or $opt_v;
        sleep($sleep_time);
    }
}
__END__