Chapter 2: Getting Started

This chapter describes the development of distributed, object-based applications with VisiBroker for C++ on Unix platforms. A sample application is used to illustrate each step of the development process. It includes the following major sections:

The Library Application

Application Development

Running the IDL Compiler

The Client Files

Implementing the Server

Implementing the Client

Compiling the Client and Server

Running the Client and Server

Conclusion

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.

The directory examples/library, located within the directory where your VisiBroker package was installed, contains the files discussed in this chapter. If you do not know the location of the VisiBroker package on your system, see your administrator.

Note: Some of the file names used as examples in this chapter have more than eight characters and may not work with your platform.

Application Development

You will use the following steps to develop and run the sample application.

  1. Identify the objects required by the application.
  2. Write a specification for the objects using the Interface Definition Language (IDL).
  3. Use the IDL compiler to generate the client stub code and server skeleton code.
  4. Write the client application code.
  5. Write the object server code.
  6. Compile the client and server code.
  7. Start the object server.
  8. Run the client application.

The Library Object

One of the objects in your sample application is the library that contains a selection of books. A complete library server would probably offer methods to checkout and check-in books as well as add, remove and search for books. In this simple example, our library object will only offer a single method named add_book to add a book to the library.

Defining the Library Objects

The file lib.idl, shown below, contains the IDL specifications for the book structure and the library interface. The 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);

};

Running the IDL Compiler

The VisiBroker IDL compiler is named orbeline. Since your lib.idl file requires no special handling, it can be complied by typing the following command.

prompt> orbeline lib.idl
For more information on the command line options for the IDL compiler, see the VisiBroker for C++ Reference Guide.

Code Generation

The IDL compiler generates four files; lib_client.cc, lib_client.hh, lib_server.cc and lib_server.hh. Two of the files are for building the client application and two are for building the object server. All generated files have either a "cc "or "hh" suffix to help you distinguish them from source files that you create, which should use the "C" and "h" extensions.

The Client Files

The include file lib_client.hh contains the C++ type definitions for the 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;

};

The Library Class

The 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);

...

};

The _bind Method

When your application invokes the _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.

The add_book method is a pure virtual function. You create the actual implementation of this method for the library server object. The following excerpt shows the _sk_library class definition contained in the lib_server.hh file.

The lib_server.cc file contains the implementation for the _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);

};

Implementing the Server

There are two tasks you must complete to implement the library object server; create the server's 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.

The library Class Hierarchy

The 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.

Creating the Library Class

The 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);

};

Implementing Library Methods

You must provide implementations for the server's 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 Class

Library library_server;


// Notify BOA that object is ready

boa->obj_is_ready(&library_server);


// Begin event loop of receving messages

boa->impl_is_ready();

return(1);

}

// Library constructor

Library::Library(const char *object_name) :

_sk_library(object_name)

{ }
// Add book method

CORBA::Boolean Library::add_book(const book& book_info)

{ return bk_list.add_to_list(book_info);

}

The main Routine

Before instantiating the 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.

Lastly, the server calls the impl_is_ready method to start the event loop that receives client requests. The details of the event loop are discussed in Chapter 4.

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>

#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);
...

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];

...

Binding to the Library Server

Before your client application invokes the 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.

...

library *library_object;

try {

library_object = library::bind();

}

catch(const CORBA::Exception& excep) {

cout << "Error binding to library object" << endl;

return(0);

} ...

Note: If your platform's C++ compiler does not support try and catch, you can use the VisiBroker macros PMCTRY and PMCCATCH, described in Appendix A of this guide.

Adding the Book

The invocation of the 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.

...

try {

ret = library_object->add_book(book_entry);

} catch(const CORBA::Exception& excep) { cout << "Error adding book" << endl;

CORBA::release(library_object);

return(0);

} ...

The following example shows the complete library client application.

#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 object

library *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_object

try { 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);

}

Compiling the Client and Server

The lib_clnt.C file that you created and the lib_client.cc file generated by the IDL compiler to create the client application are compiled and linked together. The lib_srvr.Clib_srvr.C file that you created, along with the lib_server.cc and the lib_client.cc files generated by the IDL compiler, are compiled and linked to create the library server. Both the client application and the library server must be linked with the VisiBroker liborb library.

Selecting a Makefile

The library subdirectory of the examples directory of your VisiBroker release contains an appropriate makefile for your platform. You may need to customize the makefile to work with your environment. Shown below is a sample makefile for the Solaris SPARCworks C++ compiler.


CC = CC					# set to your C++ compiler

ORBDIR = /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

Running the Client and Server

Now that you have compiled your client application and server, you are ready to run your first VisiBroker application. Running the client application involves these steps:

  1. Set your environment variables
  2. Start the OSAgent.
  3. Start the library server
  4. Run the library client application.

Setting the VisiBroker Environment Variables

The environment ORBELINE must be set to point to the directory that contains your VisiBroker license file.

The following figure shows setting the ORBELINE environment variable with csh.

prompt> setenv ORBELINE /usr/local/vbroker/adm
The following figure shows setting the ORBELINE environment variable with the Bourne shell.

prompt> ORBELINE=/usr/local/vbroker/adm
prompt> export ORBELINE

Starting the osagent

Before you run either your client application or the library server, you must first start the directory service daemon, osagent. The osagent is described in Chapter 5.

prompt> osagent &

Starting the Library Server

Start your library server by typing:

prompt> lib_server &

Running the Client

T o run your client and add the book Metamorphosis by Ovid, type:

prompt> lib_client Ovid Metamorphosis

Conclusion

Congratulations! You have just completed the library application and have been introduced to all of the basic features of VisiBroker. The remaining chapters in this guide will cover the details you will need to create more complex and powerful applications.




[Preface] [Chapter 1] [Chapter 2] [Chapter 3] [Chapter 4] [Chapter 5] [Chapter 6]
[Chapter 7] [Chapter 8] [Chapter 9] [Chapter 10] [Chapter 11] [Chapter 12] [Appendix A]