d56 d56 - 2 months ago 20
Java Question

How to use guava Closer properly

i'm struggling to figure out how to use the Closer utility from the guava library. Please see the code below.


  • One thing is, that the IndexWriter throws an
    IOException
    on both object initialization and
    close()
    . Therefore, the code in the finally and rethrow blocks is underlined.

  • The other question is, why do i have to catch
    Throwable
    instead of other exception types and do i have to rethrow the errors (i would prefer to log those at the spot)



`

int getDocumentsCount() {
Closer closer = Closer.create();
try {
IndexWriter iwriter = closer.register(openIndexWriter());
return iwriter.numDocs();
} catch (Throwable e) {
logger.error(e, e);
return -1;
} finally {
closer.close();
}
}


IndexWriter openIndexWriter() throws IOException {
return new IndexWriter(directory, analyzer, false,
IndexWriter.MaxFieldLength.UNLIMITED);
}


`

Thanks a lot

(stuck with Java 6)

Answer

From Guava's own explanation, you have to use Throwable, yes.

Here's their example snippet:

public void foo() throws IOException {
  Closer closer = Closer.create();
  try {
    InputStream in = closer.register(openInputStream());
    OutputStream out = closer.register(openOutputStream());
    // do stuff with in and out
  } catch (Throwable e) { // must catch Throwable
    throw closer.rethrow(e);
  } finally {
    closer.close();
  }
}

Note that they catch Throwable and rethrow it directly from the Closer instance.

As to why it is Throwable and not, let's say IOException or RuntimeException, it's because the Closer must know that an error occurred so that it can close the resources properly. All is only a matter of doing things proper. So it can work if you don't do it properly, but it's not guaranteed.

Not that if your method can throw MyOwnCheckedException, for instance, you have to declare them:

} catch (Throwable t) {
  throw closer.rethrow(e, MyOwnCheckedException.class);
} finally {
  closer.close();
}

Java 7 example, for comparison:

public void foo() throws IOException {
  try (InputStream in = openInputStream();
       OutputStream out = openOutputStream();
    // do stuff with in and out
  }
}

If you compare the Closer example with the Java 7 example, you can see that I still have to declare the IOException in the method signature.


For your case, this is what you have to do:

int getDocumentsCount() {
  try {
    Closer closer = Closer.create();
    try {
       IndexWriter iwriter = closer.register(openIndexWriter());
      return iwriter.numDocs();
    } catch (Throwable e) {
      closer.rethrow(e);
    } finally {
      closer.close();
    }
  } catch (IOException e) {
    logger.error(e, e);
    return -1;
  }
}

To avoid try-pyramids, I'd do the following:

int getDocumentsCount() {
  try {
    return doGetDocumentsCount();
  } catch (IOException e) {
    logger.error(e, e);
    return -1;
  }
}

int doGetDocumentsCount() {
  Closer closer = Closer.create();
  try {
    IndexWriter iwriter = closer.register(openIndexWriter());
    return iwriter.numDocs();
  } catch (Throwable e) {
    closer.rethrow(e);
  } finally {
    closer.close();
  }
}