Get J2EE architecture and design answers

Discussions

News: Get J2EE architecture and design answers

  1. Get J2EE architecture and design answers (16 messages)

    Find answers to your questions about J2EE application architecture and design from IBM's J2EE consultant Bobby Woolf. In this article he answers a range of questions from various realms of J2EE. He discusses tools, SOA/ESB, sync/async, and much more.

    Sample
    Question:

    Here is a design issue about transaction management and rollback writing on a single DB2® instance (non-distributed transaction). An application is using the DAO pattern to save data into multiple tables, each DAO used one save on one table. A session facade EJB business methods call more than one DAO to save on more than one table. Container managed transactions is enabled. I read some articles on declarative transaction management and you need to do one of two things:

    • Use setRollBackOnly() in the business method
    • Make the business method to throw a runtime exception

    For now, the second choice has been selected; a DAOException is made to extend RuntimeException and all DAO classes throw a DAOException instance in case an error occurred. Could you please advise on what is the best choice, what makes our code more reusable? For the first choice, I see that you need to add extra code lines setRollBackOnly() to each business method, which I see as inconvenient.

    Using the WebSphere Studio V4 IDE test environment, transaction management and roll back go fine, but whenever the application is deployed on WebSphere Application Server V4, no rollback occurs at all. Is there any specific configuration or setting to enable declarative transaction management on WebSphere Application Server V4?

    Answer:

    Good use of patterns. Data Access Object (DAO) and Session Facade are both good ones to use. Because you are using EJBs, you have run into a common dilemma: How should your EJB code implement and use application exceptions, when should it use setRollbackOnly(), and how does this all go together?

    An application exception represents the occurrence of a business logic error: withdrawing more than an account balance, reserving a seat which is already reserved, getting a credit card charge denied, etc. This is different from a system exception, which represents a system level error like running out of memory or running past the end of an array. According to the EJB specification (Chapter 18), an application exception must be a subclass of Exception and must not be a subclass of RuntimeException nor RemoteException, which is very good advice. Meanwhile, according to the spec, the container must catch system exceptions, mark the transaction for rollback, and throw RemoteException or EJBException to the EJB client. The container must pass application exceptions through to the EJB client without changing
    the exception nor changing the transaction.

    I think this is rather inconsistent, that system exceptions cause automatic rollback while application exceptions specifically do not. An exception has been thrown so should you really commit the transaction (which is the automatic behavior)? I don't think so. Nevertheless, this is the way EJB works.

    So, in your situation: Your application exception classes are subclasses of RuntimeException. This is bad. While it performs the rollback automatically, it also converts your hopefully meaningful and descriptive application exception to a generic one. The spec says not to do this.

    You are going to have to handle rollbacks yourself. This means you need to get in the habit of writing code in your session facades (your topmost layer of code in your EJB container) that catches application exceptions, calls setRollbackOnly(), and rethrows the same exception so that the EJB client knows what happened. This is the way to write good EJB code: Don't subclass RuntimeException, and use setRollbackOnly() before throwing an exception out of the EJB container.

    Find answers to your questions about J2EE application architecture and design from IBM's J2EE master and consultant Bobby Woolf.

    Threaded Messages (16)

  2. "This is the way to write good EJB code: Don't subclass RuntimeException, and use setRollbackOnly() before throwing an exception out of the EJB container."

    I'd argue against throwing, as an example, DAOExceptions from the EJB container. What is your client going to do with a DAOException? Must the client catch all types of Exceptions possibly thrown from the EJB call? I say the EJB catches exceptions (like DAOException, even perhaps runtime exceptions), maybe does some logging, creates a BusinessException (nest the DAOException in there if you must) then throw that sucker. Your BusinessException can be an EJBException (which is a RuntimeException) so the transaction may get rolled-back if the method descriptor so says.
    The BusinessException can have many flavors...one flavor is for exceptions that are unrecoverable, another for recoverable errors - like a server side validation of user input that can be resubmitted.
  3. WAS advertisment/howto cocktail[ Go to top ]

    Bobby Woolf: I think this is rather inconsistent, that system exceptions cause automatic rollback while application exceptions specifically do not. An exception has been thrown so should you really commit the transaction (which is the automatic behavior)? I don't think so. Nevertheless, this is the way EJB works.
    Try to withdraw $200000 from bank account, get NSF exception, then application logic may decide to withdraw as much as account has. So, there is no reason here for automatic transaction rollback.
    Bobby Woolf: Currently, Web services (WS-I Basic Profile 1.1) are synchronous. The caller sends an HTTP request and blocks waiting for the HTTP response. Because Web services are for application-to-application communication, they are more useful when they can support asynchronous communication.
    One can simulate asynchronous call using two or more calls instead of one. One to send a request, another to read a result. It can be done either polling the server from client, or by calling back client from server, if both ends of this connection are available for modification. Anyway, webservices is just an API for client and server, for async calls there should be a transaction/queueing middleware.
  4. "Try to withdraw $200000 from bank account, get NSF exception, then application logic may decide to withdraw as much as account has. So, there is no reason here for automatic transaction"

    Will the second call to withdraw amount less than $200000 not be part of a new transaction?
  5. Client should catch any exceptions[ Go to top ]

    I think allowing DAOExceptions to propagate up to the client is preferable. Simply wrap any service calls with try/catch(Exception), then use the catch block to expose any generic failure message you wish to the client. This allows clients to gracefully deal with all exception types regardless of service implementation (imagine the case where you change service impls and it throws different subclasses of RuntimeExceptions). This approach also avoids the code overhead of catching, interpreting, handling and rethrowing service exceptions unless it is necessary.
    If you need more specific (i.e., Application) Exception handling in the client, you simply insert the specific try/catch(ApplicationException) blocks in which the client is interested before the generic block.
  6. Client should catch any exceptions[ Go to top ]

    Why should the client even know that DAOExceptions exist? What if the backend business throws IOExceptions, SAXExceptions, YouNameItExceptions. Should the client know and deal with each of these? Every time the back end business changes the client has to change too?

    "imagine the case where you change service impls and it throws different subclasses of RuntimeExceptions"

    Your interface should be fixed regardless of the impl.

    The client makes a call like buyTicket(..). buyTicket() may or may not call a DAO, open a file, parse XML, etc. And it may change over time. The client should only care about TicketPurchaseException. buyTicket() catches all types of exceptions and rethrows, if needed, a TicketPurchaseException.
    TicketPurchaseException could be an EJBException which is a RuntimeException which may cause a transaction to rollback.
  7. Client should catch any exceptions[ Go to top ]

    Why should the client even know that DAOExceptions exist? What if the backend business throws IOExceptions, SAXExceptions, YouNameItExceptions.

    Correct. The client may not even have access to the class file for the exception being thrown. Using DB2 7.2 if you let SQLException be thrown up to a client, you get a marshalling error. DB2 doesn't throw SQLException itself, it throws a subclass (which the client usually doesn't have access to).

    If an exception is unexpected during the normal business flow: log it, transfer any message to an "InternalError" type "BusinessException", and code a roll back. Only let the RuntimeException rollback work as a backstop to catch those things you've missed. After all, if there is a marshalling exception, the client will not be able to tell the user what the real error was anyway.
  8. Client should catch any exceptions[ Go to top ]

    After all, if there is a marshalling exception, the client will not be able to tell the user what the real error was anyway.

    This is a very good technical reason for catching any and all exceptions on the service side that I had not thought of. This would definitely go along the lines of interface consistency with regards to RuntimeException types.

    However, this would still be gracefully handled by clients using try/catch(Exception).

    Thanks Sartoris and Reg for your input. I'm going to refactor some EJB methods using your suggested pattern.
  9. Client should catch any exceptions[ Go to top ]

    "try/catch(TicketPurchaseException)

    anyway, why not just use:

    try/catch(Exception)
    "

    I would vote for something like this on the client side.

    ...
    try {
     businessTier.purchaseTicket(...);
    } catch (TicketPurchaseException tpe) {
     // these can be errors like input errors. errors that are user fixable
    } catch (BusinessException be) {
     // business errors not fixable by user, may be fixable by the application, like DB connection errors-->
    } catch (Throwable t) {
     // handle anything else
    }

    BusinessTier.purchaseTicket() should endeavor to not throw any exceptions other than TicketPurchaseException and BusinessException but just in case catch Throwable.
  10. Keeping the client simple[ Go to top ]

    I agree that the client shouldn't have knowledge of DAOExceptions; I'm not suggesting that a client should be expected to catch these.

    What we've done is have EJB clients at a minimum use try/catch(Exception) to catch any possible RuntimeExceptions, and return a generic error message to the user rather than the typical server 500 page. The nice thing about presenting the same page with a generic error message is that the user can try the operation again without clicking the back button.

    I agree the interface should be fixed. To me, this means "the method signature does not change," while you are thinking more along the lines of "the method signature does not change, and the types of RuntimeExceptions thrown also do not change." While this is a more strict interpretation, I am not convinced that it is necessarily preferable.

    My concern is in masking any and all exceptions from the client; it removes from the client any chance to act on any system-level exception messages. In most cases this is preferable. But what if your client is another system-level component? You may in the future decide you actually need certain RuntimeExceptions exposed.

    Another consideration is this: you are suggesting that a particular exception (e.g. TicketPurchaseException) be used as a wrapper for arbitrary exceptions of which the client should not be concerned. I suggest that since it is really just a wrapper exception, and that the client will have to have:

    try/catch(TicketPurchaseException)

    anyway, why not just use:

    try/catch(Exception)

    which is shorter to type, one less class to code and conveys an equal amount of information about the actual exception (except it is not tied to a particular interface, so the client developer needn't spend time looking up the appropriate catch class)? You also then are saved from having to type up the framework for wrapping your exceptions into each EJB method.

    What are your thoughts?
  11. The worse of two evils?[ Go to top ]

    You are going to have to handle rollbacks yourself. This means you need to get in the habit of writing code in your session facades (your topmost layer of code in your EJB container) that catches application exceptions, calls setRollbackOnly(), and rethrows the same exception so that the EJB client knows what happened.

    While setting the rollback at the topmost level (at the session facade) might give you the flexibility of deciding on each facade method whether to rollback or not if a method, say f(), fails. It suffers though from that you have to put in the code to catch exception, setRollback, and rethrow the exception on each and every facade method,which might be messy if you have a fairly complicated architecture and f() is a fairly popular method and there are a couple of exception that you wanna make this decision on.

    While setting rollback as early as possible (ie before throwing the application exception) will solve that problem. If there is a need, to decide on each facade method whether to rollback or not, then you would call f() in a new transaction context from the facade.

    Which one is better? I do not know.
  12. Exception Handling[ Go to top ]

    Exception handling could be a lot simpler with a few rules like the following

    1. If the exceptions are recoverable use checked exceptions
    2. If they are not recoverable use exception classes that extend from RuntimeException

    This fits perfectly for transactions. Ideally for unrecoverable exceptions (remember we are using unchecked exceptions) the transactions would roll-back automatically. But if they are supposed to be recoverable something needs to be done anyway without rolling back the transaction.

    Also ensure to maintain proper error messages at a meta-level so the unchecked exceptions do not look generic to the client.
  13. Exception Handling[ Go to top ]

    "Also ensure to maintain proper error messages at a meta-level so the unchecked exceptions do not look generic to the client. "

    In an good exception handling design what the client gets will always be generic exception and not the specific runtime exception that has occured.
  14. Exception Handling[ Go to top ]

    In an good exception handling design what the client gets will always be generic exception and not the specific runtime exception that has occured.
    Yes you are right. But I was referring to messages displayed to the user and not classes. Classes needs to be generic, that's alright.
  15. Exception Handling[ Go to top ]

    Exception handling could be a lot simpler with a few rules like the following

    • 1. If the exceptions are recoverable use checked exceptions

    • 2. If they are not recoverable use exception classes that extend from RuntimeExceptionThis fits perfectly for transactions.
    <p/>
    There is an in-between: non-recoverable but further action needs to be taken (eg, clearing a cache which a failure renders stale).
    <p/>
    Bear in mind that not all DB exceptions are fatal. DB collisions are a prime example.
    <p/>
    (Personally, I like checked exceptions. They help make the code self-documenting).
  16. cmt and rollback[ Go to top ]

    I like this approach (method rolls back on application error) but I wish I could have the transaction set as Required (for all methods) and optionally on the fly execute the desired method with RequiresNew semantics.

    (ie tell the container on the fly, just before the method is called)

    Currently, defining it as RequiresNew adds a dependancy that the calling method catches the exception and does a setrollbackonly (as the method did not create the error it doesnt quite fit the suggested pattern), also there is additional overhead of starting a new transaction (every time) just to cope with what is possibly a one-off requirement.
  17. cnt - rollback[ Go to top ]

    actually maybe the solution is obvious...

    1. following the ejb spec and suggested pattern at the top of this thread so your method rolls back and throws an application exception.

    2. when you want the method to NOT abort the enclosing transaction, create another method in your bean that simply calls the existing method and define it as RequiresNew. (while by default all methods are Required)

    3. for the RequiresNew variant you would also need to consider if the ejb is remote or local - if local handle ejbexception (or rolled back subclass)

    after handling the Remote or EJBException, I dont see why the process couldn't continue without breaking any rules in the ejb spec.

    comments?