In Java, how to check that AutoCloseable.close() has been called?

  • A+
Category:Languages

I am authoring a java library. Some of the classes that are meant to be used by library users, hold native system resources (over JNI). I'd like to ensure that the user "disposes" these objects, as they are heavy, and in a testsuite they may cause leakage between testcases (for example, I need to ensure TearDown will dispose). For this purpose I made the Java classes implement AutoCloseable, but this doesn't seem to suffice, or I'm not using it correctly:

  1. I don't see how to use try-with-resources statement in the context of tests (I'm using JUnit5 with Mockito), in that the "resource" is not short-lived - it is part of the test fixture.

  2. Being diligent as always, I tried implementing finalize() and testing for closure there, but it turns out finalize() is not even called (Java10). This is also marked as deprecated and I'm sure this idea will be frowned upon.

How is this done? To be clear, I want the application's tests (that use my library) to fail if they don't call close() on my objects.


Edit: adding some code if it helps. It's not much, but it's what I'm trying to do.

@SuppressWarnings("deprecation") // finalize() provided just to assert closure (deprecated starting Java 9) @Override protected final void finalize() throws Throwable {     if (nativeHandle_ != 0) {          // TODO finalizer is never called, how to assert that close() gets called?         throw new AssertionError("close() was not called; native object leaking");     } } 

 


This post does not directly answer your question, but provides an alternate point of view.

One approach to make your clients consistently call close is to free them from this responsibility.

How can you do it?

Use template pattern.

Inspired by Spring framework

This approach is widely used in Spring framework.

See for example:

Example

You mentioned that you're working with TCP, so lets assume that you have a TcpConnection class that has a close() method.

Lets define TcpConnectionOpertaions interface:

public interface TcpConnectionOperations {   <T> T doWithConnection(TcpConnectionAction<T> action); } 

and implement it:

public class TcpConnectionTemplate implements TcpConnectionOperations {   @Override   public <T> T doWithConnection(TcpConnectionAction<T> action) {     try (TcpConnection tcpConnection = getConnection()) {       return action.doWithConnection(tcpConnection);     }   } } 

TcpConnectionAction is just callback, nothing fancy.

public interface TcpConnectionAction<T> {   <T> T doWithConnection(TcpConnection tcpConnection); } 

How the library should be consumed now?

  • It must be consumed only through TcpConnectionOperations interface.
  • Consumers supply only actions

For example:

String s = tcpConnectionOperations.doWithConnection(connection -> {   // do what we with with the connection   // returning to string for example   return connection.toString(); }); 

Pros

  • Clients don't have to worry about:
    • getting a TcpConnection
    • closing the connection
  • You are in control of creating connections:
    • you can cache them
    • log them
    • collect statistics
    • many other use cases...
  • In tests you can provide mock TcpConnectionOperations and mock TcpConnections and make assertions against them

Cons

This approach may not work if the lifecycle of a resource is longer than action. Maybe it is necessary for the client to keep the resource for a longer time.

Then you might want to dive deep in ReferenceQueue/Cleaner (since Java 9) and related API.


If you're interested you can watch this talk. It's in Russian, but still may be helpful (part of my answer is inspired by this talk too).

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: