Exception Handling

From Apache OpenOffice Wiki
Jump to: navigation, search



UNO uses exceptions as a mechanism to propagate errors from the called method to the caller. This error mechanism is preferred instead of error codes (as in MS COM) to allow a better separation of the error handling code from the code logic. Furthermore, Java, C++ and other high-level programming languages provide an exception handling mechanism, so that this can be mapped easily into these languages.

In IDL, an exception is a structured container for data, comparable to IDL structs. Exceptions cannot be passed as a return value or method argument, because the IDL compiler does not allow this. They can be specified in raise clauses and transported in an any. There are two kinds of exceptions, user-defined exceptions and runtime exceptions.

User-Defined Exceptions

The designer of an interface should declare exceptions for every possible error condition that might occur. Different exceptions can be declared for different conditions to distinguish between different error conditions.

The implementation may throw the specified exceptions and exceptions derived from the specified exceptions. The implementation must not throw unspecified exceptions, that is, the implementation must not throw an exception if no exception is specified. This applies to all exceptions except for RuntimeExceptions, described later.

When a user-defined exception is thrown, the object should be left in the state it was in before the call. If this cannot be guaranteed, then the exception specification must describe the state of the object. Note that this is not recommended.

Every UNO IDL exception must be derived from com.sun.star.uno.Exception, whether directly or indirectly. Its UNO IDL specification looks like this:

  module com { module sun { module star { module uno { 
  exception Exception
  {
      string Message; 
      com::sun::star::uno::XInterface Context; 
  }; 
  }; }; }; };

The exception has two members:

  • The message should contain a detailed readable description of the error (in English), which is useful for debugging purposes, though it cannot be evaluated at runtime. There is currently no concept of having localized error messages.
  • The Context member should contain the object that initially threw the exception.

The following .IDL file snippet shows a method with a proper exception specification and proper documentation.

  module com { module sun { module star { module beans { 
 
  interface XPropertySet: com::sun::star::uno::XInterface
  {
      ...
      /** @returns 
          the value of the property with the specified name. 
 
          @param PropertyName 
              This parameter specifies the name of the property. 
 
          @throws UnknownPropertyException 
              if the property does not exist. 
 
          @throws com::sun::star::uno::lang::WrappedTargetException 
              if the implementation has an internal reason for the
              exception. In this case the original exception
              is wrapped into that WrappedTargetException.
      */
      any getPropertyValue( [in] string PropertyName ) 
          raises( com::sun::star::beans::UnknownPropertyException, 
                  com::sun::star::lang::WrappedTargetException );
      ...
  }; 
 
  }; }; }; };

Runtime Exceptions

Throwing a runtime exception signals an exceptional state. Runtime exceptions and exceptions derived from runtime exceptions cannot be specified in the raise clause of interface methods in IDL.

These are a few reasons for throwing a runtime exception are:

  • The connection of an underlying interprocess bridge has broken down during the call.
  • An already disposed object is called (see com.sun.star.lang.XComponent and the called object cannot fulfill its specification because of its disposed state.
  • A method parameter was passed in an explicitly forbidden manner. For instance, a null interface reference was passed as a method argument where the specification of the interface explicitly forbids this.

Every UNO call may throw a com.sun.star.uno.RuntimeException, except acquire and release. This is independent of how many calls have been completed successfully. Every caller should ensure that its own object is kept in a consistent state even if a call to another object replied with a runtime exception. The caller should also ensure that no resource leaks occur in these cases. For example, allocated memory, file descriptors, etc.

If a runtime exception occurs, the caller does not know if the call has been completed successfully or not. The com.sun.star.uno.RuntimeException is derived from com.sun.star.uno.Exception. Note, that in the Java UNO binding, the com.sun.star.uno.Exception is derived from java.lang.Exception, while the com.sun.star.uno.RuntimeException is directly derived from java.lang.RuntimeException.

A common misuse of the runtime exception is to reuse it for an exception that was forgotten during interface specification. This should be avoided under all circumstances. Consider, defining a new interface.

An exception should not be misused as a new kind of programming flow mechanism. It should always be possible that during a session of a program, no exception is thrown. If this is not the case, the interface design should be reviewed.

Good Exception Handling

This section provides tips on exception handling strategies. Under certain circumstances, the code snippets we call bad below might make sense, but often they do not.

  • Do not throw exceptions with empty messages

Often, especially in C++ code where you generally do not have a stack trace, the message within the exception is the only method that informs the caller about the reason and origin of the exception. The message is important, especially when the exception comes from a generic interface where all kinds of UNO exceptions can be thrown.

When writing exceptions, put descriptive text into them. To transfer the text to another exception, make sure to copy the text.

  • Do not catch exceptions without handling them

Many people write helper functions to simplify recurring coding tasks. However, often code will be written like the following:

  // Bad example for exception handling 
  public static void insertIntoCell( XPropertySet xPropertySet ) {
      [...]
      try {
          xPropertySet.setPropertyValue("CharColor",new Integer(0));
      } catch (Exception e) {
      }
  }

This code is ineffective, because the error is hidden. The caller will never know that an error has occurred. This is fine as long as test programs are written or to try out certain aspects of the API (although even test programs should be written correctly). Exceptions must be addressed because the compiler can not perform correctly. In real applications, handle the exception.

The appropriate solution depends on the appropriate handling of exceptions. The following is the minimum each programmer should do:

  // During early development phase, this should be at least used instead
  public static void insertIntoCell(XPropertySet xPropertySet) {
      [...]
      try {
          xPropertySet.setPropertyValue("CharColor",new Integer(0));
      } catch (Exception e) {
          e.dumpStackTrace();
      }
  }

The code above dumps the exception and its stack trace, so that a message about the occurrence of the exception is received on stderr. This is acceptable during development phase, but it is insufficient for deployed code. Your customer does not watch the stderr window.

The level where the error can be handled must be determined. Sometimes, it would be better not to catch the exception locally, but further up the exception chain. The user can then be informed of the error through dialog boxes. Note that you can even specify exceptions on the main() function:

  // this is how the final solution could look like
  public static void insertIntoCell(XPropertySet xPropertySet) throws UnknownPropertyException,
          PropertyVetoException, IllegalArgumentException, WrappedTargetException {
      [...]
      xPropertySet.setPropertyValue("CharColor",new Integer(0));
  }

As a general rule, if you cannot recover from an exception in a helper function, let the caller determine the outcome. Note that you can even throw exceptions at the main() method.

Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages