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 \"dispo
If you are interested in consistency in tests, just add method destroy()
marked by @AfterClass
annotation into test class and close all previously allocated resources in it.
If you are interested in an approach that allow you to protect the resource from being not closed, you could provide a way that doesn't expose resource to user explicitly. For example, you code could control resource life cycle and accept only Consumer
from user.
If you can't do that, but still want to be sure that resource will be closed even if user doesn't use it correctly you'll have to do several tricky things. You could split your resource on sharedPtr
and resource
itself. Then expose sharedPtr
to user and put it into some internal storage wrapped into WeakReference
. As result of that you'll be able to catch the moment when GC removes sharedPtr
and call close()
on the resource
. Be aware that resource
must not be exposed to user. I prepared an example, it's not very accurate, but hope it shows the idea:
public interface Resource extends AutoCloseable {
public int jniCall();
}
class InternalResource implements Resource {
public InternalResource() {
// Allocate resources here.
System.out.println("Resources were allocated");
}
@Override public int jniCall() {
return 42;
}
@Override public void close() {
// Dispose resources here.
System.out.println("Resources were disposed");
}
}
class SharedPtr implements Resource {
private final Resource delegate;
public SharedPtr(Resource delegate) {
this.delegate = delegate;
}
@Override public int jniCall() {
return delegate.jniCall();
}
@Override public void close() throws Exception {
delegate.close();
}
}
public class ResourceFactory {
public static Resource getResource() {
InternalResource resource = new InternalResource();
SharedPtr sharedPtr = new SharedPtr(resource);
Thread watcher = getWatcherThread(new WeakReference<>(sharedPtr), resource);
watcher.setDaemon(true);
watcher.start();
Runtime.getRuntime().addShutdownHook(new Thread(resource::close));
return sharedPtr;
}
private static Thread getWatcherThread(WeakReference ref, InternalResource resource) {
return new Thread(() -> {
while (!Thread.currentThread().isInterrupted() && ref.get() != null)
LockSupport.parkNanos(1_000_000);
resource.close();
});
}
}