Skip to main content

Exception handling: shouldn't client get "same" exception as server?

8 replies [Last post]
ljnelson
Offline
Joined: 2003-08-04

The spec reads thusly:

The EJB specification for exception handling is designed to meet these high-level goals:
• An application exception thrown by an enterprise bean instance should be reported to the client
precisely (i.e., the client gets the same exception)[74].

I have an *entity* class that throws what I consider to be an application exception: it is checked and declared in the throws clause in question.

When I throw this exception, the client gets nothing at all that would indicate that this is the exception being thrown. Instead, he gets a whole pile of repeated serverish exceptions--java.rmi.ServerException, EJBException, etc., all of which indicate that the transaction has rolled back (they tell me this about 23 times). Nowhere in the stack is any indication of what went wrong.

In the server log, by contrast, I see quite clearly what has happened.

Is this a problem, or is this by design? How would I go about remedying this if it *is* a problem?

Thanks,
Laird

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
stvconsultants
Offline
Joined: 2006-03-06

Thinking about this a little more...

why do you care what line in your method is failing... are you going to write some fancy code in your method that recovers from the error? It would seem to me that a better design is to just know that something went wrong when you called the method. Debugging will involve looking in the logs, and the logs list the original exception that caused the rollback (it would be nice if this was wrapped in the rollback exception though).

If you are doing proper @TransactionAttribute annotations, you'll have some methods that SUPPORT transactions, some that don't need transactions, some that must have transactions to be executed, etc.

The business logic that is calling them will be managing the transaction(s) that these calls execute within (either explicitly with a UserTransaction, or implicitly with the automatically container generated transactions). That business logic is the only logic that should know how to handle when things go wrong. I don't see that the lower level methods should be worrying about the transaction-level error trapping

mperezma
Offline
Joined: 2005-03-22

Hello,

Persistence API define a set of exceptions wich may be usefull for handling errors at runtime (EntityNotFoundException, OptimisticLockException) and give some info to the user about what went wrong..

The answer to the "batching" behaviour was so simple: just make a em.flush() after persist operation, and problem is solved 8))) Now I catch the "duplicate entry" exception at this time and handle the error, throwing a custom exception or so..

Many thanks
Mario

stvconsultants
Offline
Joined: 2006-03-06

yeah, that's a much better solution

ljnelson
Offline
Joined: 2003-08-04

Partial answer of my own question. I was wrong; the error was due to a runtime exception (specifically an IllegalArgumentException) being thrown from a class used by the entity. This is in keeping, obviously, with the specification.

Is there some way to make the IllegalArgumentException thrown on the server side "show up" on the client side?

Thanks,
Laird

mperezma
Offline
Joined: 2005-03-22

The main problem I'm facing with exceptions ocurred in persistence layer, is that toplink seems to batch all database operations, and only tries to execute them when the entry ejb method commits. So is impossible for me to catch any exception.. I will explain:

I've this method in a local stateless session bean:

public String testPersistence(Entity entity) {
try {
System.out.println("Executing em.persist(entity)");
em.persist(entity);
System.out.println("Execution ok");
} catch(Exception e) {
System.out.println("Error executing, returning exception
message...");
return e.getMessage();
}
return "ok";
}

I call this method from a remote statefull session bean:

@EJB
private testLocal testBean;

public String testExecution(Entity entity) {
try {
String result = null;
System.out.println("Calling local ejb...");
result = testBean.testPersistence(entity);
System.out.println("Ok calling local ejb...");
return result;
} catch(Exception e) {
return e.getMessage();
}
}

Then I force an exception by trying to persist an entity twice, duplicating a unique field in the database. What I see in the server log is allways:

Calling local ejb...
Executing em.persist(entity)
Execution ok
Ok calling local ejb...

and after that, it begins the SQLException in the log, "Duplicate entry 'ABC' for key 2" is thrown, but I saw "Execution ok" in the server log!!!

So no exceptions are thrown by toplink until main ejb method (testExecution) transaction is being commited.

So, how could I catch exceptions wich occurs in persistence layer? I'm using JTA transaction type with toplink and MySql as database engine.

I posted this question in nbj2ee Netbeans list, may be I will get some advice here. Thank you all.

Mario

stvconsultants
Offline
Joined: 2006-03-06

The answer is that each EJB method is within its own transaction and transactions are not nested in Java EE 5 (although the spec does allow for nested transactions to be a future enhancement)

The solution you need is to flag your methods with
@TransactionAttribute annotations correctly. (Note I'm not saying the following is the correct useage for your case, just it might illustrate how to decide on your solution)

To use your example,

change testPersistence to be
[code]
@TransactionAttribute(value=TransactionAttributeType.REQUIRES_NEW)
public String testPersistence(Entity entity) {
[/code]

change testExecution to be
[code]
@TransactionAttribute(value=TransactionAttributeType.SUPPORTS)
public String testExecution(Entity entity) {
[/code]

Thus when you initially call testExecution, it does not need a transaction, so none is started...

When testExecution calls testPersistence, it requires a new transaction, so it gets one. That transaction will be forced to _either_ commit _or_ roll-back and throw an exception once testPersistence completes.

mperezma
Offline
Joined: 2005-03-22

Thank you a lot for your comments. I've tried adding these annotations, but problem still being the same: the line em.persist(entity) DOES NOT throw any exception, the db operation isn't executed at this time.

So, as you said, the transaction is forced to commit or rollback *ONCE testPersistence COMPLETES*. It's too late for get any detail about the error, we only know that EJB method was marked for "rollbackOnly" (testExecution catches an EJBException: "Transaction aborted; nested exception is: javax.transaction.RollbackException: Transaction marked for rollback")

Any ideas? Thanks again
Mario

stvconsultants
Offline
Joined: 2006-03-06

eh, you could maybe have TransactionAttributeType.NONE and then get yourself an (I'm trying to remember) EJBContext from which you get a UserTransaction and manage the transaction yourself...

My reason for thinking you want TransactionAttributeType.NONE is to simplify your code so that you don't have to worry about handling nested-type transactions...