IndoKnight, what a perfect wrap up you gave of how Exception
semantics within the Java EE framework.. work.
Here's the two only lines a "bean provider", that is you and me, need to know about exceptions in Java EE:
Your bean is totally free to recover from any type of exception or error as you see fit, with respect to other constraints the bean might be under. If you recovered from an exception, then congratulations - there's no problem anymore =)
A relevant constraint could for example be that "enterprise beans that use container-managed transaction demarcation must not use any transaction-management methods that interfere with the container's transaction demarcation boundaries" to quote the Java EE 7 tutorial page 48-2 (would you like to programmatically set the rollback of a container managed transaction, use EJBContext.setRollbackOnly()).
You are also discouraged, as with any type of Java application, to handle Throwable
or Error
thrown from a really low level. RuntimeException
is theoretically counted in this category, as it so famously signals a "developer error" that's like "totally totally unexpected" - yet we all know it isn't.
Basically, an unexpected exception (runtime exceptions + other shit that we assume originate from someone else) is assumed to be unhandleable by your code and should be handled by the server instead. The server is required to handle the "unhandleable" exception (see the EJB 3.2 specification, page 204) by printing something about it in the log (I'll get into the details a bit later!).
More specifically..
You asked (and here is what I believe or hold true):
Are all application exceptions described above supposed to be thrown
directly by EJB to client?
Yes. And the transaction (if one is active) shall not be rolled back unless you explicitly state that it should by using the rollback attribute of the ApplicationException
. Java code throwing exceptions is a very natural thing. Java EE has no intention of demolishing this programming model. So go ahead, throw checked exceptions however you like, forcing the clients to try-catch those or mark runtime exceptions as being application exceptions and throw those too. Happy throwing!
If System Exceptions are wrapped inside javax.ejb.EJBException before
throwing to the client, then is javax.ejb.EJBException considered as
System Exception?
Yes, but not for the reason you provide. To be clear, there is no type called SystemException. It is just fancy wording to describe those kind of exceptions that the majority of beans out there didn't figure could happen, and most likely, they originate from the EJB container. Your code should definitely not throw an EJBException. Doing so would probably only thwart the mind of the Server. Nor can you annotate the exception as being @ApplicationException since you don't own the code, it is provided by the Java EE API. You could extend EJBException, but it wouldn't make any sense to disguise your code as being part of the server's codebase. Most importantly, since EJBException extends RuntimeException, I think it is safe to categorize the EJBException as a "system-level exception".
Watch out for the Devil
Some remote clients will receive the RemoteException instead of the EJBException.
Interceptors that you might not even be aware of in a huge enterprise project, could swallow exceptions thrown from your method making an active transaction commit even though you never had plans to let him do that.
I bet you think that a suppressed exception is always retrievable using Throwable.getCause(). Do note that the EJB 3.2 specification does not say that the container has to or should spare a reference to the suppressed cause. In fact, the only thing the container has to do is log the exception. Then, if the bean is not a singleton, the bean instance must be discarded and never used again. Also, if the bean is a message-driven bean, only then is the original exception required to be "wrapped". How-to "wrap" the exception is not specified. Super portable cool code should probably have a look at both the Throwable.getCause() method and Throwable.getSuppressed() too. Don't blindly expect in your handling code to always find the original exception.
Asynchronous methods (public session bean methods annotated @Asynchronous) that return void cannot propagate an exception to his client. Thus they must not declare or throw application exceptions (see the EJB 3.2 specification page 82). Do note that when you invoke an asynchronous method, it might be the case that the server cannot provide threading resources to service your request and if so, he is required to throw an.. EJBException (page 48)!