The Library Application
Compiling the Client and Server
The Library Application
In this chapter, you will build a sample client application that adds a book to a library's book server. The book server could be used by a university's library to track the books in its inventory. The client application could be used by the university's purchasing department to add titles when new books arrive. Application Development
You will use the following steps to develop and run the sample application.
library
object will only offer a single method named add_book
to add a book to the library.
book
structure consists of just two strings; one for the author of the book and one for the book's title. The library
object's add_book
method requires a struct book
as its only argument.
struct book {> string authors;string title;
}; interface library { boolean add_book(in book book_info);
};
prompt> orbeline lib.idlFor more information on the command line options for the IDL compiler, see the VisiBroker for C++ Reference Guide.
book
structure as well as a C++ definition for the library
class. The IDL compiler also generates a book_var
class that acts as a wrapper for the book
structure.You may find it more convenient to use the book_var
class rather than the book
structure.
struct book { CORBA::String_var author;CORBA::String_var title;
// operator= is generated for internal use
.
.
.
};
class book_var{
public:
book_var();
book_var(book *ptr);
book_var(const book_var& var);
~book_var();
book_var& operator=(book *ptr);
book_var& operator=(const book_var& var);
book *operator->();
operator book *();
operator book &();
. . .
// other methods for internal use
private:
book *_ptr;
};
library
class definition generated in lib_client.hh contains the add_book
method specified in the IDL file, along with a variety of other methods. The lib_client.cc file contains the C++ implementation of methods for use by the client application as well as internally used methods. Your client application will use the add_book
method to send an "add book" request to the library server.
class library: public virtual CORBA::Object
{ private:... // methods used internally
public:
static library_ptr _duplicate(library_ptr obj);
static library_ptr _nil();
static library_ptr _narrow(CORBA::Object *obj);
static library_ptr _bind(
const char *object_name = NULL,
const char *host_name = NULL,
const CORBA::BindOptions* opt=NULL);
virtual CORBA::Boolean add_book(
const book& book_info);
...
};
_bind
method, the ORB locates and establishes a connection with the library server and returns a handle to the library object. If the ORB cannot locate or connect to the library server object, the _bind
method will return NULL
and a system exception will be raised. The binding process is described in detail in Chapter 3.
the add_book Method
The add_book
method generated by the IDL compiler for your client application is actually a stub method. When your client application calls add_book, a request is sent to the ORB with all the necessary parameters. The ORB ensures that the request is sent to the library server object. Once the method is executed on the server, the ORB returns the results to your client application. Other Methods
Several other methods are provided that allow your client application to duplicate, initialize and narrow a library
object reference. These methods are not used in the example client application, but they are discussed in detail in Chapter 3.
The library_var Class
A class named library_var
is also generated by the IDL complier, though it is not used in the example application. The library_var
class adds the ability to automatically delete object references when the object is deleted or re-initialized. The _var
classes are described in detail in Chapter 10.
The Server Files
The include file lib_server.hh contains the C++ definitions for the _sk_library
class that you use to derive the implementation of the library object server. This class contains a skeleton method _add_book
. This skeleton method is used by the ORB on the server side to unpack the parameters from your client application's "add book" request and invoke the actual add_book
method on the server object._sk_library
class definition contained in the lib_server.hh file._add_book
method and other methods that are used internally by the ORB.
class _sk_library: public library{ ...
public:
...
/* The following operations need to be implemented
* by the server */
virtual CORBA::Boolean add_book(
const book& book_info) = 0;
/* The following operations are implemented* automatically */
static void _add_book(void *obj,
CORBA::MarshalStream &strm,
CORBA::Principal_ptr principal,
const char *oper);
};
Library
class and implement the main
routine. To create the server's Library
class, you must first understand its relationship with the client's library
class and the _sk_library
class.
Library
server class that you implement is derived from the _sk_library
class that was generated by the IDL compiler. Look closely at the _sk_library
class definition and notice it is derived from the library
class defined in the lib_client.hh file. The following figure shows the class hierarchy.
Library
class is the actual implementation of the library object, defined in the libsrv.h file. This class uses a book_list
class to provide a fixed array of book
structures. The method add_to_list
is called by the Library::add_book
method. This file is not generated by the IDL compiler.
#include <lib_server.hh>
const CORBA::ULong MAX_BOOKS = 3;
class book_list { private:short _book_count;
book* _book_array[MAX_BOOKS];
public:
book_list() { _book_count = 0; }
~book_list() {
for (int i=0; i < _book_count; i++) {
delete _book_array[i];
}
}
CORBA::Boolean add_to_list (const book &bk) {
if (_book_count >= MAX_BOOKS)
return 0;
} else {
_book_array[_book_count] = new book(bk);
_book_count++;
return 1
} };
class Library: public _sk_library{ private:
book_list bk_list;
public:
Library(const char *object_name = NULL);
CORBA::Boolean add_book(const book& book_info);
};
Library::add_book
method as well as the object's constructor. In our example these implementations are placed in the lib_srvr.C file, along with the main
routine. This code is not generated by the IDL compiler.The
Library::Library
constructor must call the _sk_library
constructor to perform internal initialization and to register the object's interface with the ORB.The
add_book
method involves a simple call to the bk_list
object's add_to_list
method, the Library
class' internal representation of the list of books.
#include <lib_server.hh>int main(int argc, char *const *argv)
{ // Initialize ORB and Basic Object Adaptor (BOA)
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
CORBA::BOA_var boa = orb->BOA_init(argc, argv);
// Instantiate Library ClassLibrary library_server;
// Notify BOA that object is readyboa->obj_is_ready(&library_server);
// Begin event loop of receving messagesboa->impl_is_ready();
return(1);
}
// Library constructorLibrary::Library(const char *object_name) :
_sk_library(object_name)
{ }
// Add book methodCORBA::Boolean Library::add_book(const book& book_info)
{ return bk_list.add_to_list(book_info);
}
Library
object, the main routine must make two calls; one to the ORB and the other to the Basic Object Adaptor (BOA). The BOA is the interface between the object implementation and the ORB. The BOA allows your object to notify the ORB when it is ready to accept client requests.The
argc
and argv
parameters are the same parameters passed to the main routine. These parameters are described in "Advanced Networking Options" and can be used to specify options for the ORB and BOA.
After instantiating the
Library
object, the server tells the BOA that the object is ready by invoking the obj_is_ready
method.impl_is_ready
method to start the event loop that receives client requests. The details of the event loop are discussed in Chapter 4.
#include <lib_client.hh>
main(int argc, char *const *argv)
{
CORBA::Boolean ret; Implementing the Client
The file named lib_clnt.C contains the library client application. The application accepts two parameters, the author and title of a book. These parameters will be used to initialize a book
structure which will then be used as a parameter to the library
object's add_book
method. Since your application will use the library
class, it must include the lib_clnt.h file. Initialization
The first thing your client application needs to do is initialize the ORB.#include <iostream.h>
// Initialize the ORB
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
...
Checking the Parameters
After initializing the ORB, your application validates the author and book title parameters and creates a book
structure. For more information on using argc
and argv
, see "ORB_init Options". Notice that argv[1]
and argv[2]
are cast to const char *
. This casting causes memory to be allocated automatically because the author and title are defined as String_var
types (see the example in Client Files).
...if(argc < 3) {
cout << "You must specify an author and title"
<< endl;
return(0);
} book book_entry;
book_entry.author = (const char *)argv[1];
book_entry.title = (const char *)argv[2];
...
add_book
method, it must first invoke the _bind
method. The implementation of the _bind
method is generated automatically by the IDL complier. The _bind
method requests the ORB to locate and establish a connection to the library server. If the server is successfully located and a connection is established, a proxy object is created to represent the server's Library
object. It is a reference to the proxy object that is returned to your client application.If the
_bind
method fails, a system exception is raised. You should use the try
and catch
statements to detect any failures, print a message and exit the client application.
...Note: If your platform's C++ compiler does not supportlibrary *library_object;
try {
library_object = library::bind();
}
catch(const CORBA::Exception& excep) {
cout << "Error binding to library object" << endl;
return(0);
} ...
try
and catch
, you can use the VisiBroker macros PMCTRY
and PMCCATCH
, described in Appendix A of this guide.
add_book
method, like the _bind
method, should use try
and catch
to handle any exceptions that may be raised. The add_book
method on the client side is actually a stub generated by the IDL compiler that marshals all the data required for the request so that it can be sent to the object server.
...The following example shows the complete library client application.try {
ret = library_object->add_book(book_entry);
} catch(const CORBA::Exception& excep) { cout << "Error adding book" << endl;
CORBA::release(library_object);
return(0);
} ...
#include <instream.h>#include <lib_client.hh>
main(int argc, char *const *argv)
{ CORBA::Boolean ret;
// Initialize the ORB
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
if(argc < 3) { cout << "You must specify an author and title"
<< endl;
return(0);
}
book book_entry;
book_entry.author = (const char *)argv[1];
book_entry.title = (const char *)argv[2];
// Declare the library objectlibrary *library_object;
try {// Locate object and return a pointer to it
library_object = library::_bind();
} // Check for errors
catch(const CORBA::Exception& excep) {
cout << "Error binding to library object" << endl;
return(0);
}
// perform the add_book invocation on library_objecttry { ret = library_object->add_book(book_entry);
} // Check for errors
catch(const CORBA::Exception& excep) {
cout << "Error adding book" << endl;
CORBA::release(library_object);
return(0);
} if(ret == 1) { cout << "Book added successfully" << endl; } else { cout << "Unable to add book" << endl;
}
CORBA::release(library_object);
return(1);
}
CC = CC # set to your C++ compilerORBDIR = /usr/local/vbroker # directory where VisiBroker was installed
CCINCLUDES = -I. -I$(ORBDIR)/include
CCFLAGS = $(CCINCLUDES) # compiler flags you might need
# such as "-g"
ORBLIB = -L$(ORBDIR)/lib -lorb # The VisiBroker library (single threaded)
LDFLAGS = -lsocket -lnsl -ldl # System libraries required by Solaris
SRCS = lib_client.cc lib_server.cc library_client.cc library_server.cc
.SUFFIXES: .o .cc .hh
.cc.o:$(CC) $(CCFLAGS) -c -o $@ $<
.C.o:$(CC) $(CCFLAGS) -c -o $@ $<
all: lib_client lib_server
library_client: lib_client.o lib_clnt.o
$(CC) -o lib_client lib_client.o \
lib_clnt.o $(ORBLIB) $(LDFLAGS)
library_server: lib_server.o lib_srvr.o lib_client.o$(CC) -o lib_server lib_server.o \
lib_srvr.o lib_client.o $(ORBLIB) $(LDFLAGS)
clean:rm -f *.o *.hh *.cc core lib_client lib_server
The following figure shows setting the ORBELINE environment variable with csh.
prompt> setenv ORBELINE /usr/local/vbroker/admThe following figure shows setting the ORBELINE environment variable with the Bourne shell.
prompt> ORBELINE=/usr/local/vbroker/adm
prompt> export ORBELINE
prompt> osagent &
prompt> lib_server &
prompt> lib_client Ovid Metamorphosis