1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | * Iso8601: |
6 | * Initial Developer: Robert Rathsack (firstName dot lastName at gmx dot de) |
7 | */ |
8 | package org.h2.util; |
9 | |
10 | import java.io.Closeable; |
11 | import java.io.PrintWriter; |
12 | import java.io.StringWriter; |
13 | import java.lang.ref.PhantomReference; |
14 | import java.lang.ref.ReferenceQueue; |
15 | import java.util.Collections; |
16 | import java.util.HashSet; |
17 | import java.util.Set; |
18 | |
19 | /** |
20 | * A phantom reference to watch for unclosed objects. |
21 | */ |
22 | public class CloseWatcher extends PhantomReference<Object> { |
23 | |
24 | /** |
25 | * The queue (might be set to null at any time). |
26 | */ |
27 | private static ReferenceQueue<Object> queue = new ReferenceQueue<Object>(); |
28 | |
29 | /** |
30 | * The reference set. Must keep it, otherwise the references are garbage |
31 | * collected first and thus never enqueued. |
32 | */ |
33 | private static Set<CloseWatcher> refs = createSet(); |
34 | |
35 | /** |
36 | * The stack trace of when the object was created. It is converted to a |
37 | * string early on to avoid classloader problems (a classloader can't be |
38 | * garbage collected if there is a static reference to one of its classes). |
39 | */ |
40 | private String openStackTrace; |
41 | |
42 | /** |
43 | * The closeable object. |
44 | */ |
45 | private Closeable closeable; |
46 | |
47 | public CloseWatcher(Object referent, ReferenceQueue<Object> q, |
48 | Closeable closeable) { |
49 | super(referent, q); |
50 | this.closeable = closeable; |
51 | } |
52 | |
53 | private static Set<CloseWatcher> createSet() { |
54 | return Collections.synchronizedSet(new HashSet<CloseWatcher>()); |
55 | } |
56 | |
57 | /** |
58 | * Check for an collected object. |
59 | * |
60 | * @return the first watcher |
61 | */ |
62 | public static CloseWatcher pollUnclosed() { |
63 | ReferenceQueue<Object> q = queue; |
64 | if (q == null) { |
65 | return null; |
66 | } |
67 | while (true) { |
68 | CloseWatcher cw = (CloseWatcher) q.poll(); |
69 | if (cw == null) { |
70 | return null; |
71 | } |
72 | if (refs != null) { |
73 | refs.remove(cw); |
74 | } |
75 | if (cw.closeable != null) { |
76 | return cw; |
77 | } |
78 | } |
79 | } |
80 | |
81 | /** |
82 | * Register an object. Before calling this method, pollUnclosed() should be |
83 | * called in a loop to remove old references. |
84 | * |
85 | * @param o the object |
86 | * @param closeable the object to close |
87 | * @param stackTrace whether the stack trace should be registered (this is |
88 | * relatively slow) |
89 | * @return the close watcher |
90 | */ |
91 | public static CloseWatcher register(Object o, Closeable closeable, |
92 | boolean stackTrace) { |
93 | ReferenceQueue<Object> q = queue; |
94 | if (q == null) { |
95 | q = new ReferenceQueue<Object>(); |
96 | queue = q; |
97 | } |
98 | CloseWatcher cw = new CloseWatcher(o, q, closeable); |
99 | if (stackTrace) { |
100 | Exception e = new Exception("Open Stack Trace"); |
101 | StringWriter s = new StringWriter(); |
102 | e.printStackTrace(new PrintWriter(s)); |
103 | cw.openStackTrace = s.toString(); |
104 | } |
105 | if (refs == null) { |
106 | refs = createSet(); |
107 | } |
108 | refs.add(cw); |
109 | return cw; |
110 | } |
111 | |
112 | /** |
113 | * Unregister an object, so it is no longer tracked. |
114 | * |
115 | * @param w the reference |
116 | */ |
117 | public static void unregister(CloseWatcher w) { |
118 | w.closeable = null; |
119 | refs.remove(w); |
120 | } |
121 | |
122 | /** |
123 | * Get the open stack trace or null if none. |
124 | * |
125 | * @return the open stack trace |
126 | */ |
127 | public String getOpenStackTrace() { |
128 | return openStackTrace; |
129 | } |
130 | |
131 | public Closeable getCloseable() { |
132 | return closeable; |
133 | } |
134 | |
135 | } |