Design Notes

The Zserver is designed in layers as described below. It is based on the assumption that a new instantiation of the server will be started for each client by the inet demon when contacted by a client at the agreed-upon port.

Zserver's Organization (layers, directories, source files)


     Main function knows nothing about Z39.50 or any IR application.
     It invokes zserver functions:
     - init_zserver(): reads table of databases
       and set default pdus;

     - zserver(): loops on incoming msgs from
       the client (via socket) until terminate
     - close_zserver(): release resources

     IR application-independent server code which:

     - initializes database table and defaults

     - loops
         reads incoming msg in BER-format from socket
         translates incoming msg to incoming PDU
         invokes zservices level (process_PDU())
         translates outgoing PDU to BER-format msg
         writes outgoing msg to socket

     - releases resources when server closes down


Z39.50 services

     IR application-independent code to process
     PDUs and invoke the application-specific
     functions (e.g. those in /zprise) that
     in turn invoke the IR application functions

     Based on the PDU choice id, process_PDU()
     invokes the routines (init, process, close)
     defined for the operation in op_table.c

                zservices.c   #includes:
                                        init.c  implemented
                                     present.c  implemented
                                      search.c  implemented

                                    op_table.c  Table of pointers to
                                                Z3950 operation functions
                                                (e.g., init_search, 
                                                search,...) indexed by
                                                PDU id.  

Application services


     This layer bridges any gap between the layer (zservices) above it, which
     knows about the Z39.50 protocol, and the particular IR application layer 
     below it (e.g. PRISE). This is the layer that may require adjustment when
     a new IR application is inserted.
              ap_services.c   #includes:
                                  ap_initdb.c       implemented for PRISE
                                  ap_search.c       implemented for PRISE
                                 ap_present.c       implemented for PRISE
              Extensions to         ap_help.c       implemented for PRISE 
              Z39.50 search      ap_generic.c

IR application


    This is the IR application proper.

See"Adding/substituting your own search engine" for more detailed information on how the zserver interfaces to an IR application.

Zserver's Static Calling Structure

 -> init_zserver() 
 -> zserver()                      Loop awaiting/processing incoming msgs
     --> process_msg()             Handle single message from socket
        ---> ber2pdu()             Translate BER-encoded msg into PDU
        ---> process_PDU()         Take a request PDU; return a response PDU  
              ----> ap_table_entry.init ==                   Initialization
                                                             by operation
                     > init_search()                         (no init_init())
                     > init_present()                  

             ----> ap_table_entry.proc ==                    Main processing
                                                             by operation

                     > init()

                       -----> get_db_list()
                       -----> init_default_pdus(

                     > search()

                       -----> MakeSearchResponse()

                       -----> ap_link()                      Find ap_service_-
                                                             entry for IR app.
                                                             given db. name

                       -----> ap_service_entry.delete ==     Release result
                                > ap_delete()                set resources

                       -----> ap_service_entry.init_search == Get ready to 
                                > init_ap_search()           search
                       -----> ==     Perform search
                                > ap_search()

                       -----> get_elementSetType()           Get some format

                       loop-> ap_service_entry.present ==    Get a database
                                > ap_present()               record and turn
                                                             it into a 
                                                             retrieval recd.

                            > InsertResponseRecord()         Add retrieval
                                                             record or sur- 
                                                             rogate to the
                       -----> ap_service_entry.close_search ==
                                > close_ap_search()

                     > present()

                       -----> MakePresentResponse()

                       -----> ap_service_entry.init_present ==
                                > init_ap_present()

                       -----> get_elementSetType()

                       loop-> ap_service_entry.present ==    See above
                                > ap_present()

                            > InsertResponseRecord()         See above

                       -----> ap_service_entry.close_present ==
                                > close_ap_present()

             ----> ap_table_entry.close ==                   Close processing
                                                             by operation 
                     > close_search()

                     > close_present()

        ---> pdu2ber()                                       Translate out-
                                                             PDU to BER-
                                                             encoded msg.
        ---> FreePDU()

        ---> FreePDU()

 -> close_zserver()

    --> free_dbs()

Notes on Error Handling

In general the Zserver has been written to be tolerant of invalid requests. So, for example, the zserver looks for an element set name of F or B but finding G instead or none, it uses its default. If the result set contains 3 records and the client asks for 4 starting with the third, the zserver just returns as many as it can (1).

The zserver always attempts to write a message to the log file

when it encounters a processing error. It attempts to communicate the situation to the client via the PDU and stay up. However, in the following circumstances the zserver will log a message and exit:

- Errors encountered before the msg-read/write loop in process_msg() is entered

- Errors encountered down in the PRISE search engine

- Memory allocation errors

Note that socket processing takes over standard in, out, and error, so writing to standard error will bring the zserver down.

Last modified: 8. March 1996