X-Git-Url: http://git.indexdata.com/?p=simpleserver-moved-to-github.git;a=blobdiff_plain;f=SimpleServer.pm;h=ff1e62fd79c51c3cffa8517be46746c9f0b655c0;hp=523cf751360b2bde2684bf62757d262cad4e7bb3;hb=598ab7c8864fd51f885b7b9c096719f92a5d7ef2;hpb=10f7175c8c96ba07fa7902b9f36df5086f3ce02e diff --git a/SimpleServer.pm b/SimpleServer.pm index 523cf75..ff1e62f 100644 --- a/SimpleServer.pm +++ b/SimpleServer.pm @@ -1,5 +1,5 @@ ## -## Copyright (c) 2000, Index Data. +## Copyright (c) 2000-2004, Index Data. ## ## Permission to use, copy, modify, distribute, and sell this software and ## its documentation, in whole or in part, for any purpose, is hereby granted, @@ -25,10 +25,7 @@ ## ## -## $Log: SimpleServer.pm,v $ -## Revision 1.6 2001-03-13 14:17:15 sondberg -## Added support for GRS-1. -## +## $Id: SimpleServer.pm,v 1.20 2004-05-28 20:14:28 sondberg Exp $ package Net::Z3950::SimpleServer; @@ -41,13 +38,8 @@ require DynaLoader; require AutoLoader; @ISA = qw(Exporter AutoLoader DynaLoader); -# Items to export into callers namespace by default. Note: do not export -# names by default without a very good reason. Use EXPORT_OK instead. -# Do not simply export all your public functions/methods/constants. -@EXPORT = qw( - -); -$VERSION = '0.02'; +@EXPORT = qw( ); +$VERSION = '0.08'; bootstrap Net::Z3950::SimpleServer $VERSION; @@ -57,19 +49,16 @@ my $count = 0; 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; - $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}; - $self->{PRESENT} = $args->{PRESENT}; + 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; @@ -91,11 +80,28 @@ sub launch_server { if (defined($self->{PRESENT})) { set_present_handler($self->{PRESENT}); } + if (defined($self->{SCAN})) { + set_scan_handler($self->{SCAN}); + } 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; @@ -128,7 +134,7 @@ Net::Z3950::SimpleServer - Simple Perl API for building Z39.50 servers. 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? @@ -141,15 +147,13 @@ Net::Z3950::SimpleServer - Simple Perl API for building Z39.50 servers. ## 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, - FETCH => \&my_fetch_handler - }); + FETCH => \&my_fetch_handler); ## Launch server: - $handle->launch_server("ztest.pl", @ARGV); + $z->launch_server("ztest.pl", @ARGV); =head1 DESCRIPTION @@ -181,6 +185,7 @@ of events: - Search request - Present request - Fetching of records + - Scan request (browsing) - Closing down connection Note that only the Search and Fetch handler functions are required. @@ -197,17 +202,18 @@ output parameters. 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, - PRESENT => \&my_present_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 - $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 @@ -228,9 +234,15 @@ The argument hash passed to the init handler has the form $args = { ## Response parameters: + IMP_ID => "", ## Z39.50 Implementation ID IMP_NAME => "", ## Z39.50 Implementation name IMP_VER => "", ## Z39.50 Implementation version ERR_CODE => 0, ## Error code, cnf. Z39.50 manual + ERR_STR => "", ## Error string (additional info.) + USER => "xxx" ## If Z39.50 authentication is used, + ## this member contains user name + PASS => "yyy" ## Under same conditions, this member + ## contains the password in clear text HANDLE => undef ## Handler of Perl data structure }; @@ -243,13 +255,14 @@ result sets or a handle to a back-end search engine of some sort), it is always best to store them in a private session structure - rather than leaving them in global variables in your script. -The Implementation name and version are only really used by Z39.50 +The Implementation ID, name and version are only really used by Z39.50 client developers to see what kind of server they're dealing with. Filling these in is optional. The ERR_CODE should be left at 0 (the default value) if you wish to accept the connection. Any other value is interpreted as a failure -and the client will be shown the door. +and the client will be shown the door, with the code and the +associated additional information, ERR_STR returned. =head2 Search handler @@ -265,6 +278,7 @@ mous hash. The structure is the following: DATABASES => ["xxx"], ## Reference to a list of data- ## bases to search QUERY => "query", ## The query expression + RPN => $obj, ## Reference to a Net::Z3950::APDU::Query ## Response parameters: @@ -309,6 +323,134 @@ 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. +A more convenient alternative to the QUERY member may be the RPN +member, which is a reference to a Net::Z3950::APDU::Query object +representing the RPN query tree. The structure of that object is +supposed to be self-documenting, but here's a brief summary of what +you get: + +=over 4 + +=item * + +C is a hash with two fields: + +Z<> + +=over 4 + +=item C + +Optional. If present, it is a reference to a +C. This is a string of dot-separated integers +representing the OID of the query's top-level attribute set. + +=item C + +Mandatory: a refererence to the RPN tree itself. + +=back + +=item * + +Each node of the tree is an object of one of the following types: + +Z<> + +=over 4 + +=item C + +=item C + +=item C + +These three classes are all arrays of two elements, each of which is a +node of one of the above types. + +=item C + +See below for details. + +=back + +(I guess I should make a superclass C and make +all of these subclasses of it. Not done that yet, but will do soon.) + +=back + +=over 4 + +=item * + +C is a hash with two fields: + +Z<> + +=over 4 + +=item C + +A string containing the search term itself. + +=item C + +A reference to a C object. + +=back + +=item * + +C is an array of references to +C objects. (Note the plural/singular +distinction.) + +=item * + +C is a hash with three elements: + +Z<> + +=over 4 + +=item C + +Optional. If present, it is dot-separated OID string, as above. + +=item C + +An integer indicating the type of the attribute - for example, under +the BIB-1 attribute set, type 1 indicates a ``use'' attribute, type 2 +a ``relation'' attribute, etc. + +=item C + +An integer indicating the value of the attribute - for example, under +BIB-1, if the attribute type is 1, then value 4 indictates a title +search and 7 indictates an ISBN search; but if the attribute type is +2, then value 4 indicates a ``greater than or equal'' search, and 102 +indicates a relevance match. + +=back + +=back + +Note that, at the moment, none of these classes have any methods at +all: the blessing into classes is largely just a documentation thing +so that, for example, if you do + + { use Data::Dumper; print Dumper($args->{RPN}) } + +you get something fairly human-readable. But of course, the type +distinction between the three different kinds of boolean node is +important. + +By adding your own methods to these classes (building what I call +``augmented classes''), you can easily build code that walks the tree +of the incoming RPN. Take a look at C for a +sample implementation of such an augmented classes technique. + + =head2 Present handler The presence of a present handler in a SimpleServer front-end is optional. @@ -393,6 +535,61 @@ these. 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: @@ -409,15 +606,12 @@ or something similar, this is the place to do it. =head1 AUTHORS Anders Sønderberg (sondberg@indexdata.dk) and Sebastian Hammer -(quinn@indexdata.dk). +(quinn@indexdata.dk). Substantial contributions made by Mike Taylor +(mike@miketaylor.org.uk). =head1 SEE ALSO -perl(1). - Any Perl module which is useful for accessing the database of your choice. =cut - -