Netscape Internet Service Broker for C++ Programmer's Guide

[Contents] [Previous] [Next]

Chapter 5
Error Handling

This chapter describes how errors are reflected and handled in the CORBA model. User exceptions and system exceptions are discussed. This chapter includes the following major sections:

Exceptions in the CORBA Model

System Exceptions

User Exceptions

NOTE: If the C++ compiler for your platform does not support exceptions, see Appendix A for a discussion on using CORBA-defined Environments for handling 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 IDL interface 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

ISB for C++ uses C++ classes to represent both system and user exceptions. The Exception is the base class for both the SystemException and UserException classes. When an exception is raised, an application can narrow, or cast down, from the Exception class to a specific UserException or SystemException. The following code listing shows portions of the Exception class definition.

Portions of the Exception class definition.

class Exception
{
   ...
   public:
      Exception(const Exception &);
      ~Exception();
      Exception &operator=(const Exception &);
      ...

      friend ostream& operator<<(ostream& strm, const char *_name() const;
      const char*_repository_id() const;
};

Methods Provided by the Exception Class

All exceptions have a name and a repository ID, although 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.

For example, assume that a client application tries to bind to an object whose server is currently not running, causing an exception to be raised. If the application called the _name method on the exception object it would return a string containing "CORBA::NO_IMPLEMENT". If the 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, although it is possible for object implementations to raise them using the Implementation Event Handler. When the ORB raises a SystemException, it will be one of the CORBA-defined exceptions.

The SystemException class.

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.

The CompletionStatus values.

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

ISB for C++ exception classes allow an 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.

Table 5.1 CORBA-defined system exceptions. 
Exception Name

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 code example illustrates how you might enhance the library client application to print an exception using the << operator.

NOTE: If the C++ compiler for your platform does not support exceptions, see Appendix A for a discussion on using CORBA-defined Environments for handling exceptions.
Printing an exception.

....
library_var library_object;
try {
...
rc = resolveURI(host, port, uri, p);
library_object = library::_narrow(p);
...
}
// Check for errors
catch(const CORBA::Exception& excep) {
cout << "Error binding to library:" << endl;
cout << excep; << endl;
return(0);
}
...
If you executed the client application with these modifications without a server present, the output shown below would explain that the operation did not complete and give the reason for the exception.

Output from modified library client application.

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. The following code example shows how you might modify the client application.

Narrowing an exception to a system exception.

...
library_var library_object;
try {
   ...
   rc = resolveURI(host, port, uri, p);
   library_object = library::_narrow(p);
   ...
}
// 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 listing shows how the output would appear if a system exception occurred.

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 code example shows this technique.

Catching specific types of exceptions.

...
library_var library_object;
   try {
     rc = resolveURI(host, port, uri, p);
     library_object = library::_narrow(p);
   }
   // 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 code listing below shows the UserException class that the IDL compiler will use to derive the user exceptions you specify for your object.

The UserException class.

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

Defining User Exceptions

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

The CapacityExceeded class generated by the IDL compiler.

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 method's signature.

The new add_book method 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.

Modifying the object implementation to throw an exception.

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.

Catching a UserException.

...
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. The code listing below 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.

Adding a value to the CapacityExceeded exception.

// 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 table.

Table 5.2 Compatibility macro mapping for compilers that support exceptions.
Macro Name 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)

About 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, you do not have to explicitly call these methods yourself. ISB for C++ 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.

The Environment class.

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();
   ...
}


[Contents] [Previous] [Next]

Last Updated: 02/03/98 15:31:59


Copyright © 1997 Netscape Communications Corporation

Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use