/*
* 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;
}
}