Pregunta

So I have a try/finally block. I need to execute a number of methods in the finally block. However, each one of those methods can throw an exception. Is there a way to ensure that all these methods are called (or attempted) without nested finally blocks?

This is what I do right now, which is pretty ugly :

protected void verifyTable() throws IOException {
    Configuration configuration = HBaseConfiguration.create();
    HTable hTable = null;                                               

    try {
        hTable = new HTable(configuration, segmentMatchTableName);      

        //...
        //various business logic here
        //...

    } finally {                         
        try {
            try {
                if(hTable!=null) {
                    hTable.close(); //This can throw an IOException
                }               
            } finally {
                try {
                    generalTableHelper.deleteTable(configuration, segmentMatchTableName); //This can throw an IOException
                } finally {
                    try {
                        generalTableHelper.deleteTable(configuration, wordMatchTableName); //This can throw an IOException
                    } finally {
                        generalTableHelper.deleteTable(configuration, haplotypeTableName); //This can throw an IOException      
                    }
                }
            }                               
        } finally {
            HConnectionManager.deleteConnection(configuration, true); //This can throw an IOException   
        }
    }               
}

Is there a more-elegant way to do this?

¿Fue útil?

Solución

The standard (working) way to right resource management in Java (the principle applies to other languages as well) is:

Resource resource = acquire(resource);
try {
    use(resource);
} finally {
    resource.release();
}

Or using the shortcut (with an extra bit of cleverness) in the current version of Java SE:

try (Resource resource = acquire(resource)) {
    use(resource);
}

(As Joe K points out, you may need to wrap the resource to make it confirm to the specific interface that the Java language depends upon.)

Two resources, and you just apply the idiom twice:

Resource resource = acquire(resource);
try {
    SubResource sub = resource.acquire();
    try {
        use(sub);
    } finally {
        sub.release();
    }
} finally {
    resource.release();
}

And in Java SE 7:

try (
    Resource resource = acquire(resource);
    SubResource sub = resource.acquire()
) {
    use(resource, sub);
}

The really great advantage of the new language feature is that resource handling was more often than not broken when written out.

You might have more complicated exception handling. For instance, you don't want to throw low-level exceptions such as IOException through to the application proper - you probably want to wrap in some subtype of RuntimeException. This can, with Java's typicaly verboseness, be factored out using the Execute Around idiom (see this excellent question). From Java SE 8, there will also be shorter syntax with randomly different semantics.

with(new ResourceSubAction() { public void use(Resource resource, SubResource sub) {
    ... use resource, sub ...
}});

Otros consejos

If this is Java 7, you could consider using the new try-with-resources construct. You may need to create some basic AutoCloseable wrappers for deleting the tables.

In general, there's no way out of this. You need multiple finally blocks.

However, I don't want to comment on your specific code, whether or not that's an appropriate design. It certainly looks pretty odd.

There is no way I'm afraid. There was a similar pattern when closing io resources. Eg what do you do when closing a file throws an IOException? Usually you just had to ignore it. As this was a bit if an anti pattern they introduced the try-with syntax in Java 7. For your example though I think there is no other option. Perhaps put each finally into its own method to make it clearer

To call multiple methods from a finally block, you have to ensure that none of them throw -- which is a good idea anyway, because any exception thrown from a finally block will override the exception or return value thrown from the try/catch.

The most common use case is a file or database connection, in which case you write a "close quietly" method (or use one from an existing library, such as Jakarta Commons IO). If the things you need to clean up don't let you use a pre-existing method, you write your own (in your case, deleteTableQuietly()).

If you're using JDK-7, you can also use the "try with resource" construct.

You could create an abstract class Action with an execute method, and derive from that class one class for each method throwing exception which you want to call, calling this method from the execute method. Then you can create a list of Actions and loop over the elements of the list, calling their execute method in a try finally block, ignoring exceptions.

deleteTableSilently(table1);    
deleteTableSilently(table2);    
deleteTableSilently(table3);


deleteTableSilently()
    try
        deleteTable()
    catch whatever
        log.error();

Consider using the java.util.concurrent framework -- if you code each call as an individual Callable (named or anonymous), you could use ExecutorService.invokeAll.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top