How to tell caller of an error if you cant change the signature of amethod

Discussion in 'Java' started by Royan, Apr 14, 2008.

  1. Royan

    Royan Guest

    Lets say you have the following interface:

    Interface IFoo {
    public String[] readStrings();
    }



    The thing is that you cannot change that interface, but you have to
    implement it. Now that readStrings() method must read from file, then
    do some parsing and finally retrieve an array of Strings. As you can
    imagine a couple of exceptional conditions have to be handled within
    the implementation of this method:

    1) IOException
    2) EOFException
    3) Reader initialization error (failure of BufferedReader, or
    DataInputStream, whatever is used in implementation)
    ....
    n)

    I'll repeat, one cannot change the signature of the method, for
    instance, I cannot add *throws* declaration. So what would you do in
    order to tell caller of an error? Please note that caller needs to
    differentiate one error form another, so returning null is OK but only
    for one type of error.
    And one the most important things, that method must implemented in two
    roles: RMI and File-System, what would be your suggestions taking into
    consideration everything said above?
     
    Royan, Apr 14, 2008
    #1
    1. Advertisements

  2. Royan

    Roedy Green Guest

    you can throw an IllegalArgumentException or some other unchecked
    exception.
     
    Roedy Green, Apr 14, 2008
    #2
    1. Advertisements

  3. Royan

    Jan Thomä Guest

    If the interface is under your control, you can define a set of Exceptions
    that can be thrown by implementations on certain circumstances:

    - MyFooIGotAnErrorWhileReadingException etc..

    and add these to the signature. If the interface however is not under your
    control, you have no other way than throwing unchecked exceptions
    (Exceptions derived from RuntimeException), as Roedy Green already
    mentioned.


    Best regards,
    Jan
     
    Jan Thomä, Apr 14, 2008
    #3
  4. One example of where it is clearly infeasible is, when the
    interface is actually not ones own work, like Runnable.run().
     
    Andreas Leitgeb, Apr 14, 2008
    #4
  5. Royan

    Jan Thomä Guest

    Okay for the sake of nitpicking there are a bazillion other ways of doing
    it. However I implied that

    - the OP has to use the provided interface
    - cannot change the provided interface

    Therefore you can either hack something into the return value, which is
    impractical as the caller cannnot find out what went wrong (as the OP
    clearly stated) or you throw RuntimeExceptions which are not checked by the
    compiler. Or you can invent some kind of messaging service into which the
    called function pushes it's error code and the caller retrieves it, but i
    thought we speak about practical solutions here...

    Jan
     
    Jan Thomä, Apr 14, 2008
    #5
  6. I don't think that's an entirely impractical solution:

    public interface ExceptionHandler {
    public void handleException (Exception e);
    }

    public class MyFoo implements IFoo {
    private final ExceptionHandler eh;

    public MyFoo (ExceptionHandle eh) {
    this.eh = eh;
    }

    public String[] readStrings () {
    try {
    // Read from a file and possibly throw IOException
    } catch (RuntimeException re) {
    throw re; // propagate unchecked exc. untouched
    } catch (Exception e) {
    eh.handleException (e);
    }
    }
    }

    There are infinite variations on this; you could, for example, make
    readStrings rethrow checked exceptions as RuntimeExceptions if eh is
    null, or remove the bypass for unchecked exceptions and make
    ExceptionHandler deal with Throwable instead of Exception.

    Sure, it's clunkier than the natural solution: the exception handler
    is lexically separated from the code that can throw an exception.
    However, if the interface really is immutable, this might be the
    cleanest way I can think of to retain the checked-ness of exceptions
    without changing the interface.

    For completeness, here's an ExceptionHandler that knows about
    IOExceptions:

    public class IOExceptionHandler implements ExceptionHandler {
    public void handleException (Exception e) {
    try {
    throw e;
    } catch (IOException ioe) {
    System.err.println ("Handled an IOException: " + ioe);
    } catch (Exception e) {
    // Unexpected variety of failure here.
    throw new RuntimeException (e);
    }
    }
    }

    As illustrated, the design of ExceptionHandler as written demands that
    implementations be prepared for *any* Exception, even if the caller
    will only ever pass some specific type of Exception. The design could
    be improved by providing specialized ExceptionHandler interfaces for
    different implementations of IFoo, such that each handler only has to
    deal with the exceptions that can actually be thrown.

    -o
     
    Owen Jacobson, Apr 14, 2008
    #6
  7. Royan

    Jan Thomä Guest

    Well a good approach to implement the error handling, indeed. And probably
    not even impractical.
    Indeed. Plus every implementation can come up with a different way of
    implementing error handling which is probably not a good thing either. So
    if the interface is specified that way the OP layed out, it would imply to
    me that this function must not throw any exception in any case - or at
    least it would imply that the caller does not need to expect any exceptions
    to be thrown by any implementation. If this was not the case, the function
    should specify which Exceptions are to be thrown on which conditions.

    So since the implementation is free to do any error handling strategy that
    the implementor deems good enough, i'd say wrapping any exception into a
    runtime exception does the job for me. Otherwise the interface (== the
    contract between me and the caller) should have stated something else.

    Jan
     
    Jan Thomä, Apr 14, 2008
    #7
  8. I don't think it should be an existing exception. Rather, it should be a
    class, extending RuntimeException, that is declared in the same package
    as the implementing code. That way, there is no risk that some method in
    the call stack throws the same exception with a specified meaning, and
    it is being caught by something that method's caller.

    The new exception can, of course, wrap an IOException etc. to preserve
    all the details.

    Patricia
     
    Patricia Shanahan, Apr 14, 2008
    #8
  9. Rethrowing an exception does not, under the Sun JVM, clear its
    stacktrace:

    public class Foo {
    public static void main(String[] args) {
    Handler h = new Handler();

    try {
    throw new Exception();
    } catch (Exception e) {
    h.handle(e);
    }
    }
    }

    class Handler {
    public void handle(Exception e) {
    try {
    throw e;
    } catch (Exception ee) {
    e.printStackTrace();
    }
    }
    }

    //--

    Prints:

    java.lang.Exception
    at Foo.main(Foo.java:6)

    The stacktrace is actually populated when the exception is created;
    one of the problems with creating an Exception object in advance is
    that the stacktrace will not indicate where it was thrown!

    There are some clever uses for this behaviour that I will leave it to
    you to discover. :)
    That works for single exceptions (and it's probably how I'd do it
    under the constraints), but not all services only throw one type of
    exception that's more specific than Exception. Other approaches are
    necessary if you want to make the exception handler interface specific
    to the exceptions that can actually be thrown, for these services.

    -o
     
    Owen Jacobson, Apr 14, 2008
    #9
  10. Lew wrote:
    ....
    I think this is very application dependent, and the OP needs to look at
    the problem from the caller's point of view. For example, if I access my
    bank's web site, I would rather get a general "unable to process your
    transaction" than a denial that I have any accounts with them.

    Patricia
     
    Patricia Shanahan, Apr 14, 2008
    #10
  11. .... even, if for some reason the bank believes that the latter
    was the case ?
     
    Andreas Leitgeb, Apr 14, 2008
    #11
  12. Yes, if that is the bank's considered opinion. No, if the web site has
    just lost contact with the database, and has no idea whether I have
    accounts or not.

    Patricia
     
    Patricia Shanahan, Apr 14, 2008
    #12
  13. Royan

    Royan Guest

    OK So let me summarize: There are three options:

    1) Wrap checked exceptions and rethrow some sort of RuntimeException
    that would contain some detailed message of an error
    2) Retrieve some predefined, most likely constant value of type
    String[] that any class can access and understand what has happened.
    3) Create an exception handling method and handle all exceptional
    cases there

    Am I correct that there are no more solutions to the problem (assuming
    we cannot change interface)?

    If so I vote for option 2, because option 1 and 3 is evil. I'll
    explain why, though again I'll just summarize arguments that have
    already been outlined in this or that way above.

    1) Wrapping exception is a bad idea since we break the semantical
    meaning of the code. Utility method should not handle exceptions fired
    because of its activity. It must simply handover the situation to some
    other method. If it was not like that I doubt we would see every API
    IO read/write method throwing IOException
    3) Exception handler method that is used within utility method seems
    to be even worse. First because caller should (in our case this is the
    method where a call to readStream occurs) always receive information
    about the success or failure of "readStrings()" operation. If we grab
    every checked exception and return null indicating an error we will
    never be able to properly separate view from model. For instance if
    FileNotFoundException has occurred, I may want to visually tell user
    of an error, but if i grab that exception an return null I will not be
    able to understand if it was a FileNotFoundException or IOException or
    whatever exception I will always receive null.

    So what do you think? Do you concur with my arguments?

    -R.
     
    Royan, Apr 14, 2008
    #13
  14. So, even if the bank's considered opinion is that you do not have an
    account with them, then you'd still prefer a message like "unable to
    process your transaction", but if it was just an issue with DB
    connectivity, then you'd want to be told that "you do not have any
    accounts with them" ?

    Funny, I'd have chosen exactly the opposite ... :)
     
    Andreas Leitgeb, Apr 14, 2008
    #14
  15. The point I was making was to implement 3 by having the caller pass in
    its desired exception handling strategy (via the constructor in my
    example). It is equivalent to wrapping the method in an application-
    specific try/catch block; the difference is in how it's implemented,
    not in the functionality it provides.

    So, for your case, you would pass in a handler (exception callback, if
    you will) that knows to display a "file not found" dialog on
    FileNotFoundException.

    -o
     
    Owen Jacobson, Apr 14, 2008
    #15
  16. Royan

    Jan Thomä Guest

    That is why they declare it. We talked about having a fixed method
    declaration which cannot be changed. In that case the method was
    ill-declared in the first place and should have declared the right way,
    that is specifying the exceptions to be thrown and catched. Since we cannot
    redeclare it, i go for option 1 because:

    2) Worst of all for me, because you can easily forget to handle this
    special return value and your code will run all the time you code it
    (because the error never happens), it will run through all your test cases
    but it will blow when being in production for the first month and you will
    not even have an exception stacktrace but just a program that produces
    weird behaviour with no idea why it does that way (you can search for ages
    on such a thing, I've had that to the limit). Throwing an exception is the
    best way to make sure that errors are handled, because you either handle
    them or you got a nice big stacktrace in your error logs and your client
    will surely ask you about it (well my client at least would do so).
    Additionally you might have a hard time finding a special return value at
    all which will not be returned when the function exits normally (e.g.
    assume a function that does a name lookup and returns null when no such
    name can be found - how do you handle an exception like "i had no database"
    when the null you got could result from normal operation as well?).
    Which however is more or less the thing you suggest in option 2) ;) Except
    for the fact that you can get the error message from somewhere, which you
    cannot do when doing plain option 2). So its still not good but better than
    option 2), i think.


    Anyways, i suppose most of the important stuff has been said to this point.
    I assume, that we all can agree that a proper error handling strategy needs
    to be tailored to the problem and that there is no silver bullet in doing
    so. One has to do some tradeoffs when being faced with facts that one
    cannot change (like an ill-designed external library), and making a choice
    is not always easy, so asking for a good strategy is never a bad idea. I
    liked the discussion very much :)

    Jan
     
    Jan Thomä, Apr 14, 2008
    #16
  17. Royan

    Royan Guest

    Thanks Jan, and everyone who has taken part in the discussion, I agree
    with Jan that everyone can make up his/her mind now and decide which
    solution is better.
     
    Royan, Apr 14, 2008
    #17
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.