Using Flick's Runtime Libraries

Flick's runtimes provide the definitions and functions that are necessary in order for you to compile Flick-generated stubs into working code. For instance, Flick's runtime header files provide the definitions of the macros used in generated stubs. Flick's runtime libraries provide functions to manage object references, interact with the underlying IPC layer, and so on.

Flick's current runtimes were written to provide the minimum functionality required by Flick-generated code. The runtimes are not intended to be highly flexible or composable or even complete; for instance, Flick's IIOP runtime for C stubs has minimal CORBA ORB functionality. The current set of runtimes is simply sufficient to get clients and servers talking to one another. Future versions of Flick will include more complete runtimes, or will include more code generators that work with runtimes developed by third parties.

4.1 The Runtime Libraries and Headers

Flick's runtime libraries are contained under the runtime/libraries directory of the Flick distribution:

libflick-iiop.a
The IIOP runtime library for C stubs, to be using with stubs generated by Flick's IIOP back end, flick-c-pbe-iiop.
libflick-suntcp.a
The ONC/TCP runtime library, to be used with stubs generated by Flick's ONC/TCP back end, flick-c-pbe-sun.
libflick-mach3mig.a
The Mach 3 runtime library, to be used with stubs generated by Flick's Mach 3 back end, flick-c-pbe-mach3mig.
libflick-trapeze.a
The Trapeze runtime library, to be used with stubs generated by Flick's Trapeze back end, flick-c-pbe-trapeze.

The runtime libraries for three of Flick's back ends are distributed separately and are not further described in this manual:

flick-c-pbe-iiopxx
As previously described, this back end generates C++ stubs that work with TAO version 1.0, the real-time ORB from Washington University in St. Louis. You will need a copy of TAO (from http://www.cs. wustl.edu/~schmidt/TAO.html or ftp://ace.cs.wustl.edu/pub/ ACE/) in order to run the code produced by Flick's IIOP/C++ back end.
flick-c-pbe-khazana
The Khazana runtime will be included as part of the Khazana distribution. See http://www.cs.utah.edu/flux/ for information about Khazana.
flick-c-pbe-fluke
The runtime library and header files for Fluke stubs are included as part of the Fluke operating system. See http://www.cs.utah.edu/flux/ for information about Fluke.

Flick's runtime header files are contained in the runtime/headers directories of your Flick source tree and your Flick build tree. While most of Flick's runtime headers are located in the Flick source tree, two of Flick's runtime header files are generated by Flick's configure script, and are therefore located in the object tree that you create when you build the Flick tools. To compile Flick-generated stubs, you must make sure that both the source- and object-tree runtime/headers directories are contained in your C compiler's system include path. Alternately, the Flick installation process (described in Section 2.3) will place all of the required header files in a single place on your system.

The rest of this chapter describes Flick's IIOP and ONC/TCP runtimes in detail. Chapter 5 shows how to use each of these runtimes in a simple phonebook application.

4.2 The IIOP Runtime

The IIOP runtime is a very minimal ORB-like system, implementing only the functions that are required to implement basic client/server communication. The current runtime should be sufficient, however, to allow Flick-generated stubs to communicate with any other implementation of IIOP, including programs running on top of other ORBs. The following sections describe how to use Flick's IIOP runtime. A complete application based on the IIOP runtime is described in Section 5.1.

4.2.1 Server Command Line Options

When an IIOP server starts, the Flick-generated main function calls CORBA_ORB_init to initialize the IIOP runtime and start the ORB. Flick's runtime searches for and processes the following command line arguments:

-ORBipaddr ipaddress
(Optional.) Specify the network interface for the server. This option is useful if your machine has multiple network interfaces. If unspecified, the server will use the host's primary network interface.
-ORBid name
(Optional.) Use name as the server's ORB identifier; see Section 14.26.1 of the CORBA 2.0 specification. This option is generally not used. The default ORB identifier is "FlickORB--".
-OAport portnumber
(Required.) Specify the port that the server will use. There is no default value; the port number must be explicitly given on the command line.
-OAid name
(Optional.) Use name as the server's BOA (Basic Object Adapter) identifier; see Section 14.26.2 of the CORBA 2.0 specification. Like -ORBid, this option is rarely used. The default BOA identifier is "FlickBOA--".

4.2.2 Creating Object Implementations

After initializing the ORB and the BOA (done for you by the Flick-generated main function), a server must create one or more object implementations (object instances) that will be available to service client requests. These implementations are created by a function called register_objects which you must write as part of your server's application code. The prototype for this function is:
    void register_objects(CORBA_ORB, CORBA_BOA, int argc, char **argv,
                          CORBA_Environment *);

The main function generated by Flick will call your version of register_objects in order to initialize your server's objects. In general, your register_objects function should parse the given command line in order to create its objects. Your function should call CORBA_BOA_create to create each of its object instances:1
    CORBA_Object CORBA_BOA_create(CORBA_BOA boa,
                                  CORBA_ReferenceData *obj_key,
                                  const char *obj_type, FLICK_SERVER obj_impl,
                                  CORBA_Environment *ev);

The first argument should be the CORBA_BOA that was passed to your register_objects function. The second argument is a pointer to an octet sequence that names the new object instance. This name must be unique across all object instances. As is usual, the sequence is described by a C structure containing three fields: _buffer, _length, and _maximum. The third argument is a string that identifies the type (class) of the object; this string may be in any format you choose. (Flick does not parse the object type string; Flick uses the string only when creating and parsing object references.) The fourth argument to CORBA_BOA_create is a pointer to the Flick-generated dispatch function for the object interface; as far as Flick's IIOP runtime is concerned, this is what determines the object's class. The name of the server dispatch function is usually the name of the interface concatenated with the suffix "_server". The fifth and final argument is a pointer to a CORBA_Environment; your register_objects function should check the environment after each call to CORBA_BOA_create to see if an error has occurred.

It is useful for your register_objects function to create any additional data structures that your server will need in order to represent its objects. For instance, the phonebook application in Section 5.1 creates a list of structures to represent its phonebooks; each structure is keyed by a CORBA_ReferenceData and contains the data for a single phonebook object. The server work functions later use CORBA_BOA_get_id to locate the data for a phonebook object, mapping from a CORBA object reference to the object's CORBA_ReferenceData, and then using that name to locate the phonebook object's data.

Your register_objects function should raise an exception if it is unable to initialize your server's objects. If no exception is raised, then the Flick-generated main server function will begin to process requests on the registered objects.

4.2.3 Creating Object References

A client can create an object reference with the following function:
    CORBA_Object CORBA_ORB_string_to_object(CORBA_ORB orb, CORBA_char *name,
                                            CORBA_Environment *ev);

The name can be either a stringified Interoperable Object Reference (IOR) or a URL-style name of the form "iiop:1.0//hostname :port /objecttype /objectname ".2 The latter form is generally preferred because it is easier to type. Whenever a Flick-based server creates an object instance, the server prints both the IOR and URL-style name of the new object.

Alternately, a client can create an object reference by going through the following steps:

  1. Initialize an ORB, using CORBA_ORB_init.
  2. Create a BOA, using CORBA_ORB_BOA_init.
  3. Create the object reference by calling CORBA_BOA_create with the appropriate CORBA_ReferenceData (key) value.

This procedure will find the corresponding object instance within the given ORB and create an appropriate object reference. Of course, CORBA_ORB_string_to_object is generally simpler for clients to use.

4.2.4 Using Object References

Once a client has obtained an object reference, it may use the reference to invoke operations on the object as defined by the object's IDL interface. Flick's IIOP runtime provides these additional functions for manipulating object references:

CORBA_char *CORBA_ORB_object_to_string(CORBA_ORB, CORBA_Object, CORBA_Environment *); Create and return a stringified Interoperable Object Reference (IOR).
CORBA_char *CORBA_ORB_object_to_readable_string(CORBA_ORB, CORBA_Object, CORBA_Environment *); Create and return a human-readable, URL-style stringified object reference. (This is not a standard CORBA function.)
CORBA_Object CORBA_ORB_string_to_object(CORBA_ORB, CORBA_char *, CORBA_Environment *); Create and return an object reference from a stringified object reference (IOR or URL).
CORBA_ReferenceData *CORBA_BOA_get_id(CORBA_BOA, CORBA_Object, CORBA_Environment *); Return the corresponding key for the given object reference.
CORBA_boolean CORBA_Object_is_nil(CORBA_Object, CORBA_Environment *); Return true if the object reference is nil.
CORBA_Object CORBA_Object_duplicate(CORBA_Object, CORBA_Environment *); Duplicate an object reference.
void CORBA_Object_release(CORBA_Object, CORBA_Environment *); Release an object reference.
CORBA_unsigned_long CORBA_Object_hash(CORBA_Object, CORBA_unsigned_long, CORBA_Environment *); Return a value suitable for use in a hash table. The value is not guaranteed unique, but good enough to implement an efficient hash table with the number of buckets indicated by the second argument.
CORBA_boolean CORBA_Object_is_equivalent(CORBA_Object, CORBA_Object, CORBA_Environment *); Return true if the two object references refer to the same object instance.

4.3 The ONC/TCP Runtime

Flick's ONC/TCP runtime was designed so that it would be possible for users to write an ONC RPC-based client or server, and then compile the application code to use either Flick-generated stubs or rpcgen-generated stubs. This requirement introduced a few oddities into Flick's ONC/TCP runtime but can be very helpful in demonstrating the speed gained by using Flick-generated stubs. (If you are curious, the files test/programs/sun/sunstat-work.c and test/programs/sun/sunstat-use.c in the Flick distribution show how to write code that is compatible with both Flick and rpcgen.)

4.3.1 Starting a Server

Flick's ONC/TCP back end creates a main function that automatically registers the interfaces described in the original IDL file. (In an ONC RPC IDL file, an interface is a program-version pair.) There are no objects to create or register; the server itself is the "object" in ONC RPC. The Flick-generated main function then executes the server main loop, receiving requests and sending replies.

If the Flick-generated main function is suitable for your application, then you need do nothing more. If, however, your server needs to perform special initialization, you will have to write your own main function to set up your application, register your interfaces, and then run the main server loop. The rest of this section describes the procedure for registering your interfaces with Flick's ONC/TCP runtime.

Your main function must initialize a FLICK_SERVER_DESCRIPTOR structure for each interface that it wants to register:
    typedef struct FLICK_SERVER_DESCRIPTOR {
            unsigned int prog_num;   /* The program number. */
            unsigned int vers_num;   /* The version number. */
            ...
    } FLICK_SERVER_DESCRIPTOR;

The values of prog_num and vers_num fields would normally be the program and version numbers listed in your original IDL file. After filling in the FLICK_SERVER_DESCRIPTOR structure, your server must call flick_server_register to register the interface with the runtime:
    /* Returns 1 for success or 0 for failure. */
    int flick_server_register(FLICK_SERVER_DESCRIPTOR, FLICK_SERVER);

The first argument is the structure containing the program and version numbers that was previously filled in. The second argument is a pointer to the Flick-generated server dispatch function for the interface. The name of this function is generally the name of the ONC RPC program, followed by an underscore and the version number of the interface: "program _version ".

After your main function has registered all of its interfaces, it must call the flick_server_run function. This function takes no arguments and should never return. If it does, there has been a fatal error.

4.3.2 Connecting to a Server

When a server is run, it registers itself with the RPC portmapper facility. Client processes can connect to a running server simply by specifying the host machine of the server and the program and version numbers of the desired interface. To connect to a server, a client process fills out a FLICK_SERVER_LOCATION structure:
    typedef struct FLICK_SERVER_LOCATION {
            char *server_name;      /* The host machine.   */
            unsigned int prog_num;  /* The program number. */
            unsigned int vers_num;  /* The version number. */
    } FLICK_SERVER_LOCATION;

Once this structure is initialized, the client calls flick_client_create to connect to the server:
    /* Returns 1 for success or 0 for failure. */
    int flick_client_create(CLIENT *, FLICK_SERVER_LOCATION);

The first argument must point to a CLIENT structure; that structure will be initialized by the call to flick_client_create.

4.3.3 Using the Server Connection

Once a client has established a connection to a server, it must pass the initialized CLIENT data to the Flick-generated stub functions in order to make RPCs. When the client is ready to close its connection to the server, it should call flick_client_destroy:
    void flick_client_destroy(CLIENT *);

4.3.4 Using the Service Request Data (svc_req)

The second argument to an ONC RPC-style server work function is generally a pointer to a struct svc_req, a structure that describes the context of the current RPC: the program, version, and operation numbers, the client's credentials, and so on. When writing a server work function, you must include this argument in your function's parameter list; see Section 5.3.2 for examples.

However, beware: Flick's ONC/TCP runtime does not currently initialize the structure that is passed to your work function! Currently, Flick-generated stubs include this parameter simply to be prototype-compatible with rpcgen-generated code. In a future version of Flick, the correct svc_req data will be provided to server work functions.