Chapter 6: Error Handling

This chapter describes how errors are reflected and handled in the CORBA model. User exceptions and system exceptions are discussed. If your platform does not support the C++ try and catch statements, an alternative error mechanism is discussed. This chapter includes the following major sections:

Exceptions in the CORBA Model

System Exceptions

User Exceptions

Exceptions in the CORBA Model

The CORBA specification defines a set of system exceptions that can be raised when errors occur in the processing of a client request. You can define user exceptions in the IDL interface for an objects you create and specify the circumstances under which those exceptions are to be raised. If an object raises an exception while handling a client request, the ORB is responsible for reflecting this information back to the client.

The Exception Class

VisiBroker uses C++ classes to represent both system and user exceptions. Since both types of exceptions require similar functionality, SystemException and UserException classes are derived from a common Exception class. When an exception is raised, your application can narrow, or cast down, from the Exception class to a specific UserException or SystemException. The following example shows portions of the Exception class definition.


class Exception

{

...

public:

Exception(const Exception &);

~Exception();

Exception &operator=(const Exception &);

...

friend ostream& operator<<(ostream& strm,

const Exception& exc);

const char *_name() const;

const char*_repository_id() const;

};

Methods Provided by the Exception Class

All exceptions have a name and a repository ID, though the name of the exception name is sufficient for error reporting. The repository ID includes the name as well as additional information about the exception. You can invoke the _name and _repository_id methods on an exception to obtain this information.

Assume you have a client application that requests a bind for an object whose server is currently not running, causing an exception to be raised. If your application called the _name method on the exception object it would return a string containing "CORBA::NO_IMPLEMENT". If your application called the _repository_id method, it would return a string containing "IDL:obg.omg/CORBA/NO_IMPLEMENT:1.0".

System Exceptions

System exceptions are usually raised by the ORB, though it is possible for object implementations to raise them using the Implementation Event Handler discussed in Chapter 7. When the ORB raises a SystemException, it will be one of the CORBA-defined error conditions shown in a later section.


class SystemException: public Exception

{

public:

static const char *_id;

virtual ~SystemException();

ULong minor() const;

void minor(ULong val);

CompletionStatus completed() const;

void completed(CompletionStatus status);

...

static SystemException *_narrow(Exception *exc);

private:

ULong _minor;

completion_status _status;

...

};

Completion Status

System exceptions have a completion status that tells you whether or not the operation that raised the exception was completed. The CompletionStatus enumerated values are shown below. COMPLETED_MAYBE is retuned when the status of the operation cannot be determined.


enum CompletionStatus {

COMPLETED_YES = 0;

COMPLETED_NO = 1;

COMPLETED_MAYBE = 2;

};

You can retrieve and set the completion status using these SystemException methods.

CompletionStatus			completed();
void completed(CompletionStatus status);

Getting and Setting the Minor Code

You can retrieve and set the minor code using these SystemException methods. Minor codes are used to provide better information about the type of error.


ULong		minor() const;

void minor(ULong val);

Casting to a SystemException

The design of the VisiBroker exception classes allows your application to catch any type of exception and then determine its type by using the _narrow method. A static method, -_narrow accepts a pointer to any Exception object. If the pointer is of type SystemException, _narrow will return the pointer to you. If the pointer is not of type SystemException, _narrow will return a NULL pointer.

CORBA-defined system exceptions

The following section lists the CORBA-defined system exceptions. The exception name is given first followed by the description.

UNKNOWN

Unknown exception.

BAD_PARAM

An invalid parameter was passed.

NO_MEMORY

Dynamic memory allocation failure.

IMP_LIMIT

Implementation limit violated.

COMM_FAILURE

Communication failure.

INV_OBJREF

Invalid object reference specified.

NO_PERMISSION

No permission for attempted operation.

INTERNAL

ORB internal error.

MARSHAL

Error marshalling parameter or result.

INITIALIZE

ORB initialization failure.

NO_IMPLEMENT

Operation implementation not available.

BAD_TYPECODE

Invalid typecode.

BAD_OPERATION

Invalid operation.

NO_RESOURCES

Insufficient resources to process request.

NO_RESPONSE

Response to request not yet available.

PERSIST_STORE

Persistent storage failure.

BAD_INV_ORDER

Routine invocations out of order.

TRANSIENT

Transient failure.

FREE_MEM

Unable to free memory.

INV_INDENT

Invalid identifier syntax.

INV_FLAG

Invalid flag was specified.

INTF_REPOS

Error accessing interface repository.

BAD_CONTEXT

Error processing context object.

OBJ_ADAPTOR

Failure detected by object adaptor.

DATA_CONVERSION

Data conversion error.

OBJECT_NOT_EXIST

Object is not available.

Handling System Exceptions

Your applications should always check for system exceptions after making ORB-related calls. The following example illustrates how you might enhance the library client application, discussed in Chapter 2, to print an exception using the << operator.

Note: If the C++ compiler for your platform does not support exceptions, see page 6-13 for a discussion on using CORBA-defined Environments for handling exceptions.


	....

library *library_object;

try {

library_object = library::_bind();

}

// Check for errors

catch(const CORBA::Exception& excep) {

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

cout << excep; << endl;

return(0);

}
...

If you were to execute the client application with these modifications without a server present, the output shown in the following example would explain that the operation did not complete and the reason for the exception.

Error binding to library:
Exception: CORBA::NO_IMPLEMENT
Minor: 0
Completion Status: NO

Narrowing to a System Exception

You can modify the library client application to attempt to narrow any exception that is caught to a SystemException. Figure 6-7shows how you might modify the client application. The following example shows how the output would appear if a system exception occurred.


	....

library_var *library_object;

try {

library_object = library::_bind();

}

// Check for errors

catch(const CORBA::Exception& excep) {

CORBA::SystemException *sys_excep;

sys_excep = CORBA::SystemException::_narrow(&excep);

if(sys_excep != NULL) {

cout << "System Exception occurred:" << endl;

cout << " exception name: " <<

sys_excep->name() << endl;

cout << " minor code: " <<

sys_excep->minor() << endl;

cout << " completion code: " <<

sys_excep->completed() << endl;

} else {

cout << "Not a system exception" << endl;

}

return(0);

}

...

The following example shows output from the system exception.

System Exception occurred:
exception name: CORBA::NO_IMPLEMENT
minor code: 0
completion code: 1

Catching System Exceptions

Rather than catching all types of exceptions, you may choose to specifically catch each type of exception that you expect. The following example shows this technique.


	....

library_var *library_object;

try {

library_object = library::_bind();

}

// Check for errors

catch(const CORBA::SystemException& excep) {

cout << "System Exception occurred:" << endl;

cout << " exception name: " <<

sys_excep->name() << endl;

cout << " minor code: " <<

sys_excep->minor() << endl;

cout << " completion code: " <<

sys_excep->completed() << endl;

}

// Try catching other types of exceptions.

...

User Exceptions

Exceptions that can be raised by an object are called user exceptions. When you define your object's interface in IDL you can specify the user exceptions that the object may raise. The following example shows the UserException class that the IDL compiler will use to derive the user exceptions you specify for your object.


class UserException: public Exception

{

public:

...

static const char *_id;

virtual ~UserException();

static UserException *_narrow(Exception *exc);

};

Defining User Exceptions

Assume that you want to enhance the library application, introduced in Chapter 2, so that the library object will raise an exception. If the library object's book list is full and an attempt is made to add a book, you want a user exception named CapacityExceeded to be raised. The additions to the IDL specification for the library interface are shown in bold letters.


// IDL specification for book and library objects

struct book {

string author;

string title;

};

interface library {

exception CapacityExceeded {

};

boolean add_book( in book book_info)

raises(CapacityExceeded);

};

The IDL compiler will generate this C++ code for a CapacityExceeded exception class.


class library: public virtual CORBA::Object

{

...

class CapacityExceeded: public CORBA::UserException

{

public:

CapacityExceeded();

~CapacityExceeded();

static CapacityExceeded *_narrow(CORBA::Exception *exc);

...

};

...

};

On platforms that support C++ exceptions, the library and _sk_library classes generated by the IDL compiler from this specification will incorporate the throw directive into the add_book methods signature.


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

throw (library::CapacityExceeded);

Modifying the Object implementation

The Library object must be modified to use the exception by changing the add_book function prototype and throwing the exception under the appropriate error conditions.


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

throw (library::CapacityExceeded)

{

CORBA::Boolean ret;

if( (ret = bk_list.add_to_list(book_info)) == 0 )

throw library::CapacityExceeded();

return ret;

}

Catching User Exceptions

When an object implementation raises an exception, the ORB is responsible for reflecting the exception to your client application. Checking for a UserException is similar to checking for a SystemException. To modify the library client application to catch the CapacityExceeded exception, you would make modifications like those shown below.


...

try {

ret = library_object->add_book(book_entry);

}

// Check for System Exceptions
catch(const library::CapacityExceeded& excep) {
cout << "CapacityExceeded returned:" << endl;
cout << excep; << endl;
// Do any necessary clean-up
return(0);
}
...

Adding Fields to User Exceptions

You can associate values with user exceptions. Figure 6-16 shows how to modify the IDL interface specification to add a size value to the CapacityExceeded user exception.The object implementation that raises the exception is responsible for setting the value. The new value is printed automatically when the exception is put on the output stream.


// IDL specification for book and library objects

struct book {

string author;

string title;

};

interface library {

exception CapacityExceeded {

long size;

};

boolean add_book( in book book_info)

raises(CapacityExceeded);

};

Portability considerations

You may want to consider always using these macros in your applications since they automatically adapt to the capabilities of your C++ compiler. Applications that use these macros can be more easily ported to all supported platforms. There are two sets of compatibility macros; one set for compilers with exception support and one set for compilers without exception support. The defined constant _PMC_NOEXCEPTIONS determines which macro set will be used. If _PMC_NOEXCEPTIONS is not defined, then the compatibility macros will be mapped as shown in the following section.

The following section shows compatibility macro mapping for compilers that support exceptions. The macro name is given first followed by the macro expansion.

PMCTRY

try

PMCTHROW(type_name)

throw(type)

PMCTHROW_LAST

throw;

PMCCATCH(type_name, variable_name)

catch(const type &var)

PMCAND_CATCH

catch(const type &var)

PMCEND_CATCH

none

PMCTHROW_SPEC(x)

none or throw(x)

Understanding the Environment Class

The Environment class enables exceptions to be registered in your application's environment. Methods are provided that allow the PMC macros to determine if a system or user exception has occurred and obtain the details of the exception. If you use the PMC macros shown in Figure 6-17, you should not have to explicitly call these methods yourself.

VisiBroker creates a default Environment object for each process. If your platform supports threads, an Environment object is created for each thread.

The include file env.h contains the Environment class' definition.


class Environment

{

private:

Exception *_exception;

public:

Environment();

~Environment();

...

Exception *exception() const;

void exception(Exception *exp);

void clear();

...

};

Environment Methods

The PMC macros make use of the Environment class internally. If you do not want to use the PMC macros and do not have exception support, you can use the Environment class.

The exception method is used by PMCTHROW to raise an exception.

void		exception(Exception *exp);
This method is used by PMCCATCH to return the exception that has been set for the environment. If no exception has been set, a NULL pointer is returned.

Exception			exception(Exception *exp);
The clear method clears any exception that has been raised in the environment. This method is invoked after the exception has been retrieved.

void			clear();
The is_nil method determines if the supplied pointer is NULL. If the pointer is NULL, a value other than zero is returned. If the pointer is not NULL, zero is returned. The behavior of the is_nil method is defined in the CORBA specification.

static Boolean					is_nil(Environment_ptr env);
You can use the following CORBA class static method to obtain a pointer to the Environment object for the current process or current thread, if threads are supported.


class CORBA {

...

static Environment& current_environment();

...

}


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