Raising Exceptions in Java

Servant Exceptions in Java

To throw an exception from an operation implementation, you simply instantiate the exception, initialize it, and throw it. For example:

public void write(String[] text, Current current)
    throws GenericError
        // Try to write file contents here...
    catch(Exception ex)
        throw new GenericError("Exception during write operation", ex);

Note that, for this example, we have supplied the optional second parameter to the GenericError constructor. This parameter sets the inner exception and preserves the original cause of the error for later diagnosis.

If you throw an arbitrary Java run-time exception (such as a ClassCastException), the Ice run time catches the exception and then returns an UnknownException to the client.

The server-side Ice run time does not validate user exceptions thrown by an operation implementation to ensure they are compatible with the operation's Slice definition. Rather, Ice returns the user exception to the client, where the client-side run time will validate the exception as usual and raise UnknownUserException for an unexpected exception type.

If you throw an Ice run-time exception, such as MemoryLimitException, the client receives an UnknownLocalException. For that reason, you should never throw Ice run-time exceptions from operation implementations. If you do, all the client will see is an UnknownLocalException, which does not tell the client anything useful.

Three run-time exceptions are treated specially and not changed to UnknownLocalException when returned to the client: ObjectNotExistException, OperationNotExistException, and FacetNotExistException.

JVM Error Semantics

Servant implementations might inadvertently raise low-level errors during the course of their operation. These exceptions, which are subclasses of java.lang.Error, should not normally be trapped by the servant because they often indicate the occurrence of a serious, unrecoverable situation. For example, the JVM throws StackOverflowError when a recursive method has used all available stack space.

The Ice run time traps instances of java.lang.Error thrown by servants and then attempts to log the exception and send an UnknownException to the client. This attempt may or may not succeed, depending on the nature of the error and the condition of the JVM. As an example, the servant implementation might raise OutOfMemoryError and Ice's attempt to log the error and send a response could also fail due to lack of memory.

For an occurrence of OutOfMemoryError or AssertionError, Ice does not re-throw the error after logging a message and sending a response. For all other subclasses of Error, Ice re-throws the error so that the JVM's normal error-handling strategy will execute.

When the JVM raises a subclass of Error, it usually means that a significant problem has occurred. Ice tries to send an UnknownException to the client (for twoway requests) in order to prevent the client from waiting indefinitely for a response. However, the JVM may prevent Ice from successfully sending this response, which is another reason your clients should use invocation timeouts as a defensive strategy. Finally, in nearly all occurrences of an error, the server is unlikely to continue operating correctly even if Ice is able to complete the client's request. For example, the JVM terminates the thread that raised an uncaught error. In the case of an uncaught error raised by a servant, the thread being terminated is normally from the Ice server-side thread pool. If all of the threads in this pool eventually terminate due to uncaught errors, the server can no longer respond to new client requests.

See Also