1

The general rule of exceptions is that you catch more specific ones before catching general ones. I have a case where closing a server throws SocketException: socket closed coming from my listener which is caught by IOException, but I don't want to show the user that message when they close the server. Everything else is probably an actual error so it should be shown to them, so I catch SocketException, check its message and if it is not socket closed then it should be rethrown to be caught and handled as an IOException. Java/NetBeans 7.0.1 do not seem to like that. Here is my code:

public void run() {
        while (runner == Thread.currentThread()) {
            System.out.println("waiting for connection...");
            try {
                Socket s = server.accept(); //throws SocketException/IOException
                if (session == null) {
                    session = new ReceiveSession(s, parent);
                } else {
                    s.close();
                }
            } catch (SocketException e) {
                if (!e.getMessage().equals("socket closed")) {
                    throw e; //error line, "unreported exception SocketException"
                }
            } catch (IOException e) {
                e.printStackTrace();
                parent.showError("Someone tried to connect but the connection failed: " + e);
                session = null;
            }
        }

    }

When trying to clean and build, I get:

error: unreported exception SocketException; must be caught or declared to be thrown
                throw e;
1 error

Since SocketException extends IOException, it should be caught by the more general IOException. Why do I get this error? (Running the project as is in NetBeans works perfectly btw, it doesn't show the user the exception when the server shuts down as I want.)

ldam
  • 4,412
  • 6
  • 45
  • 76

3 Answers3

6

Your throw is outside the try block, so the thrown exception will not be handled by the catch below.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
4

The second catch will not catch the rethrow since it wasn't thrown in the try block for which the second catch is responsible. You'll need to restructure the code.


For example, the most straight forward restructuring would be this:

public void run() {
    while (runner == Thread.currentThread()) {
        System.out.println("waiting for connection...");
        try {
            try {
                Socket s = server.accept(); //throws SocketException/IOException
                if (session == null) {
                    session = new ReceiveSession(s, parent);
                } else {
                    s.close();
                }
            } catch (SocketException e) {
                if (!e.getMessage().equals("socket closed")) {
                    throw e; //error line, "unreported exception SocketException"
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            parent.showError("Someone tried to connect but the connection failed: " + e);
            session = null;
        }
    }
}

The nested tries make me cry a little, but it would get the job done (and to be honest, off the top of my head, I can't think of a better way that doesn't go way too far).

Corbin
  • 33,060
  • 6
  • 68
  • 78
  • So one cannot just rethrow an exception to a more general catch like (I think) you can in C#? Annoying, but it makes sense. – ldam Jan 04 '13 at 10:02
  • @biltong It's done for a reason if you think about it. A `catch (Exception)` block would mean that any exception thrown in a `catch` block of the same level would be automatically recaught. What if that *wasn't* the desired behavior? Yes, annoying in this situation, but this is a corner case, in my opinion. – Corbin Jan 04 '13 at 10:04
  • After restructuring as you suggested it's working fine with no errors, thank you. I'll accept the answer when I'm allowed to. – ldam Jan 04 '13 at 10:07
  • I would refactor it as `try {...} catch (IOException e) { if ((!(e instanceof SocketException)) || (!"socket closed".equals(e.getMessage())) { // [current code of the catch block here] }` – SJuan76 Jan 04 '13 at 10:07
  • @SJuan76 I considered that way, but really it's just no cleaner than nested tries. If the code grows much larger than this method already is though, that would probably be better to avoid getting into arrow head madness. (Also, the "".equals is better.... might edit in a minute) – Corbin Jan 04 '13 at 10:09
  • @SJuan76 good idea, but is it not better to use `&&` instead of `||` in this case? I want to check that it is a `SocketException` as well as get the message. There may be other reasons that a `SocketException` is thrown that would then be handled incorrectly by your code. – ldam Jan 04 '13 at 10:10
  • You want to enter the `if` if `e` is not a `SocketException` (another `IOException`) **or** if `e` is a `SocketException` and the message is not "socketClosed". This is what my condition does. – SJuan76 Jan 04 '13 at 10:20
  • You could always De Morgan it: `if (!a || !b) -> if (!(a && b))`. (And yes, De Morgan was just used a verb.) – Corbin Jan 04 '13 at 10:25
0

Catch statements catch exceptions from their respective try block, not from other catch blocks in the list. You're throwing the exception from outside the try, so it does not get caught by subsequent catch blocks.

Mike
  • 2,434
  • 1
  • 16
  • 19