1

I was experimenting with Apache HTTP client library in Eclipse

<dependency org="org.apache.httpcomponents" name="httpclient" rev="4.3.1"/>  

and following snippet of code throws checked Exception and needs to be handled.

 HttpResponse response = httpClient.execute(httprequest);

Eclipse gives 3 suggestions

  1. Add throws Exception - throws ClientProtocolException, IOException (Works fine)

  2. Surround with try catch -

    try {
        HttpResponse response = httpClient.execute(httprequest);
    }
    catch (ClientProtocolException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    

    (Also works fine)

  3. Surround with try/multicatch

    try {
        HttpResponse response = httpClient.execute(httprequest);
    }
    catch (ClientProtocolException | IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    

3rd option gives error

The exception ClientProtocolException is already caught by the alternative IOException

I saw the source code for ClientProtocolException and it IOException. As far as my understanding goes when catching multiple Exception we can catch more generic Exception below more specific one. So we can't catch ClientProtocolException after catching IOException.

So why does this happen in multi try-catch? And if it is not suppose to work why does Eclipse give that suggestion in 1st place?

Aniket Thakur
  • 66,731
  • 38
  • 279
  • 289

3 Answers3

1

One must take a look at the bytecode generated by javac to understand why the compiler error arises.

I modified your code and added a RuntimeException in order to make it compile and analyse the generated byte code.

The following source code

try {
    HttpResponse response = httpClient.execute(request);
} catch (ClientProtocolException | RuntimeException e) {
    System.out.println("ClientProtocolException and RuntimeException");
} catch (IOException e) {
    System.out.println("IOException");
}

will lead to this byte code like this

TRYCATCHBLOCK L0 L1 L2 org/apache/http/client/ClientProtocolException
TRYCATCHBLOCK L0 L1 L2 java/lang/RuntimeException
TRYCATCHBLOCK L0 L1 L3 java/io/IOException
...
L2
 FRAME SAME1 java/lang/Exception
 ASTORE 1
L5
  LINENUMBER 18 L5
  GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  LDC "ClientProtocolException and RuntimeException"
  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L6
  GOTO L4
L3
  LINENUMBER 19 L3
  FRAME SAME1 java/io/IOException
  ASTORE 1
L7
  LINENUMBER 20 L7
  GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  LDC "IOException"
  INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L4
...

As you can see the compiler just generates an exception table entry (TRYCATCHBLOCK) that points to the same byte code (L2) in case of an ClientProtocolException or RuntimeException.

So what would happen if you try to do this

catch (ClientProtocolException | IOException e)

Than the compiler would have to generate two exception table entries that point to the same byte code to execute and they have a common hierarchy (one is the subclass of the other). This means that if an exception is caught and the jvm tries to determine the next byte code to execute that two exception table entries would match. So the compiler gives you the error: The exception ClientProtocolException is already caught by the alternative IOException.

Just remove the ClientProtocolException, because a ClientProtocolException is an IOException.

René Link
  • 48,224
  • 13
  • 108
  • 140
1

The code should not compile according to the JLS:

It is a compile-time error if a union of types contains two alternatives Di and Dj (i ≠ j) where Di is a subtype of Dj (§4.10.2).

Looking through Eclipse's bug list, there are a few related to the quick-fix multi-catch proposals. This looks like Bug 388724.

httpClient.execute throws both exceptions when it only needs to throw IOException, which is a case that the Eclipse developers didn't consider when testing.

fgb
  • 18,439
  • 2
  • 38
  • 52
1

With respect to the try/multicatch option #3: ClientProtocolException is a subclass of IOException. Since you want the same catch-block behavior for both, there's no need to catch exceptions of the subclass type explicitly. Any such thrown exceptions will be caught by the IOException superclass.

This question was also answered here.

Community
  • 1
  • 1
fwc
  • 103
  • 2