views:

55

answers:

1

Edit: I have looked into Spring 3's @ExceptionHandler annotation and combining this with Option 1 below looks to be a pretty clean solution.

See http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers

I also found this to be a good read: http://blog.decaresystems.ie/index.php/2006/04/07/difficult-choices-in-handling-exceptions-in-enterprise-java-applications/


I have been developing using the Spring MVC framework for some time now however I am struggling to come up with a 'nice' way to pass errors that are raised in the service layer back to the JSP.

Basically, I don't believe that business logic (beyond "this field is mandatory") should be in the Validators, especially any logic that requires access to the DB. So, what I have been doing is placing further, more complicated validation and business logic in the service layer.

For example, lets say I have a page that allows a user to buy a Book. They click "Purchase" on the JSP and the controller calls the service to make it all happen... Now, what happens if the service sees that they have insufficient funds - how do I get this message back to the JSP so a nice little "Insufficient funds" message can be displayed to the user? I have considered two ways and I'm not sure which is correct...

Option 1: Exceptions

The first way I thought was to raise an exception in the service layer, trap it in the controller and add a message to the BindingResult.

Service:

public void pay(Book book) throws InsufficientFundsException {
    // Some logic goes here, which ends up throwing the above exception
}

Controller:

public ModelAndView(@ModelAttribute("book") Book book, BindingResult errors) {
    try {
        pay(book);
    } catch (InsufficientFundsException ex) {
        errors.reject("insufficient.funds");
    }
    return new ModelAndView(blahblahblah);
}

Option 2: Pass BindingResult to Service layer

The second way was to pass the BindingResult object to the service layer and raise further errors against it.

Service:

public void pay(Book book, BindingResult errors) {
    // User has insufficient funds, so...
    errors.reject("insufficient.funds);
}

I can see problems with both of these ways. Option 1 feels awkward because not only do I have to catch the exception, I then have to add the error to the binding result so it feels like I'm doing the same thing twice. And Option 2 seems to bind the service layer too tightly to the controller.

Finally, I realise there is the SimpleMappingExceptionResolver that could be used in conjunction with Option 1, but I'm not sure how appropriate it is (perhaps I have not seen a proper example?). In the above example, lets just say for argument's sake that I'd like the user returned to the original form with a red error above the form, not redirected to an entirely different page. The SimpleMappingExceptionResolver seems to me to be useful when you want to redirect a user to a standard error page when a certain exception is raised (which is not quite what I want to know how to do).

+2  A: 

Java uses exceptions to naturally handle this kind of thing. In the end it generally simplifies your logic and reduces the chance of making a mistake by forgetting to check that something had an error. You are also able to move error logic out of the main flow of the code.

I don't see why the case you present is different from any other case where I would use exception handling to deal with errors.

TofuBeer
Thanks. Further investigation of Spring 3's exception handler reveals that I can deal with raised exceptions in the controller via the @ExceptionHandler annotation, in that I can treat it the same way as I would a request.This to me seems to be a bit nicer than try/catching in the normal rqeuest method.
Ben J
@Ben J: I'm not sure if `@ExceptionHandler` methods have access to the `BindingResult`, though.
skaffman