/*
 * Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
 * and the EPL 1.0 (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 * Iso8601:
 * Initial Developer: Robert Rathsack (firstName dot lastName at gmx dot de)
 */
package org.h2.util;

import java.io.Closeable;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

A phantom reference to watch for unclosed objects.
/** * A phantom reference to watch for unclosed objects. */
public class CloseWatcher extends PhantomReference<Object> {
The queue (might be set to null at any time).
/** * The queue (might be set to null at any time). */
private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
The reference set. Must keep it, otherwise the references are garbage collected first and thus never enqueued.
/** * The reference set. Must keep it, otherwise the references are garbage * collected first and thus never enqueued. */
private static Set<CloseWatcher> refs = createSet();
The stack trace of when the object was created. It is converted to a string early on to avoid classloader problems (a classloader can't be garbage collected if there is a static reference to one of its classes).
/** * The stack trace of when the object was created. It is converted to a * string early on to avoid classloader problems (a classloader can't be * garbage collected if there is a static reference to one of its classes). */
private String openStackTrace;
The closeable object.
/** * The closeable object. */
private Closeable closeable; public CloseWatcher(Object referent, ReferenceQueue<Object> q, Closeable closeable) { super(referent, q); this.closeable = closeable; } private static Set<CloseWatcher> createSet() { return Collections.synchronizedSet(new HashSet<CloseWatcher>()); }
Check for an collected object.
Returns:the first watcher
/** * Check for an collected object. * * @return the first watcher */
public static CloseWatcher pollUnclosed() { ReferenceQueue<Object> q = queue; if (q == null) { return null; } while (true) { CloseWatcher cw = (CloseWatcher) q.poll(); if (cw == null) { return null; } if (refs != null) { refs.remove(cw); } if (cw.closeable != null) { return cw; } } }
Register an object. Before calling this method, pollUnclosed() should be called in a loop to remove old references.
Params:
  • o – the object
  • closeable – the object to close
  • stackTrace – whether the stack trace should be registered (this is relatively slow)
Returns:the close watcher
/** * Register an object. Before calling this method, pollUnclosed() should be * called in a loop to remove old references. * * @param o the object * @param closeable the object to close * @param stackTrace whether the stack trace should be registered (this is * relatively slow) * @return the close watcher */
public static CloseWatcher register(Object o, Closeable closeable, boolean stackTrace) { ReferenceQueue<Object> q = queue; if (q == null) { q = new ReferenceQueue<>(); queue = q; } CloseWatcher cw = new CloseWatcher(o, q, closeable); if (stackTrace) { Exception e = new Exception("Open Stack Trace"); StringWriter s = new StringWriter(); e.printStackTrace(new PrintWriter(s)); cw.openStackTrace = s.toString(); } if (refs == null) { refs = createSet(); } refs.add(cw); return cw; }
Unregister an object, so it is no longer tracked.
Params:
  • w – the reference
/** * Unregister an object, so it is no longer tracked. * * @param w the reference */
public static void unregister(CloseWatcher w) { w.closeable = null; refs.remove(w); }
Get the open stack trace or null if none.
Returns:the open stack trace
/** * Get the open stack trace or null if none. * * @return the open stack trace */
public String getOpenStackTrace() { return openStackTrace; } public Closeable getCloseable() { return closeable; } }