Assignment 2, CS-701, Fall 1997

Object Broker Project-- Use Case "2a"

Use Case "2a" -- Change Stucture of om Source Code

Introduction

This part of the project is not really a Use Case because the resulting program will seem no different to the user from the version of om that you already built. In fact, it is possible that you already have written your program in a way that meets the requirements given here. Just make sure that your code meets these requirements before you start to work on Use Case 3.

The rationale for this step is that both om and the ob to be developed for Use Case 3 share some common features, so they should also share common code where possible. In particular, both programs will generate a log file that has a name derived from the name of the program and which contains a timestamped list of messages. Also, both programs receive datagrams that contain requests, which are strings that are in the general format:

      <request type> <argument> ...
Both om and ob use the <request type> (sometimes called the keyword)part of a request to select a request processing function to be called, and pass a string consisting of all the <argument>s separated by spaces to the function. The request processing function returns another string, called the reply. (A client sends a request in a datagram to om or ob, and om or ob sends the reply in another datagram to the client.)

The requirement being introduced here is to implement log file management and request processing function management in separately compiled source modules.

Log File Management

The interface to the log file could be implemented using two or three methods, one to open the file, one to write a message to it, and possibly a third one to close it. The first method should be passed argv[0] from main() and do all the work to construct the proper name for the file and to create it. The second method should be passed a string, and should generate a the full log message, including the timestamp, and write it to the log file.

Put functions prototypes for your log file management functions in a header file, put their definitions (implementations) in a .cc file, and adjust your Makefile so that building om links the object file for the main program with the object file for the log file management functions. Be sure to #include the header file in both the source modules. Here is what you might have in your Makefile, assuming you have defined the OM_OBJS and LDFLAGS macros properly:

      om : $(OM_OBJS)
      <tab> gcc $(OM_OBJS) $(LDFLAGS) -o om 

Request Processing Function Management

In the description of Use Case 1, you were told to construct a dispatch table for locating and calling request processing functions. Pseudocode for om would include the following endless loop:
      for (;;) {

        1.  Receive a request in a datagram.

        2.  Parse the datagram into two strings, the <request
        type> and the list of <argument>s.
 
        3.  Search for the <request type> in your dispatch
        table, and call the request processing function, passing
        it the second string (the list of <argument>s) as
        its argument.
 
        4.  If the <request type> was not in the
        dispatch table, send a datagram containing the string
        "*** unrecognized request ***" to the
        client.
 
        5.  If the <request type> was in the dispatch
        table, send a datagram containing the string returned by
        the request processing function to the client.
 
        }
Put the code for Steps 2 and 3 of this pseudocode into a separate source file. You might have one function or two; I think a single function is easier to manage. It would receive the datagram as an argument and would return either the "*** unrecognized request ***" string or whatever string was returned by the request processing function. Put the function prototype for this function (these functions) in a separate header file, and #include this header file in both your main program's source file and the source file containing the function's (functions') definition(s).

What else you do for managing request processing functions depends on how you implemented your dispatch table. If you used an array of structs as described in class, you need to make the array global to both the main .cc file and the one that contains the function(s) just described. The way to manage that is to declare the array as an extern in the header file, and to provide the initialization of the array in (either) one of the .cc files. Here is some sample code. (You must use meaningful function and variable names!)

fun.hom.ccfun.cc
typedef char *rpf(char *)

rpf do_exit;
rpf do_listClasses;

struct dt {
  char *n;
  rpf *f;
  };

extern dt tab[];

// Prototype for parser/dispatcher
char *p_a_d(char*);
#include "fun.h"

dt tab[] = {
  {"exit", do_exit },
  {"listClasses", do_listClasses},
  };

    ...

// Step 2 of above algorithm
recvfrom ( ... &d ... );
// Steps 2 & 3 of above algorithm
r = p_a_d(d);
// Steps 4 & 5 of above algorithm
sendto( ... r ... );
#include "fun.h"
char*
p_a_d(char *k_a) {

  // Break k_a into k and a
    ...

  // Find k in tab[]
    ...

  // Pass a to the processing function
  // and return whatever it returns
  return tab[i].f(a);
    ...

  }
The decision about where to implement the actual request processing functions is up to you. They could be defined in either of these .cc files, or in yet another one. (Don't forget to #include the header file if you use another one.)

If you decide instead to use an object-oriented approach, you would have a class that holds the dispatch table and which has member functions for adding items to the table and for searching the table for a particular request type string. In this implementation, the class declaration goes in the header file, and the class method definitions go in a separate .cc file. You might reasonably decide to implement the dispatch table as a linked list if you follow this approach.

In either case, you will now need to update the definitions of OM_OBJS in your Makefile, and you will have to be sure to update the dependencies for each object module to include the proper header files:

      depend : $(OM_SRCS) $(HDRS)
      <tab> makedepend $(OM_SRCS)

Christopher Vickery
Computer Science Department, Queens College of CUNY