4

After reading a lot about the abusive use of exceptions in Java and how you should let an exception bubble up through the different layers of an application, I've come to a point where I don't know what I am supposed to do with the potential errors my application can have.

Basically, I have a webservice which uses the DAO pattern to access data in my database. All of the database actions can throw a SQLException. As of today, I'm using a try catch to catch the SQLException and then thow a specific defined exception called ExceptionDAO that will be handle by the webservice to return a correct message to the users (a mobile application) of my webservice.

After reading a lot about how exception should be exceptional and should not be used in control flow, I've come up with a mixed understanding of what I should do to handle any errors:

  • Use return codes for anything that is likely to happen (e.g. username already exists) and therefore, to comply with the DAO pattern, pass my business objects as parameters instead. I could also use a specific pair which would return the code + the business object instead. The webservice would then use the return code to display a specific message.
  • Use checked exceptions for anything that I can't predict will happen and let them bubble up to the webservice to handle and return a message to the users. (e.g. SQLException that I can't predict : connection aborted)
  • Let unchecked exceptions bubble up aswell and display a sort of 404 error in this case.

I also had a look at the null pattern but I don't think it suits this particular situation really well. I'm also concerned to not give too much information to the users, but rather useful and straight to the point information. Indeed, the messages returned by the webservice will be used by a mobile application to then display a message to the end-user.

I hope that I was clear enough about the problem I'm having, and I'm looking forward to your answers !

Jonathan Taws
  • 1,168
  • 11
  • 24
  • This is an important topic, but possibly too broad for a Stack Overflow question! You might also find some relevant answers on https://programmers.stackexchange.com/ – DNA Feb 21 '15 at 23:15
  • @DNA True, I read quite a few topics about my problem on programmers stackexchange ! Should I still leave it open here ? – Jonathan Taws Feb 21 '15 at 23:18
  • I suggest just using Spring Data instead of writing your own. – chrylis -cautiouslyoptimistic- Feb 21 '15 at 23:20
  • @chrylis Could you explain a bit more about Spring Data ? – Jonathan Taws Feb 21 '15 at 23:22
  • Just read the project pages. You point it at your backend of choice and it autogenerates a complete DAO layer for you. – chrylis -cautiouslyoptimistic- Feb 21 '15 at 23:24
  • 1
    @chrylis Well from what I've seen so far it all works around exceptions being thrown, that's not really what my question was about. I'd rather like to know whether I should use exceptions all the way or do a mix of exceptions and return codes. – Jonathan Taws Feb 21 '15 at 23:28
  • 2
    DO NOT use return codes. If the database code is your own, you should consider throwing more specific flavors of `SQLException` or your own custom exception. Another thing you need to determine is who needs to mitigate the exception. For example, if you are trying to write to a file and the file does not exist, you could do one of two things: 1) Create a blank file and write data to it, or 2) Use the exception as a mechanism to inform the user and let the user decide what to do. Your code wil be simpler if the exception handling is used correctly. Returning error codes isn't the way to go. – hfontanez Feb 21 '15 at 23:38
  • this is the great checked vs unchecked debate. in my personal opinion checked exceptions are tantamount to a response. I believe they're the only real mistake Sun made. unchecked for genuine exceptions. proper response for bad business rules. imo. – Richard Feb 21 '15 at 23:40
  • The name ExceptiondDAO is backwards; it sounds like a DAO for accessing exceptions. – The111 Feb 22 '15 at 01:24
  • I'll fix the naming problem. I can see why return codes aren't up to par with exceptions, but I've also read so much about you should keep exceptions for exceptional cases, and all the debate around it. Would you consider a username already existing an exceptional case ? – Jonathan Taws Feb 22 '15 at 10:50

3 Answers3

2

Return codes are suitable for C, which lacks exception handling among its features. For Java, please use exceptions, either checked or runtime, that's a matter of taste.

Personally, I hate checked exceptions, because they pollute my method signatures with information of border cases that might never occur. But maybe you want to be strict with the contract of your classes, even for such exceptional cases. If that's your case, then use checked exceptions. Otherwise, let your method signatures in peace and throw a runtime exception whenever you detect an exceptional case (such as an entity not found, entity already exists, etc).

Please note that ExceptionDAO is not a happy name. It appears to be a dao that handles exceptions. Maybe something like PersistenceException would be better.

Apart from that naming detail, I think your approach is on the right way, though not ideal. Ideally, you wouldn't need to do a try/catch for SQLExceptions inside every method that calls methods from your DAOs (or inside every method of your DAOs). Instead, a persistence exception translation mechanism would be much better. Spring, for instance, comes with one by default. Spring accomplishes this by proxying every DAO, and by letting its proxies perform a try/catch around every method invocation. Then, specific SQLExceptions with specific SQL error codes are translated to Spring's own DataAccessException hierarchy. So, in the upper layers, you would end up with a specific DataAccessException, but you wouldn't need to do a try/catch inside every method.

If you are already using Spring, then you have nothing to do, but if you aren't using it and have either many DAOs or many methods that might throw SQLExceptions, and all your DAOs implement an interface, then it might be worth the effort to implement a proxy that intercepts all the methods of your DAOs and performs a try/catch around them. Then, in this interceptor's catch block, you'd throw your ExceptionDAO (please rename it!) with a message that would depend on the original SQLException (and maybe on its SQL error code, too).

This approach has the advantage that you could handle all persistence exceptions in a single point of your program.

This very same concept could be applied to your web layer as well. Instead of letting every method of your endpoints handle your ExceptionDAO (don't forget to rename it!), you could create a proxy that intercepts every method call and perform a try/catch around it. You could then extract the message from the exception and send it in the response (or whatever you find suitable to do with it). Again, this is all based in Spring, this time, in Spring MVC Exception Handling mechanism. Here you could also handle unexpected exceptions, i.e. RuntimeExceptions and provide an appropriate message to your users.

The advantage, again, would be that you could handle all the exceptions that reached your web layer in a single point of your program.

Please see Proxy javadocs and this tutorial for further reference on Java's proxies.

fps
  • 33,623
  • 8
  • 55
  • 110
  • Thanks for such a well-explained answer. Yes, the naming isn't great, I'll change that. Even though I could create all the mechanisms you are talking about, I assume I would be better off using Spring, right ? As I am using Jersey for my webservice, I would use Spring (or SpringMVC) ? Furthermore, I was using try with resources in my DAO classes to guarantee the state of resources, I suppose Spring already performs this ? – Jonathan Taws Feb 22 '15 at 10:32
  • Yes. Much better to use Spring. It performs this with `@Repository` and `@ExceptionHandler` annotations. – fps Feb 22 '15 at 11:04
  • I suppose the "this" in your answer refers to the try with resources ? I've also see quite a few examples using only Spring, do I need SpringMVC then ? – Jonathan Taws Feb 22 '15 at 11:07
  • Sorry. I meant that you would be better off using Spring. And yes, Spring handles db connections and resources properly. You don't have to do it yourself. – fps Feb 22 '15 at 11:14
  • Alright, thanks for all the answers ! One last question if you don't mind, you said I could use Spring MVC Exception handling on the web layer. From my understanding, I would need to use Spring for the dao layer, and SpringMVC for the web layer ? – Jonathan Taws Feb 22 '15 at 11:19
  • 1
    That's correct. Spring is modular. You only add the modules you need. In your case it would be spring mvc and spring core. I believe you also need a couple of dependencies, maybe spring context. – fps Feb 22 '15 at 11:51
1

I think you are missing an important option, which is the observer pattern.

In your DAO you can have this:

public interface OnExceptionInteractionListener {
    public void onExceptionInteraction(String exceptionMessage);
}

I would have the DAO be something like:

public SomeDAO(OnExceptionInteractionListener listener, ...) {
}

and I would instantiate the DAO as

SomeDAO s = new SomeDAO(new OnExceptionInteractionListener() {
  public void onExceptionInteraction(String exceptionMessage) {
  }
}, ...);

So, if there is an exception caught then call the listener in the DAO and the next level up will handle it.

This is better performance than throwing exceptions, and better than return codes.

For more on this you can look at this answer, for other examples: https://stackoverflow.com/a/18585099/67566

I haven't tried it but I expect that lambda expressions would be useful for this also, if you are using Java8.

Community
  • 1
  • 1
James Black
  • 41,583
  • 10
  • 86
  • 166
  • I didn't think about the observer pattern, but this seems like an elegant solution to the problem, without too many boilerpate. Yes, we are using Java 8, and lambda expressions could be of some help in this ! – Jonathan Taws Feb 22 '15 at 10:37
0

Return codes are very old-school. Ideally, you should do your insert in some sort of transaction that checks:

  1. Lock username inserts

  2. Is this username available?

    3a. If so, insert and unlock

    3b. If not, unlock and inform user

If that's not possible, then a return object wrapping the result if it was created successfully and response Enum with potential outcomes.

jiggy
  • 3,828
  • 1
  • 25
  • 40
  • Are you saying that I should lock every insert on the database when I need to perform an insertion ? This is not possible in my situation, I am using connection pooling with potentially a lot of users using the webservice and therefore the database. – Jonathan Taws Feb 22 '15 at 10:38
  • This is specifically for the case you mentioned on a username already existing. This kind of insert should not even be attempted if username is meant to be unique. My solution is very coarse-grained and easy to do, but if you have a ton of throughput on that table, you can come up with something fancier. Like queuing user creation requests, have a "staging" where a single username is locked until the process is complete. Either way, I'm just saying this should be an explicit process and not an exception. – jiggy Feb 22 '15 at 15:53