elaspog
elaspog

Reputation: 1699

How to implement wrapper for multiple exceptions?

I've some modules, each containing some models what I want to parse from persisted file(s).

When I read a file I don't know which module will be able to parse it, that's why I try to parse it with my first module's parser. If that fails I try with the parser of the second module and continue that until I've tried all my parsers. The parsers can give back information in form of multiple exceptions (different subtypes of Exception class) or the parsed model object (different subtypes of a ModelBase class).

If none of the parsers succeed I want to wrap all of given exceptions into one big Exception, throw it and catch it somewhere in my application code (in form of a new big exception), where I can handle the problem (e.g. show all the parsing problems and stacktraces to the user, handle them somehow etc.).

My pseudocode:

ModelBase getModelOrBigException(File file)
    throws MyBigException {

    List<Exception> exceptions = new ArrayList<>();
    for (Module module : myModules){
        try {
            ModelBase model = module.parse(file);
            return model;
        } 
        catch (ParsingException1 p1) { exceptions.add(p1); }
        catch (ParsingException2 p2) { exceptions.add(p2); }
    }
    throw new MyBigException(exceptions);
}

I want to call the code

void openFilesHandler(){

    //... selecting file 

    try {
        process(getModelOrBigException(file));
    } catch (MyBigException e) { 
        // process the sub-exceptions or show them to user or print stacktraces of all sub-exceptions or show sub-exceptions by type etc
    }
}

Obviously if I catch MyBigException I won't be able to call methods like getStackTrace(), getMessage(), getLocalizedMessage() on them by default, only if I implement my exception class similar to this:

class MyBigException extends Exception {

    public MyBigException(Exception e1, Exception e2, ..., Exception eN){
        super(e1, e2, ..., eN); // This is not possible, only one argument is acceptable
    }
}

or to this:

class MyBigException extends Exception {

    List<Exception> exceptions = new ArrayList<>();

    public MyBigException(List<Exception> exceptions){
        super(exceptions); // This is not possible, list or array of exceptions is not allowed here
    }
}

Questions:

How should I create a new type of Exception, which can store multiple exceptions with support of the original Exception class's methods? When I do this:

myBigException.printStackTrace();

or this:

myBigException.getMessage();

I want to print/get all stacktraces of all stored exceptions. Should I pass all given exceptions to super() method?

Is there any better way to do the parsing solution better than the solution above?

Upvotes: 4

Views: 2689

Answers (3)

Vasu
Vasu

Reputation: 22422

I want to print/get all stacktraces of all stored exceptions. Should I pass all given exceptions to super() method?

If you wanted to print all stacktraces or exception messages, you are almost there and you need add few more bits as explained below:

(1) Inside MyBigException, create a constructor MyBigException(List<Exception> exceptions, String exeptionMessage) and call super(exeptionMessage);

(2) override printStackTrace() in your MyBigException and iterateover the List<Exception> and call printStackTrace() on each exception object.

You can refer the below code on the same:

MyBigException class:

public class MyBigException extends Exception {

    private List<Exception> exceptions = new ArrayList<>();

    public MyBigException(List<Exception> exceptions, String exeptionMessages){
        //call super and pass message
        super(exeptionMessages);
        this.exceptions = exceptions;
    }

    public void printStackTrace() {
        for(Exception exception : exceptions) {
            exception.printStackTrace();
        }
    }   
}

getModelOrBigException() code:

ModelBase getModelOrBigException(File file)
    throws MyBigException {

    List<Exception> exceptions = new ArrayList<>();

    //Capture exception messages as well using StringBuilder
    StringBuilder exceptioMessages = new StringBuilder();
    for (Module module : myModules){
        try {
            ModelBase model = module.parse(file);
            return model;
        } 
        catch (ParsingException1 p1) { 
            exceptions.add(p1); 
            exceptioMessages.append("YOUR MESSAGE FOR ParsingException1;");
        }
        catch (ParsingException2 p2) { 
            exceptions.add(p2); 
            exceptioMessages.append("YOUR MESSAGE FOR ParsingException2;");
        }
      }
      throw new MyBigException(exceptions, exceptioMessages.toString());
    }

Upvotes: 5

fps
fps

Reputation: 34460

You can't have many exceptions as the cause of your BigException. One exception is thrown and it goes up the stack until you handle it. You could add it to a causality relation chain, and that's why Exception's constructor accepts another exception as the cause of this exception.

But in your case, you are throwing the BigException after many parsing exceptions have been thrown and already handled (by adding them to a list).

So your first exception in the chain is actually BigException. If I were you, I would just have a getter for the list of parsing exceptions and work with that list, i.e. to inform the user, log the list etc.

Upvotes: 1

Roland
Roland

Reputation: 23252

Two options come to my mind.

  1. suppressing the given exceptions using addSuppressed. You can then retrieve it again later using getSuppressed(). This is also the mechanism which is used on try-with-resources-statements throwing exceptions. This way the stack trace of your myBigException also shows the suppressed ones automatically.

  2. add an accessor method to your exception classes internal list so you can access it from outside, e.g. getExceptions(). Here however you need to handle the stack trace of each exception yourself. You can overwrite the printStackTrace(*) methods, but that seems overhead to me.

It mainly depends on what you want to achieve (or what is more appropriate) and how you want to access the exceptions later on.

You may also want to supply your own printStackTraces() method in the second case or overwrite the getMessage()-method in both cases to supply a message that is more appropriate.

Upvotes: 1

Related Questions