Re: Returning external resources and Exceptions

From: Ryan Stewart (zzanNOtozz_at_gSPAMo.com)
Date: 12/15/04


Date: Tue, 14 Dec 2004 20:12:41 -0600


"Gerald Thaler" <gerald.thaler@gmx.de> wrote in message
news:cpnl9e$ai0$03$1@news.t-online.com...
[...]
> I worked out several solutions. None of them is "elegant". They all have
> some ugly code duplication. I first wrote a helper method that doesnt
> throw to somewhat reduce the clutter:

[...]
> Now the code is somewhat clearer: The safeClose()-calls are only executed
> when an exception is thrown. One could also perform some additional
> cleanup (deleting the output file) before rethrowing the exception in this
> case.
>
> Nevertheless, there remains the problem of code duplication. IMHO the root
> cause of the whole debacle is that the close() methods in the API can
> throw. The same is true for most other cleanup methods in the API:
> Socket.close(), java.sql.Connection.close() ...
>
> This seems to be a serious design flaw in the API. Cleanup code that
> throws is no good. It renders the finally keyword almost useless.
> 'finally' was intended primarily as a means for reliable cleanup of
> external Resources without code duplication. But it's incompatible with
> the API. 'finally' fails big time if the cleanup code itself can throw.
>
> It's only remotely usefull if you have just one resource to cleanup:
>
> } finally {
> if (out != null)
> out.close();
> }
>
> But even then there is a slight problem, as the out.close() call may throw
> and hide the original exception (the root cause of the problem) from the
> try-body. When there is more than one resource to take care of, 'finally'
> doesn't help at all if you want to write 100% reliable code. Worse, it
> even seems to fool some developers into writing broken code that will
> swallow exceptions. I wonder wether there exists an official working
> finally-idiom/pattern!?
>
I don't know of any patterns unfortunately, and I agree that it can get
messy, but that's the way file system interaction tends to be. You might
consider a more object oriented approach. Try this on for size:
import java.io.*;

public class Converter {

    protected FileInputStream in;
    protected FileOutputStream out;
    protected IOException exception;

    public Converter(FileInputStream in, FileOutputStream out) {
        this.in = in;
        this.out = out;
    }

    public void run() throws IOException {
        byte[] data;
        boolean success;
        do {
            data = this.read();
            success = this.write(convert(data));
        } while (data != null && success);
        cleanUp();
        // If any exceptions were thrown, rethrow the first one.
        if (exception != null) {
            throw exception;
        }
    }

    protected byte[] read() {
        byte[] result = null;
        try {
            // Read whatever from your input stream
        } catch (IOException ioe) {
            // If first IOException, remember it.
            if (exception == null) {
                exception = ioe;
            }
        } catch (Exception e) {
            // Do whatever
        }
        return result;
    }

    protected boolean write(byte[] data) {
        boolean success = false;
        try {
            // Write whatever to your output stream
            success = true;
        } catch (IOException ioe) {
            // If first IOException, remember it.
            if (exception == null) {
                exception = ioe;
            }
        } catch (Exception e) {
            // Do whatever
        }
        return success;
    }

    protected byte[] convert(byte[] data) {
        byte[] convertedData = null;
        // Do whatever transformations
        return convertedData;
    }

    protected void cleanUp() {
        // Try to close the input stream. Get reference to any IOException
        // if one wasn't already thrown.
        try {
            in.close();
        } catch (IOException ioe) {
            if (exception == null) {
                exception = ioe;
            }
        } catch (Exception e) {
            // Do whatever
        }
        // Try to close the output stream. Get reference to any IOException
        // if one wasn't already thrown.
        try {
            out.close();
        } catch (IOException ioe) {
            if (exception == null) {
                exception = ioe;
            }
        } catch (Exception e) {
            // Do whatever
        }
    }
}

I think it will accomplish what you want. It should compile once you get
something in the try blocks that can throw IOExceptions. It's still riddled
with try/catches, but they're more dispersed. Also, you could change the
catching of Exceptions to Throwables if you're really serious.



Relevant Pages

  • Re: Returning external resources and Exceptions
    ... > As you go through the exception handling, catch each IOException, ... One could also perform some additional cleanup ... cause of the whole debacle is that the closemethods in the API can throw. ... It's only remotely usefull if you have just one resource to cleanup: ...
    (comp.lang.java.help)
  • Re: DefWindowProc API Reference
    ... I can tell you that in some version of the API under some ... exception" so that "themes... ... get a chance to do cleanup". ...
    (microsoft.public.win32.programmer.ui)
  • Re: Why finally?
    ... other than organizational aesthetics ?? ... First of all, its not very common, or shouldn't be common to use catch all ... coders know that any cleanup will be within the finally block. ... An exception is raised in the catch block, finally will be executed, your ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: N1298 - try/finally for C
    ... implemented but I assume callers need code to handle "exception ... costs execution time even in code which does not use this feature. ... It simplifies error exit code through cleanup, ...
    (comp.std.c)
  • Re: Concentration in OCaml
    ... >> And what if cleanup() throws an exception? ... My concern with the first variant was that if no exception is risen the ... let unwind_protect body cleanup = ...
    (comp.lang.lisp)