Version 0.05 so that we can release the thing at CPAN.
[simpleserver-moved-to-github.git] / SimpleServer.pm
index b2c27a1..c4b5c96 100644 (file)
 ##
 ##
 
 ##
 ##
 
+## $Log: SimpleServer.pm,v $
+## Revision 1.12  2002-03-05 20:52:22  sondberg
+## Version 0.05 so that we can release the thing at CPAN.
+##
+## Revision 1.11  2002/03/05 20:49:56  sondberg
+## Added a couple of lines of documentation.
+##
+## Revision 1.10  2002/02/28 11:21:57  mike
+## Add RPN structure to search-handler argument hash.
+##
+## Revision 1.9  2001/08/29 11:48:36  sondberg
+## Added routines
+##
+##     Net::Z3950::SimpleServer::ScanSuccess
+##     Net::Z3950::SimpleServer::ScanPartial
+##
+## and a bit of documentation.
+##
+## Revision 1.8  2001/08/29 10:29:51  sondberg
+## Added some documentation of scan.
+##
+## Revision 1.7  2001/08/24 14:00:20  sondberg
+## Added support for scan.
+##
+## Revision 1.6  2001/03/13 14:17:15  sondberg
+## Added support for GRS-1.
+##
+
 package Net::Z3950::SimpleServer;
 
 use strict;
 package Net::Z3950::SimpleServer;
 
 use strict;
@@ -42,7 +70,7 @@ require AutoLoader;
 @EXPORT = qw(
        
 );
 @EXPORT = qw(
        
 );
-$VERSION = '0.02';
+$VERSION = '0.05';
 
 bootstrap Net::Z3950::SimpleServer $VERSION;
 
 
 bootstrap Net::Z3950::SimpleServer $VERSION;
 
@@ -52,18 +80,16 @@ my $count = 0;
 
 sub new {
        my $class = shift;
 
 sub new {
        my $class = shift;
-       my $args = shift || croak "SimpleServer::new: Usage new(argument hash)";
-       my $self = {};
+       my %args = @_;
+       my $self = \%args;
 
        if ($count) {
                carp "SimpleServer.pm: WARNING: Multithreaded server unsupported";
        }
        $count = 1;
 
 
        if ($count) {
                carp "SimpleServer.pm: WARNING: Multithreaded server unsupported";
        }
        $count = 1;
 
-       $self->{INIT} = $args->{INIT};
-       $self->{SEARCH} = $args->{SEARCH} || croak "SimpleServer.pm: ERROR: Unspecified search handler";
-       $self->{FETCH} = $args->{FETCH} || croak "SimpleServer.pm: ERROR: Unspecified fetch handler";
-       $self->{CLOSE} = $args->{CLOSE};
+       croak "SimpleServer.pm: ERROR: Unspecified search handler" unless defined($self->{SEARCH});
+       croak "SimpleServer.pm: ERROR: Unspecified fetch handler" unless defined($self->{FETCH});
 
        bless $self, $class;
        return $self;
 
        bless $self, $class;
        return $self;
@@ -82,11 +108,31 @@ sub launch_server {
        if (defined($self->{CLOSE})) {
                set_close_handler($self->{CLOSE});
        }
        if (defined($self->{CLOSE})) {
                set_close_handler($self->{CLOSE});
        }
+       if (defined($self->{PRESENT})) {
+               set_present_handler($self->{PRESENT});
+       }
+       if (defined($self->{SCAN})) {
+               set_scan_handler($self->{SCAN});
+       }
 
        start_server(@args);
 }
 
 
 
        start_server(@args);
 }
 
 
+# Register packages that we will use in translated RPNs
+package Net::Z3950::APDU::Query;
+package Net::Z3950::APDU::OID;
+package Net::Z3950::RPN::And;
+package Net::Z3950::RPN::Or;
+package Net::Z3950::RPN::AndNot;
+package Net::Z3950::RPN::Term;
+package Net::Z3950::RPN::Attributes;
+package Net::Z3950::RPN::Attribute;
+
+# Must revert to original package for Autoloader's benefit
+package Net::Z3950::SimpleServer;
+
+
 # Autoload methods go after =cut, and are processed by the autosplit program.
 
 1;
 # Autoload methods go after =cut, and are processed by the autosplit program.
 
 1;
@@ -119,7 +165,7 @@ Net::Z3950::SimpleServer - Simple Perl API for building Z39.50 servers.
 
        my $set_id = $args->{SETNAME};
 
 
        my $set_id = $args->{SETNAME};
 
-       my $record = fetch_a_record($args->{OFFSET);
+       my $record = fetch_a_record($args->{OFFSET});
 
        $args->{RECORD} = $record;
        if (number_of_hits() == $args->{OFFSET}) {      ## Last record in set?
 
        $args->{RECORD} = $record;
        if (number_of_hits() == $args->{OFFSET}) {      ## Last record in set?
@@ -132,15 +178,13 @@ Net::Z3950::SimpleServer - Simple Perl API for building Z39.50 servers.
 
   ## Register custom event handlers:
 
 
   ## Register custom event handlers:
 
-  my $handle = Net::Z3950::SimpleServer->new({
-                                               INIT   =>  \&my_init_handler,
+  my $z = new Net::Z3950::SimpleServer(                INIT   =>  \&my_init_handler,
                                                CLOSE  =>  \&my_close_handler,
                                                SEARCH =>  \&my_search_handler,
                                                CLOSE  =>  \&my_close_handler,
                                                SEARCH =>  \&my_search_handler,
-                                               FETCH  =>  \&my_fetch_handler
-                                            });
+                                               FETCH  =>  \&my_fetch_handler);
   ## Launch server:
 
   ## Launch server:
 
-  $handle->launch_server("ztest.pl", @ARGV);
+  $z->launch_server("ztest.pl", @ARGV);
 
 =head1 DESCRIPTION
 
 
 =head1 DESCRIPTION
 
@@ -170,7 +214,9 @@ of events:
 
   - Initialize request
   - Search request
 
   - Initialize request
   - Search request
+  - Present request
   - Fetching of records
   - Fetching of records
+  - Scan request (browsing) 
   - Closing down connection
 
 Note that only the Search and Fetch handler functions are required.
   - Closing down connection
 
 Note that only the Search and Fetch handler functions are required.
@@ -187,16 +233,18 @@ output parameters.
 The Perl programmer specifies the event handles for the server by
 means of the the SimpleServer object constructor
 
 The Perl programmer specifies the event handles for the server by
 means of the the SimpleServer object constructor
 
-  my $handle = Net::Z3950::SimpleServer->new({
-               INIT    =>      \&my_init_handler,
-               CLOSE   =>      \&my_close_handler,
-               SEARCH  =>      \&my_search_handler,
-               FETCH   =>      \&my_fetch_handler });
+  my $z = new Net::Z3950::SimpleServer(
+                       INIT    =>      \&my_init_handler,
+                       CLOSE   =>      \&my_close_handler,
+                       SEARCH  =>      \&my_search_handler,
+                       PRESENT =>      \&my_present_handler,
+                       SCAN    =>      \&my_scan_handler,
+                       FETCH   =>      \&my_fetch_handler);
 
 After the custom event handles are declared, the server is launched
 by means of the method
 
 
 After the custom event handles are declared, the server is launched
 by means of the method
 
-  $handle->launch_server("MyServer.pl", @ARGV);
+  $z->launch_server("MyServer.pl", @ARGV);
 
 Notice, the first argument should be the name of your server
 script (for logging purposes), while the rest of the arguments
 
 Notice, the first argument should be the name of your server
 script (for logging purposes), while the rest of the arguments
@@ -217,9 +265,9 @@ The argument hash passed to the init handler has the form
   $args = {
                                    ## Response parameters:
 
   $args = {
                                    ## Response parameters:
 
-            IMP_NAME  =>  ""       ## Z39.50 Implementation name
-            IMP_VER   =>  ""       ## Z39.50 Implementation version
-            ERR_CODE  =>  0        ## Error code, cnf. Z39.50 manual
+            IMP_NAME  =>  "",      ## Z39.50 Implementation name
+            IMP_VER   =>  "",      ## Z39.50 Implementation version
+            ERR_CODE  =>  0,       ## Error code, cnf. Z39.50 manual
             HANDLE    =>  undef    ## Handler of Perl data structure
          };
 
             HANDLE    =>  undef    ## Handler of Perl data structure
          };
 
@@ -248,17 +296,19 @@ mous hash. The structure is the following:
   $args = {
                                    ## Request parameters:
 
   $args = {
                                    ## Request parameters:
 
-            HANDLE    =>  ref      ## Your session reference.
-            SETNAME   =>  "id"     ## ID of the result set
-            REPL_SET  =>  0        ## Replace set if already existing?
-            DATABASES =>  ["xxx"]  ## Reference to a list of data-
+            HANDLE    =>  ref,     ## Your session reference.
+            SETNAME   =>  "id",    ## ID of the result set
+            REPL_SET  =>  0,       ## Replace set if already existing?
+            DATABASES =>  ["xxx"], ## Reference to a list of data-
                                    ## bases to search
                                    ## bases to search
-            QUERY     =>  "query"  ## The query expression
+            QUERY     =>  "query", ## The query expression
+            RPN       =>  $obj,    ## Blessed reference int the package
+                                   ## Net::Z3950::APDU::Query
 
                                    ## Response parameters:
 
 
                                    ## Response parameters:
 
-            ERR_CODE  =>  0        ## Error code (0=Succesful search)
-            ERR_STR   =>  ""       ## Error string
+            ERR_CODE  =>  0,       ## Error code (0=Succesful search)
+            ERR_STR   =>  "",      ## Error string
             HITS      =>  0        ## Number of matches
          };
 
             HITS      =>  0        ## Number of matches
          };
 
@@ -298,6 +348,51 @@ it is perfectly legal to not accept boolean operators, but you SHOULD
 try to return good error codes if you run into something you can't or
 won't support.
 
 try to return good error codes if you run into something you can't or
 won't support.
 
+The RPN member is a blessed reference into the package Net::Z3950::APDU::Query.
+By means of an augmented type of coding, you can easily construct a
+parser of the incoming RPN. Take a look at samples/render-search.pl for
+a sample implementation of such an augmented parser technique. 
+
+=head2 Present handler
+
+The presence of a present handler in a SimpleServer front-end is optional.
+Each time a client wishes to retrieve records, the present service is
+called. The present service allows the origin to request a certain number
+of records retrieved from a given result set.
+When the present handler is called, the front-end server should prepare a
+result set for fetching. In practice, this means to get access to the
+data from the backend database and store the data in a temporary fashion
+for fast and efficient fetching. The present handler does *not* fetch
+anything. This task is taken care of by the fetch handler, which will be
+called the correct number of times by the YAZ library. More about this
+below.
+If no present handler is implemented in the front-end, the YAZ toolkit
+will take care of a minimum of preparations itself. This default present
+handler is sufficient in many situations, where only a small amount of
+records are expected to be retrieved. If on the other hand, large result
+sets are likely to occur, the implementation of a reasonable present
+handler can gain performance significantly.
+
+The informations exchanged between client and present handle are:
+
+  $args = {
+                                   ## Client/server request:
+
+            HANDLE    =>  ref,     ## Reference to datastructure
+            SETNAME   =>  "id",    ## Result set ID
+            START     =>  xxx,     ## Start position
+            COMP      =>  "",      ## Desired record composition
+            NUMBER    =>  yyy,     ## Number of requested records
+
+
+                                   ## Respons parameters:
+
+            HITS      =>  zzz,     ## Number of returned records
+            ERR_CODE  =>  0,       ## Error code
+            ERR_STR   =>  ""       ## Error message
+          };
+
+
 =head2 Fetch handler
 
 The fetch handler is asked to retrieve a SINGLE record from a given
 =head2 Fetch handler
 
 The fetch handler is asked to retrieve a SINGLE record from a given
@@ -312,7 +407,7 @@ The parameters exchanged between the server and the fetch handler are
             HANDLE    =>  ref      ## Reference to data structure
             SETNAME   =>  "id"     ## ID of the requested result set
             OFFSET    =>  nnn      ## Record offset number
             HANDLE    =>  ref      ## Reference to data structure
             SETNAME   =>  "id"     ## ID of the requested result set
             OFFSET    =>  nnn      ## Record offset number
-            REQ_FORM  =>  "USMARC" ## Client requested record format
+            REQ_FORM  =>  "n.m.k.l"## Client requested format OID
             COMP      =>  "xyz"    ## Formatting instructions
 
                                    ## Handler response:
             COMP      =>  "xyz"    ## Formatting instructions
 
                                    ## Handler response:
@@ -323,7 +418,7 @@ The parameters exchanged between the server and the fetch handler are
             ERR_CODE  =>  0        ## Error code
             ERR_STR   =>  ""       ## Error string
             SUR_FLAG  =>  0        ## Surrogate diagnostic flag
             ERR_CODE  =>  0        ## Error code
             ERR_STR   =>  ""       ## Error string
             SUR_FLAG  =>  0        ## Surrogate diagnostic flag
-            REP_FORM  =>  "USMARC" ## Provided record format
+            REP_FORM  =>  "n.m.k.l"## Provided format OID
          };
 
 The REP_FORM value has by default the REQ_FORM value but can be set to
          };
 
 The REP_FORM value has by default the REQ_FORM value but can be set to
@@ -335,16 +430,68 @@ indicate whether the error condition pertains to the record currently
 being retrieved, or whether it pertains to the operation as a whole
 (eg. the client has specified a result set which does not exist.)
 
 being retrieved, or whether it pertains to the operation as a whole
 (eg. the client has specified a result set which does not exist.)
 
-Record formats are currently carried as strings (eg. USMARC, TEXT_XML,
-SUTRS), but this will probably change to proper OID strings in the
-future (not to worry, though, the module will supply constant values
-for the common OIDs). If you need to return USMARC records, you might
-want to have a look at the MARC module on CPAN, if you don't already
-have a way of generating these.
+If you need to return USMARC records, you might want to have a look at
+the MARC module on CPAN, if you don't already have a way of generating
+these.
 
 NOTE: The record offset is 1-indexed - 1 is the offset of the first
 record in the set.
 
 
 NOTE: The record offset is 1-indexed - 1 is the offset of the first
 record in the set.
 
+=head2 Scan handler
+
+A full featured Z39.50 server should support scan (or in some literature
+browse). The client specifies a starting term of the scan, and the server
+should return an ordered list of specified length consisting of terms
+actually occurring in the data base. Each of these terms should be close
+to or equal to the term originally specified. The quality of scan compared
+to simple search is a guarantee of hits. It is simply like browsing through
+an index of a book, you always find something! The parameters exchanged are
+
+  $args = {
+                                               ## Client request
+
+               HANDLE          => $ref         ## Reference to data structure
+               TERM            => 'start',     ## The start term
+               NUMBER          => xx,          ## Number of requested terms
+               POS             => yy,          ## Position of starting point
+                                               ## within returned list
+               STEP            => 0,           ## Step size
+
+                                               ## Server response
+
+               ERR_CODE        => 0,           ## Error code
+               ERR_STR         => '',          ## Diagnostic message
+               NUMBER          => zz,          ## Number of returned terms
+               STATUS          => $status,     ## ScanSuccess/ScanFailure
+               ENTRIES         => $entries     ## Referenced list of terms
+       };
+
+where the term list is returned by reference in the scalar $entries, which
+should point at a data structure of this kind,
+
+  my $entries = [
+                       {       TERM            => 'energy',
+                               OCCURRENCE      => 5            },
+
+                       {       TERM            => 'energy density',
+                               OCCURRENCE      => 6,           },
+
+                       {       TERM            => 'energy flow',
+                               OCCURRENCE      => 3            },
+
+                               ...
+
+                               ...
+       ];
+
+The $status flag should be assigned one of two values:
+
+  Net::Z3950::SimpleServer::ScanSuccess  On success (default)
+  Net::Z3950::SimpleServer::ScanPartial  Less terms returned than requested
+
+The STEP member contains the requested number of entries in the term-list
+between two adjacent entries in the response.
+
 =head2 Close handler
 
 The argument hash recieved by the close handler has one element only:
 =head2 Close handler
 
 The argument hash recieved by the close handler has one element only:
@@ -361,7 +508,7 @@ or something similar, this is the place to do it.
 =head1 AUTHORS
 
 Anders Sønderberg (sondberg@indexdata.dk) and Sebastian Hammer
 =head1 AUTHORS
 
 Anders Sønderberg (sondberg@indexdata.dk) and Sebastian Hammer
-(quinn@indexdata.dk).
+(quinn@indexdata.dk). Substantial contributions made by Mike Taylor (mike@tecc.co.uk).
 
 =head1 SEE ALSO
 
 
 =head1 SEE ALSO