Ignore errors detected on RECV_APDU except when the task is Connect.
[irspy-moved-to-github.git] / lib / ZOOM / IRSpy.pm
index cbf19c3..6ea8910 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: IRSpy.pm,v 1.49 2006-11-16 17:19:28 mike Exp $
+# $Id: IRSpy.pm,v 1.64 2007-02-22 17:46:23 mike Exp $
 
 package ZOOM::IRSpy;
 
@@ -16,6 +16,7 @@ use Net::Z3950::ZOOM 1.13;    # For the ZOOM version-check only
 use ZOOM::IRSpy::Node;
 use ZOOM::IRSpy::Connection;
 use ZOOM::IRSpy::Record;
+use ZOOM::IRSpy::Stats;
 use ZOOM::IRSpy::Utils qw(cql_target);
 
 our @ISA = qw();
@@ -41,7 +42,9 @@ ZOOM::IRSpy - Perl extension for discovering and analysing IR services
 
  use ZOOM::IRSpy;
  $spy = new ZOOM::IRSpy("target/string/for/irspy/database");
- print $spy->report_status();
+ $spy->targets(@targets);
+ $spy->initialise();
+ $res = $spy->check("Main");
 
 =head1 DESCRIPTION
 
@@ -72,6 +75,10 @@ sub new {
        or die "$0: can't connection to IRSpy database 'dbname'";
 
     my $xslt = new XML::LibXSLT;
+
+    $xslt->register_function($ZOOM::IRSpy::Utils::IRSPY_NS, 'strcmp',
+                             \&ZOOM::IRSpy::Utils::xslt_strcmp);
+
     my $libxml = new XML::LibXML;
     my $xsl_doc = $libxml->parse_file($irspy_to_zeerex_xsl);
     my $irspy_to_zeerex_style = $xslt->parse_stylesheet($xsl_doc);
@@ -239,10 +246,24 @@ sub _render_record {
 
 
 sub _irspy_to_zeerex {
-    my ($this, $conn) = @_;
+    my $this = shift();
+    my($conn, $save_xml) = @_;
     my $irspy_doc = $conn->record()->{zeerex}->ownerDocument;
+
+    if ($save_xml) {
+       unlink('/tmp/irspy_orig.xml');
+       open FH, '>/tmp/irspy_orig.xml';
+       print FH $irspy_doc->toString();
+       close FH;
+    }
     my %params = ();
     my $result = $this->{irspy_to_zeerex_style}->transform($irspy_doc, %params);
+    if ($save_xml) {
+       unlink('/tmp/irspy_transformed.xml');
+       open FH, '>/tmp/irspy_transformed.xml';
+       print FH $result->toString();
+       close FH;
+    }
 
     return $result->documentElement();
 }
@@ -253,7 +274,7 @@ sub _rewrite_record {
     my($conn) = @_;
 
     $conn->log("irspy", "rewriting XML record");
-    my $rec = $this->_irspy_to_zeerex($conn);
+    my $rec = $this->_irspy_to_zeerex($conn, $ENV{IRSPY_SAVE_XML});
     _really_rewrite_record($this->{conn}, $rec);
 }
 
@@ -320,6 +341,8 @@ sub check {
 
     my @conn = @{ $this->{connections} };
 
+    my $nruns = 0;
+  ROUND_AND_ROUND_WE_GO:
     while (1) {
        my @copy_conn = @conn;  # avoid alias problems after splice()
        my $nconn = scalar(@copy_conn);
@@ -381,7 +404,19 @@ sub check {
        my $i0 = ZOOM::event(\@conn);
        $this->log("irspy_event",
                   "ZOOM_event(", scalar(@conn), " connections) = $i0");
-       last if $i0 == 0 || $i0 == -3; # no events or no connections
+       if ($i0 < 1) {
+           my %messages = (
+                           0 => "no events remain",
+                           -1 => "ZOOM::event() argument not a reference",
+                           -2 => "ZOOM::event() reference not an array",
+                           -3 => "no connections remain",
+                           -4 => "too many connections for ZOOM::event()",
+                           );
+           my $message = $messages{$i0} || "ZOOM::event() returned $i0";
+           $this->log("irspy", $message);
+           last;
+       }
+
        my $conn = $conn[$i0-1];
        my $ev = $conn->last_event();
        my $evstr = ZOOM::event_str($ev);
@@ -392,15 +427,19 @@ sub check {
        eval { $conn->_check() };
        if ($@ &&
            ($ev == ZOOM::Event::RECV_DATA ||
-            $ev == ZOOM::Event::RECV_APDU ||
-            $ev == ZOOM::Event::ZEND)) {
+            $ev == ZOOM::Event::ZEND ||
+            ($ev == ZOOM::Event::RECV_APDU &&
+             !$task->isa("ZOOM::IRSpy::Task::Connect")))) {
            # An error in, say, a search response, becomes visible to
            # ZOOM before the Receive Data event is sent and persists
            # until after the End, which means that successive events
            # each report the same error.  So we just ignore errors on
-           # "unimportant" events.  ### But this doesn't work for,
-           # say, a Connection Refused, as the only event that shows
-           # us this error is the End.
+           # "unimportant" events.  We can also ignore errors on
+           # RECV_APDU in most cases, but since there is no RECV_INIT
+           # event, we need to avoid doing this if the task is
+           # Connect.  Yuck -- special cases.
+           # ### But this doesn't work for, say, a Connection Refused,
+           # as the only event that shows us this error is the ZEND.
            $conn->log("irspy_event", "ignoring error ",
                       "on event $ev ($evstr): $@");
            next;
@@ -450,7 +489,7 @@ sub check {
            }
 
        } elsif ($res == ZOOM::IRSpy::Status::TEST_SKIPPED) {
-           $conn->log("irspy_task", "test skipped during task $task");
+           $conn->log("irspy_test", "test skipped during task $task");
            $conn->current_task(0);
            $conn->next_task(0);
            # I think that's all we need to do
@@ -462,26 +501,61 @@ sub check {
 
     $this->log("irspy", "exiting main loop");
     # Sanity checks: none of the following should ever happen
+    my $finished = 1;
     foreach my $conn (@{ $this->{connections} }) {
        my $test = $conn->option("current_test_address");
        my $next = $this->_next_test($test);
        if (defined $next) {
-           warn "$conn (in test '$test') has queued test '$next'";
+           $this->log("irspy", "$conn (in test '$test') has queued test '$next'");
+           $finished = 0;
        }
        if (my $task = $conn->current_task()) {
-           warn "$conn still has an active task $task";
+           $this->log("irspy", "$conn still has an active task $task");
+           $finished = 0;
        }
        if (my $task = $conn->next_task()) {
-           warn "$conn still has a queued task $task";
+           $this->log("irspy", "$conn still has a queued task $task");
+           $finished = 0;
        }
        if (!$conn->is_idle()) {
-           warn "$conn is not idle (still has ZOOM-C level tasks queued)";
+           $this->log("irspy", "$conn still has ZOOM-C level tasks queued: see below");
+           $finished = 0;
+       }
+       my $ev = $conn->peek_event();
+       if ($ev != 0 && $ev != ZOOM::Event::ZEND) {
+           my $evstr = ZOOM::event_str($ev);
+           $this->log("irspy", "$conn has event $ev ($evstr) waiting");
+           $finished = 0;
        }
        if (!$conn->option("rewrote_record")) {
-           warn "$conn did not rewrite its ZeeRex record";
+           $this->log("irspy", "$conn did not rewrite its ZeeRex record");
+           $finished = 0;
        }
     }
 
+    # This really shouldn't be necessary, and in practice it rarely
+    # helps, but it's belt and braces.  For now, we don't do this
+    # (hence the zero in the $nruns check).
+    if (!$finished) {
+       if (++$nruns < 0) {
+           $this->log("irspy", "back into main loop, ${nruns}th time");
+           goto ROUND_AND_ROUND_WE_GO;
+       } else {
+           $this->log("irspy", "bailing after $nruns main-loop runs");
+       }
+    }
+
+    # This shouldn't happen emit anything either:
+    @conn = @{ $this->{connections} };
+    while ((my $i1 = ZOOM::event(\@conn)) > 0) {
+       my $conn = $conn[$i1-1];
+       my $ev = $conn->last_event();
+       my $evstr = ZOOM::event_str($ev);
+       $this->log("irspy",
+                  "$conn still has ZOOM-C level task queued: $ev ($evstr)")
+           if $ev != ZOOM::Event::ZEND;
+    }
+
     return $nskipped;
 }
 
@@ -556,8 +630,8 @@ sub _last_sibling_test {
        my $maybe = $this->_next_sibling_test($address);
        last if !defined $maybe;
        $nskipped++;
-       $this->log("irspy", "skipping $nskipped tests to '$address'");
        $address = $maybe;
+       $this->log("irspy", "skipping $nskipped tests to '$address'");
     }
 
     return ($address, $nskipped);