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

前端 未结 5 508
忘了有多久
忘了有多久 2021-02-01 03:38

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

5条回答
  •  粉色の甜心
    2021-02-01 04:07

    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();
            });
        }
    }
    

提交回复
热议问题