4

I am using a third party REST API in which every single API call is defined as throws IOException. I am wrapping the REST API in a Repository-style class. However, given the API interface, I am forced to either declare every method in my repository as throws IOException, or wrap every single call and rethrow it as a runtime exception.

Is there any clean way of wrapping the entire API to catch/rethrow as my own custom RuntimeException instead? I know I can wrap the calls using AspectJ and intercept the IOException, but my signature for the method won't change.

Are there any tricks I can use to convert an Exception to a RuntimeException?

For example:

APIWrapper interface has method:

public String getAppBuilds(String report_changed_since, String only_latest, String include_in_progress) throws IOException

In my repo, I would like to be able to call APIWrapper.getAppBuilds() without needing to catch the IOException.

Eric B.
  • 23,425
  • 50
  • 169
  • 316
  • it is not clear if you are talking about client code accessing the crappy API or wrapping the crappy API on the server side? –  Sep 22 '15 at 15:52
  • create a clone interface of the API without IOExceptions, and then use a Proxy to forward calls to the real API and convert IOExceptions to RuntimeExceptions. – wero Sep 22 '15 at 15:59
  • > I know I can wrap the calls using AspectJ and intercept the IOException, but my signature for the method won't change. < You can also make use of declare soft in AspectJ to soften Checked Exceptions and keep the same signature. – John McClean Sep 22 '15 at 16:00
  • 2
    You definitely need to create an interface that has the same methods but without the `throws IOException` declaration. Every solution will start with this step. Then you need to create a proxy that implements your new interface and delegates it to the original API (taking care of wrapping any `IOException`s that may be thrown from them). How you do the proxying is your choice, a simple dynamic proxy may be adequate or you could use a byte code library to generate it. – biziclop Sep 22 '15 at 16:09
  • @wero - I'm trying to avoid doing just that; the interface is quite large and I don't want to have to keep this interface updated everything the API may change - just extra work/steps. – Eric B. Sep 22 '15 at 16:09
  • @EricB.If you use a proxy, you'll only have to do the wrapping once, not for every method. But you have to do it once somehow, there's no getting away from it. – biziclop Sep 22 '15 at 16:09
  • @EricB.You can generate the interface too. – biziclop Sep 22 '15 at 16:11
  • @biziclop - Can you provide an example of how you would structure/format such a proxy? I'm not entirely sure I follow/see how to structure the proxy. – Eric B. Sep 22 '15 at 16:11
  • Lombok might help you. If you add a @SneakyThrows on your method, you won't have to declare the IOException in your method signature (but it will be thrown anyway). See https://projectlombok.org/features/SneakyThrows.html – Benoît Sep 22 '15 at 16:19
  • @Benoît - Unfortunately, I can't use Lombok. – Eric B. Sep 22 '15 at 16:23
  • @EricB. I understand. I use and like Lombok, but I find some features like the SneakyThrow are a little dangerous. Sorry no other ideas yet... :( – Benoît Sep 22 '15 at 16:26
  • @JohnMcClean I tried using AspectJ to declare soft, but the maven-compiler-plugin still fails when it attempts to precompile prior to ajc that the IOException must be caught or thrown. – Eric B. Sep 22 '15 at 16:27
  • @EricB. Apparently there is a work around for that issue http://stackoverflow.com/questions/24211943/aspectj-maven-plugin-declare-soft-how-to-compile – John McClean Sep 22 '15 at 16:29
  • @JohnMcClean I tried to reorder the plugins (running ajc first), but ajc is not complaining about the same issue; IOException unhandled. And I confirmed that I see the API having been softened in my target/classes output director (using JD). But when it is trying to compile my class, it is still failing. – Eric B. Sep 22 '15 at 16:50
  • Hi Eric. I know this one is old, but still without an accepted answer. Are you still interested in a solution? – kriegaex Jan 24 '17 at 08:18
  • @kriegaex although I have long since moved on to other projects, I'm still interested in an elegant solution to this if such a thing exists. I believe I had ended up using AJ but still had to catch the IOException in the code. And with Jar sealed, I couldn't even override any of the classes either. – Eric B. Jan 24 '17 at 12:39
  • @JohnMcClean has actually linked to [my solution](http://stackoverflow.com/a/24341085/1082681) for `declare soft` in connection with AspectJ Maven Plugin before. I have just retested it and it works flawlessly, even after upgrading Maven Compiler and AspectJ Maven and using Java 8. If it works for you, I can write an answer. Otherwise tell me which errors you get and I can help you. – kriegaex Jan 24 '17 at 13:22

2 Answers2

0

You could use project Lombok @SneakyThrows annotation (see here). This annotation allows you to hide throws in the signature at compile time.

schrieveslaach
  • 1,689
  • 1
  • 15
  • 32
-1

The only possibility is to catch the Exception and convert it to a RuntimeException exactly as library like SpringJDBC do.

Basically something like that

public class OriginalLibrary {
    public void method1() throws IOException {
        ...
    }
}

public class LibraryWithoutException {
    private OriginalLibrary original;

    public LibraryWithoutException(OriginalLibrary original) {
        this.original = original;
    }

    public void method1()  {
        try {
            original.method1();
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}

Note: it will be better to create a custom RuntimeException instead of using the standard class.

Or with your api:

public class APIWrapperNoException {
    private APIWrapper api;

    public APIWrapperNoException(APIWrapper api) {
        this.api = api;
    }

    public String getAppBuilds(String report_changed_since, 
                               String only_latest, 
                               String include_in_progress) { 
        try {
            return api.getAppBuilds(report_changed_since, 
                                    only_latest, include_in_progress);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage());
        }
    }
} 
Davide Lorenzo MARINO
  • 26,420
  • 4
  • 39
  • 56