monoceres
monoceres

Reputation: 4770

How can I clean up my exception handling?

I'm looking to clean up the exception mess that is currently the code base I'm working on.

The basic setup is this.

I have an interface that is implemented by a lot of classes that looks like this:

public interface TerminalMessage<E> {

   // Override for specific return data type.
   public E send(TerminalStream stream) throws Exception;
}

These classes throw a lot of different exceptions, like IOException, InterruptedException etc.

As it is now, all I do is call getMessage() on the catched exceptions and relay this message to the ui-code.

This is not very nice since I sometimes get bogus messages displayed to the user and I catch unwanted exceptions.

I'm thinking of creating a custom exception class (TerminalException) that would wrap all these kinds of exceptions.

However I'm not sure where to do the wrapping, should the wrapping be done where the exception is first thrown (in the output stream for example) or in every send() method. The former has the advantage of not adding much code, but it makes more sense to me that a stream throws IOException rather than a TerminalException.

The above design also doesn't really solve the sometimes bad messages displayed to the user, so some tip on how to transform the thrown exceptions into something useful to the user would be great!

Thanks!

Upvotes: 3

Views: 3388

Answers (3)

Robert
Robert

Reputation: 36

From many designs I have tried, this is the final one which I happily use on some projects.

    public enum ExceptionCodes {
        IOException("ioexception", false),
        UserNotFound("usernotfond", true);

        private static final String BUNDLE_NAME = "SOME_bundle_name";

        private final String bundleCode;
        private final String isManagable;

        ExceptionCodes(String bundleCode, String isManagable) {
            this. ...
            ...
        }

        public String message() {
            // eventually get locale from somewhere, for example some threadLocal
            return SomeBundleResolver.resolve(BUMDLE_NAME, bundleCode);
        }

        public Boolean isManagable() {
            return isManagable;
        }
    }

    public class MyGenericException extends RuntimeException {
        private final ExceptionCodes exceptionCode;
        private final Throwable throwable;

        public MyException(ExceptionCodes exceptionCode) {
           this....
           ...
        }

        public MyException(ExceptionCodes exceptionCode, Throwable throwable) {
            this. ...
            ....
        }

        public Boolean isManagable() {
            return exceptionCode.isManagable();
        }

        public String getMessage() {
            return (throwable == null) ? super.getMessage() : throwable.getMessage();
        }

        ...
    }

Point is that exception codes are managed at one place. You can add custom atributes to enum like error codes etc. One of the many problem with exceptions is that if you do not know how to handle exception at place it is unprobable you will know how to handle exception layer above. Then just two cases can occur. Either exception can be displayed in some format to user, or system has to crash in some gracefull way. Attribute isManagable is exactly about this. If exception si not managable system must go down. So the exception is handled just at top level of application in general error handler. This way you can prevent Exception explosion.

Upvotes: 0

Brady
Brady

Reputation: 10357

Another option could be to use a Template Method Design Pattern and "control" the exeptions therein as follows:

public abstract TerminalMessage<E> {
    public abstract E doSend(TerminalStream stream);
    public E send(TerminalStream stream) throws Exception {
        // do common stuff here
        try {
            doSend(stream);
        }
        // catch more specific exceptions first
        // handle all the exceptions accordingly here, thus controlling
        // in one central location what will be thrown externally
        catch(Exception) {
        }
    }
}

This way the exception handling for all of the derived classes will be the same and and localized, and the derived classes wont have to do anything special.

Upvotes: 1

Roman K
Roman K

Reputation: 3337

a custom exception is a very good idea if you have useful information in it like an error code.

just wrap everything with your TerminalException, but do not forget the cause

OR

use the first TerminalException thrown:

public class MyException extends Exception{
   String errorMessage; 

   public MyException(String message, Exception cause){
     super();

     if(cause instanceof MyException){
         // already wrapped
         errorMessage= cause.getErrorMessage();          
         initCause(cause.getCause());
     }
     else{
         // not my Exception
         errorMessage=message;
         initCause(cause); 
     } 
 ´               

   }

} 

Upvotes: 2

Related Questions