VisiBroker for C++ Developer’s Guide : Handling exceptions

Handling exceptions
Exceptions in the CORBA model
The exceptions in the CORBA model include both system and user exceptions. The CORBA specification defines a set of system exceptions that can be raised when errors occur in the processing of a client request. Also, system exceptions are raised in the case of communication failures. System exceptions can be raised at any time and they do not need to be declared in the interface.
You can define user exceptions in IDL for objects you create and specify the circumstances under which those exceptions are to be raised. They are included in the method signature. If an object raises an exception while handling a client request, the VisiBroker ORB is responsible for reflecting this information back to the client.
System exceptions
System exceptions are usually raised by the VisiBroker ORB, though it is possible for object implementations to raise them through interceptors discussed in “Using VisiBroker Interceptors”. When the VisiBroker ORB raises a SystemException, one of the CORBA-defined error conditions is displayed as shown below.
For a listing of explanations and possible causes of these exceptions, see “CORBA exceptions”.
Mismatch detected between the TransactionPolicy in the IOR and the current transaction mode (used in conjunction with VisiTransact).
SystemException class
class SystemException : public CORBA::Exception {
public:
static const char *_id;
virtual ~SystemException();
CORBA::ULong minor() const;
void minor(CORBA::ULong val);
CORBA::CompletionStatus completed() const;
void completed(CORBA::CompletionStatus status);
...
static SystemException *_downcast(Exception *);
...
};
Obtaining completion status
System exceptions have a completion status that tells you whether or not the operation that raised the exception was completed. The sample below illustrates the CompletionStatus enumerated values for the CompletionStatus. COMPLETED_MAYBE is returned when the status of the operation cannot be determined.
enum CompletionStatus {
COMPLETED_YES = 0;
COMPLETED_NO = 1;
COMPLETED_MAYBE = 2;
};
You can retrieve the completion status using these SystemException methods.
CompletionStatus completed();
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);
Determining the type of a system exception
The design of the VisiBroker exception classes allows your program to catch any type of exception and then determine its type by using the _downcast() method. A static method, _downcast() accepts a pointer to any Exception object. As with the _downcast() method defined on CORBA::Object, if the pointer is of type SystemException, _downcast() will return the pointer to you. If the pointer is not of type SystemException, _downcast() will return a NULL pointer.
Catching system exceptions
Your applications should enclose the VisiBroker ORB and remote calls in a try catch block. The code samples below illustrate how the account client program, discussed in “Developing an example application with VisiBroker”, prints an exception.
#include Bank_c.hh
int main(int argc, char* const* argv) {
try {
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
PortableServer::ObjectId_var managerId =
PortableServer::string_to_ObjectId(BankManager);
Bank::AccountManager_var manager =
Bank::AccountManager::_bind(/bank_agent_poa, managerId);
const char* name = argc > 1 ? argv[1] : Jack B. Quick;
Bank::Account_var account = manager->open(name);
CORBA::Float balance = account->balance();
cout << The balance in << name << 's account is $ << balance
      << endl;
} catch(const CORBA::Exception& e) {
cerr << e << endl;
return 1;
}
return 0;
}
If you were to execute the client program with these modifications and without a server present, the following output would indicate that the operation did not complete and the reason for the exception.
prompt>Client
Exception: CORBA::OBJECT_NOT_EXIST
Minor: 0
Completion Status: NO
Downcasting exceptions to a system exception
You can modify the account client program to attempt to downcast any exception that is caught to a SystemException. The following code sample shows you how to modify the client program.
int main(int argc, char* const* argv) {
try {
// Initialize the ORB.
CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);
// Bind to an account.
Account_var account = Account::_bind();
// Get the balance of the account.
CORBA::Float acct_balance = account->balance();
// Print out the balance.
cout << The balance in the account is $
<< acct_balance << endl;
} catch(const CORBA::Exception& e) {
CORBA::SystemException* sys_excep;
sys_excep = CORBA::SystemException::_downcast((CORBA::Exception*)&e);
if(sys_excep != NULL) {
cerr << System Exception occurred: << endl;
cerr << exception name: <<
sys_excep->_name() << endl;
cerr << minor code: << sys_excep->minor() << endl;
cerr << completion code: << sys_excep->completed() << endl;
} else {
cerr << Not a system exception << endl;
cerr << e << endl;
}
}
}
The following code sample displays the resulting output if a system exception occurs.
System Exception occurred:
exception name: CORBA::NO_IMPLEMENT
minor code: 0
completion code: 1
Catching specific types of 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 sample show this technique.
...
int main(int argc, char* const* argv) {
try {
// Initialize the ORB.
CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);
// Bind to an account.
Account_var account = Account::_bind();
// Get account balance.
CORBA::Float acct_balance = account->balance();
// Print out the balance.
cout <<
      The balance in the account is $          << acct_balance << endl;
}
// Check for system errors
catch(const CORBA::SystemException& sys_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;
}
}
...
User exceptions
When you define your object's interface in IDL, you can specify the user exceptions that the object may raise. The following code sample shows the UserException code from which the idl2cpp compiler will derive the user exceptions you specify for your object.
class UserException: public Exception {
public:
...
static const char *_id;
virtual ~UserException();
static UserException *_downcast(Exception *);
};
Defining user exceptions
Suppose that you want to enhance the account application, introduced in “Developing an example application with VisiBroker”, so that the account object will raise an exception. If the account object has insufficient funds, you want a user exception named AccountFrozen to be raised. The additions required to add the user exception to the IDL specification for the Account interface are shown in bold.
// Bank.idl
module Bank {
interface Account {
exception AccountFrozen {
};
float balance() raises(AccountFrozen);
};
};
The idl2cpp compiler will generate the following code for a AccountFrozen exception class:
class Account : public virtual CORBA::Object {
...
class AccountFrozen: public CORBA_UserException {
public:
static const CORBA_Exception::Description description;
AccountFrozen() {}
static CORBA::Exception *_factory() {
return new AccountFrozen();
}
~AccountFrozen() {}
virtual const CORBA_Exception::Description& _desc() const;
static AccountFrozen *_downcast(CORBA::Exception *exc);
CORBA::Exception *_deep_copy() const {
return new AccountFrozen(*this);
}
void _raise() const {
raise *this;
}
...
}
Modifying the object to raise the exception
The AccountImpl object must be modified to use the exception by raising the exception under the appropriate error conditions.
CORBA::Float AccountImpl::balance()
{
if( _balance < 50 ) {
raise Account::AccountFrozen();
} else {
return _balance;
}
Catching user exceptions
When an object implementation raises an exception, the VisiBroker ORB is responsible for reflecting the exception to your client program. Checking for a UserException is similar to checking for a SystemException. To modify the account client program to catch the AccountFrozen exception, make modifications to the code as shown below.
...
try {
// Initialize the ORB.
CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);
// Bind to an account.
Account_var account = Account::_bind();
// Get the balance of the account.
CORBA::Float acct_balance = account->balance();
}
catch(const Account::AccountFrozen& e) {
cerr << AccountFrozen returned: << endl;
cerr << e << endl;
return(0);
}
// Check for system errors
catch(const CORBA::SystemException& sys_excep) {
}
...
Adding fields to user exceptions
You can associate values with user exceptions. The code sample below shows how to modify the IDL interface specification to add a reason code to the AccountFrozen user exception. The object implementation that raises the exception is responsible for setting the reason code. The reason code is printed automatically when the exception is put on the output stream.
// Bank.idl
module Bank {
interface Account {
exception AccountFrozen {
int reason;
};
float balance() raises(AccountFrozen);
};
};