
/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package org.jsimpledb.kv.util;

import com.google.common.base.Preconditions;

import java.io.Closeable;
import java.io.IOException;

import org.jsimpledb.kv.CloseableKVStore;
import org.jsimpledb.kv.KVStore;
import org.slf4j.LoggerFactory;

/**
 * A {@link ForwardingKVStore} also implementing {@link CloseableKVStore}, with some associated
 * underlying {@link Closeable} resource to close when {@link #close}'d.
 */
public class CloseableForwardingKVStore extends ForwardingKVStore implements CloseableKVStore {

    private final KVStore kvstore;
    private final Closeable closeable;

    private boolean closed;

    /**
     * Constructor for a do-nothing {@link #close}.
     *
     * <p>
     * With this construtor, nothing will actually be closed when {@link #close} is invoked.
     *
     * @param kvstore key/value store for forwarding
     */
    public CloseableForwardingKVStore(KVStore kvstore) {
        this(kvstore, null);
    }

    /**
     * Convenience constructor.
     *
     * @param kvstore key/value store for forwarding; will be closed on {@link #close}
     */
    public CloseableForwardingKVStore(CloseableKVStore kvstore) {
        this(kvstore, kvstore);
    }

    /**
     * Primary constructor.
     *
     * @param kvstore key/value store for forwarding
     * @param resource {@link Closeable} resource, or null for none
     * @throws IllegalArgumentException if {@code kvstore} is null
     */
    public CloseableForwardingKVStore(KVStore kvstore, Closeable resource) {
        Preconditions.checkArgument(kvstore != null, "null kvstore");
        this.kvstore = kvstore;
        this.closeable = resource;
    }

    @Override
    protected KVStore delegate() {
        return this.kvstore;
    }

    @Override
    public synchronized void close() {
        if (this.closed)
            return;
        try {
            if (this.closeable != null)
                this.closeable.close();
        } catch (IOException e) {
            // ignore
        }
        this.closed = true;
    }

    /**
     * Ensure the associated resource is {@link #close}'d before reclaiming memory.
     */
    @Override
    protected void finalize() throws Throwable {
        try {
            synchronized (this) {
                if (!this.closed) {
                    LoggerFactory.getLogger(this.getClass()).warn(this.getClass().getSimpleName()
                      + "[" + this.closeable + "] leaked without invoking close()");
                }
            }
            this.close();
        } finally {
            super.finalize();
        }
    }
}


@@ -262,32 +262,57 @@
 edb.kv.KVStore;%0A
+import org.slf4j.Logger;%0A
 import org.slf4j
@@ -602,16 +602,195 @@
 tore %7B%0A%0A
+    private static final boolean TRACK_ALLOCATIONS = Boolean.parseBoolean(%0A      System.getProperty(CloseableForwardingKVStore.class.getName() + %22.TRACK_ALLOCATIONS%22, %22false%22));%0A%0A
     priv
@@ -854,16 +854,56 @@
 oseable;
+%0A    private final Throwable allocation;
 %0A%0A    pr
@@ -1957,16 +1957,133 @@
 source;%0A
+        this.allocation = CloseableForwardingKVStore.TRACK_ALLOCATIONS ? new Throwable(%22allocated was here%22) : null;%0A
     %7D%0A%0A 
@@ -2628,32 +2628,66 @@
 %7B%0A        try %7B%0A
+            final boolean leaked;%0A
             sync
@@ -2720,20 +2720,25 @@
         
-if (
+leaked = 
 !this.cl
@@ -2741,16 +2741,54 @@
 s.closed
+;%0A            %7D%0A            if (leaked
 ) %7B%0A    
@@ -2799,19 +2799,34 @@
         
-   
+final Logger log =
  LoggerF
@@ -2862,14 +2862,45 @@
 s())
-.warn(
+;%0A                final String msg = 
 this
@@ -2930,30 +2930,8 @@
 me()
-%0A                     
  + %22
@@ -2987,16 +2987,112 @@
 close()%22
+;%0A                if (this.allocation != null)%0A                    log.warn(msg, this.allocation
 );%0A     
@@ -3094,33 +3094,36 @@
                 
-%7D
+else
 %0A            %7D%0A 
@@ -3111,33 +3111,54 @@
 lse%0A            
-%7D
+        log.warn(msg);
 %0A            thi
@@ -3146,32 +3146,36 @@
 g);%0A            
+    
 this.close();%0A  
@@ -3164,32 +3164,46 @@
   this.close();%0A
+            %7D%0A
         %7D finall

Add TRACK_ALLOCATIONS to help debug CloseableForwardingKVStore leaks.