The (mostly) unknown story behind javax.ejb.EJBException
2015/04/22 3 Comments
Yesterday I blogged about what impact Exceptions do have on JavaEE transactions in EJB.
But there is another funny EJB Exception mechanism waiting for you to get discovered – the javax.ejb.EJBException
.
This handling dates back to the times when EJB was mainly intended to be a competition to NeXT Distributed Objects and Microsoft DCOM. Back in those days it was all about ‘Client-Server Architecture’ and people tried to spread the load to different servers on the network. A single EJB back then needed 4 classes and was inherently remote by default.
Only much later EJBs got a ‘local’ behaviour as well. And only in EJB-3.1 the No-Interface View (NIV) got introduced which made interfaces obsolete and are local-only.
But for a very long time remoting was THE default operation mode of EJBs. So all the behaviour was tailored around this – regardless whether you are really using remoting or are running in the very same JVM.
The impact of remoting
The tricky situation with remote calls is that you cannot be sure that every class is available on the client.
Imagine a server which uses JPA. This might throw a javax.persistence.EntityNotFoundException
. But what if the caller – a Swing EJB client app – doesn’t have any JPA classes on it’s classpath?
This will end up in a ClassNotFoundException or NoClassDefFoundException because de-serialisation of the EntityNotFoundException will blow up on the client side.
To avoid this from happening the server will serialize a javax.ejb.EJBException
instead of the originally thrown Exception in certain cases. The EJBException will contain the original Exceptions stack trace as pure Strings. So you at least have the information about what was going wrong in a human readable format.
If you like to read up the very details then check out 9.4 Client’s View of Exceptions in the EJB specification.
Switching on the ‘Auto Pilot”
Some containers like e.g. OpenEJB/TomEE contain a dual-strategy. We have a ‘container’ (ThrowableArtifact) which wraps the orignal Throwable plus the String interpretation and sends both informations as fallback over the line.
On the client side the de-serialization logic of ThrowableArtifact
first tries to de-serialize the original Exception. Whenever this is possible you will get the originally thrown Exception on the client side. If this didn’t work then we will use the passed information and instead of the original Exception we throw an EJBException
with the meta information as Strings.
The impact on your program?
The main impact for you as programmer is that you need to know that you probably not only need to catch the original Exception but also an EJBException
. So this really changes the way your code needs to be written.
And of course if you only got the EJBException
then you do not exactly know what was really going on. If you need to react on different Exceptions in different ways then you might try to look it up in the exception message but you have no type-safe way anymore. In that case it might be better to catch it on the server side and send an explicit @ApplicationException
over the line.
When do I get the EJBException and when do I get the original one?
I’d be happy to have a good answer myself 😉
My experience so far is that it is not well enough specified when each of them gets thrown. But there are some certain course grained groups of container behaviour:
- Container with Auto-Pilot mode; Those containers will always try to give you the original Exception. And only if it is really not technically possible will give you an EJBException. E.g. TomEE works that way.
- Container who use the original Exception for ‘local’ calls and EJBException for remote calls.
- Container who will always give you an EJBException – even for local invocations. I have not seen those for quite some time though. Not sure if this is still state of the art?
Any feedback about which container behaves what way is welcome. And obviously also if you think there is another category!