开发者

What is the advantage of chained exceptions

开发者 https://www.devze.com 2023-02-10 17:51 出处:网络
I do not und开发者_运维知识库erstand the advantages of having a chained exception in the code.

I do not und开发者_运维知识库erstand the advantages of having a chained exception in the code.

Considering the ResourceLoader example from java world, if the programmer knows of a possibility of encountering ResourceLoadException , why not catch the same exception instead of SQLException? Else, the programmer can catch both the exceptions in the same code than having to throw a new Throwable instance?


Can anyone please provide information on the need for chained exceptions?

The article says it quite well:

Exception chaining allows you to map one exception type to another, so that a method can throw exceptions defined at the same abstraction level as the method itself, without discarding important debugging information.

That is, if you have a method that loads some object from a database, you may rather want some ResourceLoadException (closer related to the methods abstraction level) instead of a low-level SQLException even if that was the original source of the problem. However, if you simply catch the SQLException and throw a ResourceLoadException instead, you may loose important debugging information.

Thus, chaining the exceptions is a good alternative. You throw a "high-level" exception, well suited for the particular method, but chain it with the exception that caused it.

Else, the programmer can catch both the exceptions in the same code than having to throw a new Throwable instance?

I don't quite follow your reasoning here. The point is that he should not need to worry about the SQLException at this level of abstraction.


Why Chain exception?

We need to chain the exceptions to make logs readable.

Take following examples of 1. without chaining and 2. chaining, exceptions to feel the difference

Create following Exceptions

    class NoLeaveGrantedException extends Exception {

        public NoLeaveGrantedException(String message, Throwable cause) {
            super(message, cause);
        }

        public NoLeaveGrantedException(String message) {
            super(message);
        }
    }

    class TeamLeadUpsetException extends Exception {

        public TeamLeadUpsetException(String message, Throwable cause) {
            super(message, cause);
        }

        public TeamLeadUpsetException(String message) {
            super(message);
        }
    }

    class ManagerUpsetException extends Exception {

        public ManagerUpsetException(String message, Throwable cause) {
            super(message, cause);
        }

        public ManagerUpsetException(String message) {
            super(message);
        }
    }

    class GirlFriendOfManagerUpsetException extends Exception {

        public GirlFriendOfManagerUpsetException(String message, Throwable cause) {
            super(message, cause);
        }

        public GirlFriendOfManagerUpsetException(String message) {
            super(message);
        }
    }

Now use them

1. Without chaining

    public class MainClass {

        public static void main(String[] args) throws Exception {
            getLeave();
        }

        static void getLeave() throws NoLeaveGrantedException {
            try {
                howIsTeamLead();
            } catch (TeamLeadUpsetException e) {
                e.printStackTrace();
                throw new NoLeaveGrantedException("Leave not sanctioned.");
            }
        }

        static void howIsTeamLead() throws TeamLeadUpsetException {
            try {
                howIsManager();
            } catch (ManagerUpsetException e) {
                e.printStackTrace();
                throw new TeamLeadUpsetException(
                            "Team lead is not in good mood");
            }
        }

        static void howIsManager() throws ManagerUpsetException {
            try {
                howIsGirlFriendOfManager();
            } catch (GirlFriendOfManagerUpsetException e) {
                e.printStackTrace();
                throw new ManagerUpsetException("Manager is in bad mood");
            }

        }

        static void howIsGirlFriendOfManager() 
             throws GirlFriendOfManagerUpsetException {
            throw new GirlFriendOfManagerUpsetException(
             "Girl friend of manager is in bad mood");
        }
    }

2. Chaining

    public class MainClass {

        public static void main(String[] args) throws Exception {
            getLeave();
        }

        static void getLeave() throws NoLeaveGrantedException {
            try {
                howIsTeamLead();
            } catch (TeamLeadUpsetException e) {
                throw new NoLeaveGrantedException("Leave not sanctioned.", e);
            }
        }

        static void howIsTeamLead() throws TeamLeadUpsetException {
            try {
                howIsManager();
            } catch (ManagerUpsetException e) {
                throw new TeamLeadUpsetException(
                           "Team lead is not in good mood", e);
            }
        }

        static void howIsManager() throws ManagerUpsetException {
            try {
                howIsGirlFriendOfManager();
            } catch (GirlFriendOfManagerUpsetException e) {
                throw new ManagerUpsetException("Manager is in bad mood", e);
            }

        }

        static void howIsGirlFriendOfManager() 
           throws GirlFriendOfManagerUpsetException {
            throw new GirlFriendOfManagerUpsetException(
              "Girl friend of manager is in bad mood");
        }
    }

Now compare logs

1. Without chaining

    com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:61)
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:55)
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:46)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:37)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)

2. Chaining

    Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:36)
        at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
    Caused by: com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:44)
        at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
        ... 1 more
    Caused by: com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
        at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:42)
        ... 2 more
    Caused by: com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
        at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:58)
        at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:50)
        ... 3 more


A caller of the loadResource should not need to know the exact details of how those resources are loaded, or at the very least not care about the details of why it failed. (keep in mind it might not be you that wrote loadResources, or it might be someone else that needs to use the loadResources method).

All you should care about when calling loadResource is it might throw a ResourceLoadException. Not that implementation details fails because of a SQLException - this might change with time too, later on someone might decide to load resources from somewhere else that could fail too.

You just need to load some resources, you need to handle if it fails, and not handle potential MainframeHasCrashedException, FileNotFoundException and the dozen other reasons loading those resources might fail.

Now, when something does fail, it's handy to have the original exception that caused the failure, such as an SQLException - so someone scouring through log files or similar can figure out the cause of the error by inspecting the stacktrace

You shouldn't be tempted to just catch Exception here either, if loadResources could also throw other exceptions , e.g. an UnautorizedException, you might not want to deal with that when you're calling loadResources - you might want to propagate that exception up to other callers that can deal with an UnautorizedException(and perhaps prompt the user for some credentials). a catch(Exception e) would swallow that UnautorizedException that you really can't deal with there.


The advantage is that the caller only has to handle a ResourceLoadException instead of SQLException. That way if you later change your data storage to be in a file that accessing it might cause an IOException. You don't have to go back and change the type of Exception that your caller handles. This is useful to your caller because the caller is going to handle either exception the same way.


I think the reason from that sample is that the author wishes to create a uniform interface which isn't dependent on some specific underlying system, like SQL. Hence, he converts the exception to the more general form, leaving the actual implementation transparent to the business logic.

However, in some cases it might be desirable to propagate an error like SQLException. This will be useful in places where one needs to take different actions based on the thrown Exception.


The first benefit is encapsulation. ResourceLoader might be an interface with several implementations (for instance one loading the resources from the database, while another loads them from the file system), where the implementation to use is selected at runtime. Then the caller should be agnostic to the root cause of why loading the resource failed, but might still wish to react to resource load failures. This is particularly useful if the interface declares two different exceptions to be thrown to which the caller might wish to respond differently (for instance, a TransientResourceLoadFailureException, where a retry might suceed, and a PermanentResourceLoadFailureException, where a retry is known not to succeed).

The second benefit of exception chaining is that each exception in the chain may have a different message, which allows to include additional information for debugging. In your case, note that the ResourceLoadException's message contains the name of the resource that could not be loaded, which is not guaranteed to be included in the SQLException, but might be necessary to reproduce the problem (certain databases are not exactly known for specific error messages).

These benefits come at the cost of having to write and maintain the catch blocks. You should decide on a case by case basis whether the benefits justify the cost.


Chaining exception is for debugging purposes.When you get a general exception, you can check if there is a chained lower exception and try to understand why that lower level exception occurred.

0

精彩评论

暂无评论...
验证码 换一张
取 消