This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 257013
Collapse All | Expand All

(-)a/openide.util.lookup/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.util.lookup
2
OpenIDE-Module: org.openide.util.lookup
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/lookup/Bundle.properties
4
OpenIDE-Module-Specification-Version: 8.33
4
OpenIDE-Module-Specification-Version: 8.34
5
5
(-)a/openide.util.lookup/src/org/openide/util/lookup/AbstractLookup.java (-1 / +32 lines)
Lines 46-51 Link Here
46
import java.io.IOException;
46
import java.io.IOException;
47
import java.io.ObjectOutputStream;
47
import java.io.ObjectOutputStream;
48
import java.io.Serializable;
48
import java.io.Serializable;
49
import java.lang.ref.Reference;
49
import java.lang.ref.ReferenceQueue;
50
import java.lang.ref.ReferenceQueue;
50
import java.lang.ref.WeakReference;
51
import java.lang.ref.WeakReference;
51
import java.util.ArrayList;
52
import java.util.ArrayList;
Lines 61-72 Link Here
61
import java.util.Set;
62
import java.util.Set;
62
import java.util.TreeSet;
63
import java.util.TreeSet;
63
import java.util.concurrent.Executor;
64
import java.util.concurrent.Executor;
65
import java.util.concurrent.atomic.AtomicBoolean;
64
import java.util.logging.Level;
66
import java.util.logging.Level;
65
import java.util.logging.Logger;
67
import java.util.logging.Logger;
66
import org.openide.util.Lookup;
68
import org.openide.util.Lookup;
67
import org.openide.util.LookupEvent;
69
import org.openide.util.LookupEvent;
68
import org.openide.util.LookupListener;
70
import org.openide.util.LookupListener;
69
import org.openide.util.lookup.implspi.ActiveQueue;
71
import org.openide.util.lookup.implspi.ActiveQueue;
72
import org.openide.util.lookup.implspi.CallbackReferencesSupport;
70
73
71
74
72
/** Implementation of the lookup from OpenAPIs that is based on the
75
/** Implementation of the lookup from OpenAPIs that is based on the
Lines 1354-1365 Link Here
1354
1357
1355
        /** caches for results */
1358
        /** caches for results */
1356
        public Object caches;
1359
        public Object caches;
1360
        
1361
        private static final AtomicBoolean registered = new AtomicBoolean(false);
1362
        private final CallbackReferencesSupport callbackSupport;
1357
1363
1358
        /** Creates a weak refernece to a new result R in context of lookup
1364
        /** Creates a weak refernece to a new result R in context of lookup
1359
         * for given template
1365
         * for given template
1360
         */
1366
         */
1361
        private ReferenceToResult(R<T> result, AbstractLookup lookup, Template<T> template) {
1367
        private ReferenceToResult(R<T> result, AbstractLookup lookup, Template<T> template) {
1362
            super(result, activeQueue());
1368
            this(new CallbackReferencesSupport(), result, lookup, template);
1369
        }
1370
        
1371
        private ReferenceToResult(CallbackReferencesSupport callbackSupport,
1372
                                  R<T> result, AbstractLookup lookup, Template<T> template) {
1373
            super(result, callbackSupport.getReferenceQueue());
1374
            this.callbackSupport = callbackSupport;
1375
            callbackSupport.setCallback(this);
1376
            if (!registered.getAndSet(true)) {
1377
                CallbackReferencesSupport.addReferenceSupportProvider(ReferenceToResult.class, new SupportProvider());
1378
            }
1363
            this.template = template;
1379
            this.template = template;
1364
            this.lookup = lookup;
1380
            this.lookup = lookup;
1365
            getResult().reference = this;
1381
            getResult().reference = this;
Lines 1396-1404 Link Here
1396
            }
1412
            }
1397
        }
1413
        }
1398
1414
1415
        @Override
1416
        protected final void finalize() throws Throwable {
1417
            callbackSupport.notifyFinalized();
1418
            super.finalize();
1419
        }
1420
        
1399
        private ReferenceToResult<T> cloneRef() {
1421
        private ReferenceToResult<T> cloneRef() {
1400
            return new ReferenceToResult<T>(getResult(), lookup, template);
1422
            return new ReferenceToResult<T>(getResult(), lookup, template);
1401
        }
1423
        }
1424
        
1425
        private static final class SupportProvider implements CallbackReferencesSupport.ReferenceSupportProvider {
1426
1427
            @Override
1428
            public CallbackReferencesSupport getSupport(Reference reference) {
1429
                return ((ReferenceToResult) reference).callbackSupport;
1430
            }
1431
            
1432
        }
1402
    }
1433
    }
1403
     // end of ReferenceToResult
1434
     // end of ReferenceToResult
1404
1435
(-)a/openide.util.lookup/src/org/openide/util/lookup/implspi/CallbackReferencesSupport.java (+301 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util.lookup.implspi;
43
44
import java.lang.ref.Reference;
45
import java.lang.ref.ReferenceQueue;
46
import java.lang.ref.WeakReference;
47
import java.util.HashMap;
48
import java.util.Map;
49
import java.util.logging.Level;
50
import java.util.logging.Logger;
51
52
/**
53
 * Support class for building callback references. Callback references are able
54
 * to execute a dedicated callback when the object the reference refers to, becomes unreachable.
55
 * <p>
56
 * Do not use this class, unless you know well what you're doing. Use rather
57
 * <code>org.openide.util.CallbackReferences</code> instead.
58
 * <p>
59
 * <b>Usage:</b>
60
 * <ol>
61
 * <li>Create new CallbackReferencesSupport(Runnable) with the desired callback,
62
 *     or new CallbackReferencesSupport() and set the callback before the reference
63
 *     object can become unreachable.</li>
64
 * <li>Create the desired reference with getReferenceQueue().
65
 *     It's important that there is one reference instance per one
66
 *     CallbackReferencesSupport instance.</li>
67
 * <li>Override the reference implementation's finalize method and call
68
 *     notifyFinalized() in it.</li>
69
 * <li>Register the reference class with the callback support provider via
70
 *     addReferenceSupportProvider()</li>
71
 * </ol>
72
 * 
73
 * @author Martin Entlicher
74
 * @since 8.34
75
 * @see org.openide.util.CallbackReferences
76
 */
77
public final class CallbackReferencesSupport {
78
    
79
    private static final Logger LOGGER = Logger.getLogger(CallbackReferencesSupport.class.getName());
80
    private static Reference<ReferenceQueue> activeReferenceQueue = new WeakReference<ReferenceQueue>(null);
81
    private static long activeRefsCount = 0l;
82
    private static final Map<Class<? extends Reference>, ReferenceSupportProvider> supportProvidersMap = new HashMap<Class<? extends Reference>, ReferenceSupportProvider>();
83
    
84
    private final ReferenceQueue callbackRefQueue;  // Need to hold the reference so that it's not GC'ed prematurely
85
    Runnable callback;
86
        
87
    /**
88
     * Gets the active reference queue.
89
     * @return the singleton queue
90
     */
91
    private static synchronized ReferenceQueue<Object> queue() {
92
        ReferenceQueue rq = activeReferenceQueue.get();
93
        if (rq == null) {
94
            rq = new ReferenceQueue();
95
            activeReferenceQueue = new WeakReference<ReferenceQueue>(rq);
96
            Daemon.ping();
97
        }
98
        return rq;
99
    }
100
    
101
    /**
102
     * Register a reference support provider. We need a way how to get the
103
     * instance of {@link CallbackReferencesSupport} associated with the
104
     * Reference when the reference is cleared by GC.
105
     * @param refClass The class type of the reference.
106
     * @param supportProvider The provider that knows how to retrieve the support from the reference of this class type.
107
     */
108
    public static void addReferenceSupportProvider(Class<? extends Reference> refClass, ReferenceSupportProvider supportProvider) {
109
        synchronized (supportProvidersMap) {
110
            supportProvidersMap.put(refClass, supportProvider);
111
        }
112
    }
113
    
114
    private static CallbackReferencesSupport getSupport(Reference ref) {
115
        Class refClass = ref.getClass();
116
        ReferenceSupportProvider rsp = null;
117
        synchronized (supportProvidersMap) {
118
            for (Map.Entry<Class<? extends Reference>, ReferenceSupportProvider> entry : supportProvidersMap.entrySet()) {
119
                if (entry.getKey().isAssignableFrom(refClass)) {
120
                    rsp = entry.getValue();
121
                    break;
122
                }
123
            }
124
        }
125
        if (rsp != null) {
126
            return rsp.getSupport(ref);
127
        } else {
128
            return null;
129
        }
130
    }
131
    
132
    private static Runnable checkCallback(Runnable callback) {
133
        if (callback == null) {
134
            throw new NullPointerException("The callback should not be null."); // NOI18N
135
        }
136
        return callback;
137
    }
138
    
139
    /**
140
     * Creates a new empty callback support, one instance to be used with one {@link Reference}.
141
     * It's necessary to set the callback runnable via
142
     * {@link #setCallback(java.lang.Runnable)} before the associated reference
143
     * is cleared.
144
     */
145
    public CallbackReferencesSupport() {
146
        this(null, queue());
147
    }
148
    
149
    /**
150
     * Creates a new callback support with associated callback runnable,
151
     * one instance to be used with one {@link Reference}.
152
     * @param callback The runnable which is run after the associated reference is cleared by GC.
153
     */
154
    public CallbackReferencesSupport(Runnable callback) {
155
        this(checkCallback(callback), queue());
156
    }
157
    
158
    private CallbackReferencesSupport(Runnable callback, ReferenceQueue queue) {
159
        this.callbackRefQueue = queue;
160
        this.callback = callback;
161
        synchronized (CallbackReferencesSupport.class) {
162
            activeRefsCount++;
163
        }
164
    }
165
    
166
    /**
167
     * Get the reference queue, solely for the purpose of reference creation.
168
     * @return The reference queue.
169
     */
170
    public ReferenceQueue getReferenceQueue() {
171
        return callbackRefQueue;
172
    }
173
    
174
    /**
175
     * Set a callback runnable to be called after the associated reference is cleared by GC.
176
     * @param callback 
177
     */
178
    public void setCallback(Runnable callback) {
179
        this.callback = callback;
180
    }
181
182
    void cleanup() {
183
        try {
184
            callback.run();
185
        } finally {
186
            callback = null;
187
            died();
188
        }
189
    }
190
191
    /**
192
     * Notify that the associated reference was finalized.
193
     */
194
    public void notifyFinalized() {
195
        if (callback != null) {
196
            callback = null;
197
            died();
198
        }
199
    }
200
201
    void died() {
202
        synchronized (CallbackReferencesSupport.class) {
203
            if (--activeRefsCount == 0) {
204
                activeReferenceQueue.clear();
205
                Daemon.finish();
206
            }
207
        }
208
    }
209
    
210
    /**
211
     * A provider of {@link CallbackReferencesSupport} for the given {@link Reference}.
212
     * @see #addReferenceSupportProvider(java.lang.Class, org.openide.util.lookup.implspi.CallbackReferencesSupport.ReferenceSupportProvider)
213
     */
214
    public static interface ReferenceSupportProvider {
215
        
216
        /**
217
         * Retrieve the {@link CallbackReferencesSupport} for the given {@link Reference}.
218
         * @param reference The reference
219
         * @return callback support associated with the reference.
220
         */
221
        CallbackReferencesSupport getSupport(Reference reference);
222
        
223
    }
224
    
225
    static final class Daemon extends Thread {
226
        private static Daemon running;
227
        
228
        public Daemon() {
229
            super("Cleanable Reference Queue Daemon");
230
        }
231
        
232
        static synchronized void ping() {
233
            if (running == null) {
234
                Daemon t = new Daemon();
235
                t.setPriority(Thread.MIN_PRIORITY);
236
                t.setDaemon(true);
237
                t.start();
238
                LOGGER.fine("starting thread");
239
                running = t;
240
            }
241
        }
242
        
243
        static synchronized boolean isActive() {
244
            return running != null;
245
        }
246
        
247
        static synchronized void finish() {
248
            if (running != null) {
249
                running.doFinish();
250
            }
251
        }
252
253
        static synchronized ReferenceQueue obtainQueue() {
254
            ReferenceQueue rq= activeReferenceQueue.get();
255
            if (rq == null) {
256
                running = null;
257
            }
258
            return rq;
259
        }
260
        
261
        private void doFinish() {
262
            interrupt();
263
        }
264
        
265
        @Override
266
        public void run() {
267
            while (true) {
268
                try {
269
                    ReferenceQueue rq = obtainQueue();
270
                    if (rq == null) {
271
                        return;
272
                    }
273
                    Reference<?> ref = rq.remove();
274
                    LOGGER.log(Level.FINE, "Got {0} with {1}", new Object[]{ref, ref == null ? null : ref.get()});
275
                    CallbackReferencesSupport support = getSupport(ref);
276
                    if (support == null) {
277
                        LOGGER.log(Level.WARNING, "An unexpected reference in the queue: {0}", ref.getClass());
278
                        continue;
279
                    }
280
                    // do the cleanup
281
                    try {
282
                        support.cleanup();
283
                    } catch (ThreadDeath td) {
284
                        throw td;
285
                    } catch (Throwable t) {
286
                        // Should not happen.
287
                        // If it happens, it is a bug in client code, notify!
288
                        LOGGER.log(Level.WARNING, "Cannot process " + ref, t);
289
                    } finally {
290
                        // to allow GC
291
                        ref = null;
292
                        support = null;
293
                    }
294
                } catch (InterruptedException ex) {
295
                    // Can happen during VM shutdown, it seems. Ignore.
296
                    continue;
297
                }
298
            }
299
        }
300
    }
301
}
(-)a/openide.util.lookup/test/unit/src/org/openide/util/lookup/LookupPermGenLeakTest.java (-1 / +1 lines)
Lines 132-138 Link Here
132
    
132
    
133
    public void testClassLoaderCanGC() throws Exception {
133
    public void testClassLoaderCanGC() throws Exception {
134
        Reference<?> ref = new WeakReference<Object>(createClass());
134
        Reference<?> ref = new WeakReference<Object>(createClass());
135
        // assertGC("Can be GCed", ref); TODO: Uncomment after #257013 is implemented.
135
        assertGC("Can be GCed", ref);
136
    }
136
    }
137
    
137
    
138
    private synchronized int waitForOne() throws InterruptedException {
138
    private synchronized int waitForOne() throws InterruptedException {
(-)a/openide.util/apichanges.xml (+20 lines)
Lines 50-55 Link Here
50
        <apidef name="xml_base">XML API</apidef>
50
        <apidef name="xml_base">XML API</apidef>
51
    </apidefs>
51
    </apidefs>
52
    <changes>
52
    <changes>
53
    <change id="callback-references">
54
        <api name="util_base"/>
55
        <summary>CallbackReferences class provides references that are able to
56
            call a defined callback, after the referent object becomes unreachable.</summary>
57
        <version major="9" minor="8"/>
58
        <date year="2015" month="12" day="15"/>
59
        <author login="mentlicher"/>
60
        <compatibility addition="yes" binary="compatible" source="compatible"/>
61
        <description>
62
            <p>
63
                <code>CallbackReferences</code> class provides a set of reference
64
                implementations that are able to process the given callback runnable,
65
                after their referent object becomes unreachable. The callback
66
                processing is performed on a dedicated daemon thread,
67
                which is shut down when there are no more callbacks to process.
68
            </p>
69
        </description>
70
        <class package="org.openide.util" name="CallbackReferences"/>
71
        <issue number="257013"/>
72
    </change>
53
    <change id="weak-listen-on-specific-property">
73
    <change id="weak-listen-on-specific-property">
54
        <api name="util_base"/>
74
        <api name="util_base"/>
55
        <summary>Weak property and vetoable listeners for a specific property name.</summary>
75
        <summary>Weak property and vetoable listeners for a specific property name.</summary>
(-)a/openide.util/manifest.mf (-1 / +1 lines)
Lines 1-5 Link Here
1
Manifest-Version: 1.0
1
Manifest-Version: 1.0
2
OpenIDE-Module: org.openide.util
2
OpenIDE-Module: org.openide.util
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/base/Bundle.properties
3
OpenIDE-Module-Localizing-Bundle: org/openide/util/base/Bundle.properties
4
OpenIDE-Module-Specification-Version: 9.7
4
OpenIDE-Module-Specification-Version: 9.8
5
5
(-)a/openide.util/src/org/openide/util/BaseUtilities.java (+2 lines)
Lines 224-229 Link Here
224
     * Be sure to call this method anew for each reference.
224
     * Be sure to call this method anew for each reference.
225
     * Do not attempt to cache the return value.
225
     * Do not attempt to cache the return value.
226
     * @since 3.11
226
     * @since 3.11
227
     * @Deprecated This API requires an indefinitely running background thread.
228
     *             Use {@link CallbackReferences} instead.
227
     */
229
     */
228
    public static ReferenceQueue<Object> activeReferenceQueue() {
230
    public static ReferenceQueue<Object> activeReferenceQueue() {
229
        return ActiveQueue.queue();
231
        return ActiveQueue.queue();
(-)a/openide.util/src/org/openide/util/CallbackReferences.java (+275 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util;
43
44
import java.lang.ref.PhantomReference;
45
import java.lang.ref.Reference;
46
import java.lang.ref.SoftReference;
47
import java.lang.ref.WeakReference;
48
import java.util.concurrent.atomic.AtomicBoolean;
49
import org.openide.util.lookup.implspi.CallbackReferencesSupport;
50
51
/**
52
 * This class provides references, that are able to call a defined callback,
53
 * after the referent object becomes unreachable. It is useful when there is
54
 * a need to run a callback code after the referent is collected.
55
 * <p>
56
 * Usually, in order to implement such logic, one needs to either create
57
 * a dedicated thread that blocks on the queue and is <code>Object.notify</code>-ed,
58
 * which is the right approach but consumes valuable system resources (threads),
59
 * or one can periodically check the content of the queue by
60
 * <code>RequestProcessor.Task.schedule</code> which is completely wrong,
61
 * because it wakes up the system every (say) 15 seconds.
62
 * <p>
63
 * In order to provide an efficient support for this problem, these references
64
 * has been provided. They share one background thread to process the callbacks,
65
 * which dies when there are no more callbacks to process.<br>
66
 * Be sure not to block in such callbacks for a long time as this prevents other
67
 * waiting references from processing.
68
 * 
69
 * @author Martin Entlicher
70
 * @since 9.8
71
 */
72
public final class CallbackReferences {
73
    
74
    private CallbackReferences() {}
75
    
76
    private static <T> T referentCheck(T referent) {
77
        if (referent == null) {
78
            throw new NullPointerException("The referent must not be null.");   // NOI18N
79
        }
80
        return referent;
81
    }
82
    
83
    /**
84
     * An implementation of {@link PhantomReference} with a callback functionality.
85
     * After the referent becomes unreachable, the provided Runnable callback is executed.
86
     */
87
    public static class Phantom<T> extends PhantomReference<T> {
88
        
89
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
90
        
91
        final CallbackReferencesSupport support;
92
        
93
        /**
94
         * Create a new phantom reference with a callback. Both referent and the
95
         * callback should not be <code>null</code>.
96
         * @param referent the object the new phantom reference will refer to
97
         * @param callback the callback executed after the referent becomes unreachable.
98
         */
99
        public Phantom(T referent, Runnable callback) {
100
            this(referentCheck(referent), new CallbackReferencesSupport(callback));
101
        }
102
        
103
        private Phantom(T referent, CallbackReferencesSupport support) {
104
            super(referent, support.getReferenceQueue());
105
            this.support = support;
106
            if (!REGISTERED.getAndSet(true)) {
107
                CallbackReferencesSupport.addReferenceSupportProvider(Phantom.class, new SupportProvider());
108
            }
109
        }
110
        
111
        @Override
112
        protected final void finalize() throws Throwable {
113
            super.finalize();
114
            support.notifyFinalized();
115
        }
116
        
117
        private static final class SupportProvider implements CallbackReferencesSupport.ReferenceSupportProvider {
118
119
            @Override
120
            public CallbackReferencesSupport getSupport(Reference reference) {
121
                return ((Phantom) reference).support;
122
            }
123
            
124
        }
125
        
126
    }
127
    
128
    /**
129
     * An implementation of {@link SoftReference} with a callback functionality.
130
     * After the referent becomes unreachable, the provided Runnable callback is executed.
131
     */
132
    public static class Soft<T> extends SoftReference<T> {
133
        
134
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
135
        
136
        final CallbackReferencesSupport support;
137
        
138
        /**
139
         * Create a new soft reference with a callback. Both referent and the
140
         * callback should not be <code>null</code>.
141
         * @param referent the object the new soft reference will refer to
142
         * @param callback the callback executed after the referent becomes unreachable.
143
         */
144
        public Soft(T referent, Runnable callback) {
145
            this(referentCheck(referent), new CallbackReferencesSupport(callback));
146
        }
147
        
148
        private Soft(T referent, CallbackReferencesSupport support) {
149
            super(referent, support.getReferenceQueue());
150
            this.support = support;
151
            if (!REGISTERED.getAndSet(true)) {
152
                CallbackReferencesSupport.addReferenceSupportProvider(Soft.class, new SupportProvider());
153
            }
154
        }
155
        
156
        @Override
157
        protected final void finalize() throws Throwable {
158
            support.notifyFinalized();
159
            super.finalize();
160
        }
161
        
162
        private static final class SupportProvider implements CallbackReferencesSupport.ReferenceSupportProvider {
163
164
            @Override
165
            public CallbackReferencesSupport getSupport(Reference reference) {
166
                return ((Soft) reference).support;
167
            }
168
            
169
        }
170
        
171
    }
172
    
173
    /**
174
     * An implementation of {@link WeakReference} with a callback functionality.
175
     * After the referent becomes unreachable, the provided Runnable callback is executed.
176
     */
177
    public static class Weak<T> extends WeakReference<T> {
178
        
179
        private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
180
        
181
        final CallbackReferencesSupport support;
182
        
183
        /**
184
         * Create a new weak reference with a callback. Both referent and the
185
         * callback should not be <code>null</code>.
186
         * @param referent the object the new weak reference will refer to
187
         * @param callback the callback executed after the referent becomes unreachable.
188
         */
189
        public Weak(T referent, Runnable callback) {
190
            this(referentCheck(referent), new CallbackReferencesSupport(callback));
191
        }
192
        
193
        private Weak(T referent, CallbackReferencesSupport support) {
194
            super(referent, support.getReferenceQueue());
195
            this.support = support;
196
            if (!REGISTERED.getAndSet(true)) {
197
                CallbackReferencesSupport.addReferenceSupportProvider(Weak.class, new SupportProvider());
198
            }
199
        }
200
        
201
        @Override
202
        protected final void finalize() throws Throwable {
203
            support.notifyFinalized();
204
            super.finalize();
205
        }
206
        
207
        private static final class SupportProvider implements CallbackReferencesSupport.ReferenceSupportProvider {
208
209
            @Override
210
            public CallbackReferencesSupport getSupport(Reference reference) {
211
                return ((Weak) reference).support;
212
            }
213
            
214
        }
215
        
216
    }
217
    
218
    /**
219
     * An abstract {@link PhantomReference}, which acts itself as a callback
220
     * by implementing {@link Runable}.
221
     * After the referent becomes unreachable, it's <code>run()</code> method
222
     * is executed.
223
     */
224
    public static abstract class PhantomCallback<T> extends Phantom<T> implements Runnable {
225
        
226
        /**
227
         * Create a new phantom callback reference.
228
         * @param referent the object the new phantom reference will refer to
229
         */
230
        public PhantomCallback(T referent) {
231
            super(referentCheck(referent), new CallbackReferencesSupport());
232
            support.setCallback(this);
233
        }
234
        
235
    }
236
    
237
    /**
238
     * An abstract {@link SoftReference}, which acts itself as a callback
239
     * by implementing {@link Runable}.
240
     * After the referent becomes unreachable, it's <code>run()</code> method
241
     * is executed.
242
     */
243
    public static abstract class SoftCallback<T> extends Soft<T> implements Runnable {
244
        
245
        /**
246
         * Create a new soft callback reference.
247
         * @param referent the object the new phantom reference will refer to
248
         */
249
        public SoftCallback(T referent) {
250
            super(referentCheck(referent), new CallbackReferencesSupport());
251
            support.setCallback(this);
252
        }
253
        
254
    }
255
    
256
    /**
257
     * An abstract {@link WeakReference}, which acts itself as a callback
258
     * by implementing {@link Runable}.
259
     * After the referent becomes unreachable, it's <code>run()</code> method
260
     * is executed.
261
     */
262
    public static abstract class WeakCallback<T> extends Weak<T> implements Runnable {
263
        
264
        /**
265
         * Create a new weak callback reference.
266
         * @param referent the object the new phantom reference will refer to
267
         */
268
        public WeakCallback(T referent) {
269
            super(referentCheck(referent), new CallbackReferencesSupport());
270
            support.setCallback(this);
271
        }
272
        
273
    }
274
    
275
}
(-)a/openide.util/test/unit/src/org/openide/util/CallbackReferencesTest.java (+237 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2015 Oracle and/or its affiliates. All rights reserved.
5
 *
6
 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7
 * Other names may be trademarks of their respective owners.
8
 *
9
 * The contents of this file are subject to the terms of either the GNU
10
 * General Public License Version 2 only ("GPL") or the Common
11
 * Development and Distribution License("CDDL") (collectively, the
12
 * "License"). You may not use this file except in compliance with the
13
 * License. You can obtain a copy of the License at
14
 * http://www.netbeans.org/cddl-gplv2.html
15
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
16
 * specific language governing permissions and limitations under the
17
 * License.  When distributing the software, include this License Header
18
 * Notice in each file and include the License file at
19
 * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
20
 * particular file as subject to the "Classpath" exception as provided
21
 * by Oracle in the GPL Version 2 section of the License file that
22
 * accompanied this code. If applicable, add the following below the
23
 * License Header, with the fields enclosed by brackets [] replaced by
24
 * your own identifying information:
25
 * "Portions Copyrighted [year] [name of copyright owner]"
26
 *
27
 * If you wish your version of this file to be governed by only the CDDL
28
 * or only the GPL Version 2, indicate your decision by adding
29
 * "[Contributor] elects to include this software in this distribution
30
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
31
 * single choice of license, a recipient has the option to distribute
32
 * your version of this file under either the CDDL, the GPL Version 2 or
33
 * to extend the choice of license to its licensees as provided above.
34
 * However, if you add GPL Version 2 code and therefore, elected the GPL
35
 * Version 2 license, then the option applies only if the new code is
36
 * made subject to such option by the copyright holder.
37
 *
38
 * Contributor(s):
39
 *
40
 * Portions Copyrighted 2015 Sun Microsystems, Inc.
41
 */
42
package org.openide.util;
43
44
import java.lang.ref.WeakReference;
45
import java.lang.reflect.Method;
46
import static junit.framework.TestCase.assertTrue;
47
import static junit.framework.TestCase.fail;
48
import org.netbeans.junit.NbTestCase;
49
import static org.netbeans.junit.NbTestCase.assertGC;
50
import org.openide.util.lookup.implspi.CallbackReferencesSupport;
51
52
/**
53
 *
54
 * @author Martin Entlicher
55
 */
56
public class CallbackReferencesTest extends NbTestCase {
57
    
58
    public CallbackReferencesTest(String testName) {
59
        super(testName);
60
    }
61
    
62
    private boolean isDaemonActive() throws Exception {
63
        // CallbackReferencesSupport.Daemon.isActive()
64
        Class daemonClass = Class.forName("org.openide.util.lookup.implspi.CallbackReferencesSupport$Daemon");
65
        Method isActiveMethod = daemonClass.getDeclaredMethod("isActive");
66
        isActiveMethod.setAccessible(true);
67
        return (Boolean) isActiveMethod.invoke(null);
68
    }
69
70
    @Override
71
    protected void tearDown() throws Exception {
72
        // Test that the Daemon thread finishes eventually
73
        while (isDaemonActive()) {
74
            System.gc();
75
            Thread.sleep(10);
76
        }
77
    }
78
    
79
    public void testRunnableReferenceIsExecuted () throws Exception {
80
        Object obj = new Object ();
81
        RunnableRef ref = new RunnableRef (obj);
82
        synchronized (ref) {
83
            obj = null;
84
            assertGC ("Should be GCed quickly", ref);
85
            ref.wait ();
86
            assertTrue ("Run method has been executed", ref.executed);
87
        }
88
    }
89
    
90
    public void testRunnablesAreProcessedOneByOne () throws Exception {
91
        Object obj = new Object ();
92
        RunnableRef ref = new RunnableRef (obj);
93
        ref.wait = true;
94
        
95
        
96
        synchronized (ref) {
97
            obj = null;
98
            assertGC ("Is garbage collected", ref);
99
            ref.wait ();
100
            assertTrue ("Still not executed, it is blocked", !ref.executed);
101
        }    
102
103
        RunnableRef after = new RunnableRef (new Object ());
104
        synchronized (after) {
105
            assertGC ("Is garbage collected", after);
106
            after.wait (100); // will fail
107
            assertTrue ("Even if GCed, still not processed", !after.executed);
108
        }
109
110
        synchronized (after) {
111
            synchronized (ref) {
112
                ref.notify ();
113
                ref.wait ();
114
                assertTrue ("Processed", ref.executed);
115
            }
116
            after.wait ();
117
            assertTrue ("Processed too", after.executed);
118
        }
119
    }
120
    
121
    public void testManyReferencesProcessed() throws InterruptedException {
122
        int n = 100;
123
        Object[] objects = new Object[n];
124
        ExpensiveRef[] refs = new ExpensiveRef[n];
125
        for (int i = 0; i < n; i++) {
126
            objects[i] = new Object();
127
            refs[i] = new ExpensiveRef(objects[i], Integer.toString(i));
128
        }
129
        objects = null;
130
        for (int i = 0; i < n; i++) {
131
            assertGC("is GC'ed", refs[i]);
132
        }
133
        for (int i = 0; i < n; i++) {
134
            synchronized (refs[i]) {
135
                while (!refs[i].executed) {
136
                    refs[i].wait();
137
                }
138
            }
139
        }
140
    }
141
    
142
    public void testNullNotAccepted() {
143
        NullPointerException npe = null;
144
        try {
145
            new CallbackReferences.Phantom<Object>(null, new Runnable() { public void run() {}});
146
        } catch (NullPointerException ex) {
147
            npe = ex;
148
        }
149
        assertNotNull("NullPointerException not thrown!", npe);
150
        npe = null;
151
        try {
152
            new CallbackReferences.Phantom<Object>(new Object(), null);
153
        } catch (NullPointerException ex) {
154
            npe = ex;
155
        }
156
        assertNotNull("NullPointerException not thrown!", npe);
157
        npe = null;
158
        try {
159
            new CallbackReferences.Soft<Object>(null, new Runnable() { public void run() {}});
160
        } catch (NullPointerException ex) {
161
            npe = ex;
162
        }
163
        assertNotNull("NullPointerException not thrown!", npe);
164
        npe = null;
165
        try {
166
            new CallbackReferences.Soft<Object>(new Object(), null);
167
        } catch (NullPointerException ex) {
168
            npe = ex;
169
        }
170
        assertNotNull("NullPointerException not thrown!", npe);
171
        npe = null;
172
        try {
173
            new CallbackReferences.Weak<Object>(null, new Runnable() { public void run() {}});
174
        } catch (NullPointerException ex) {
175
            npe = ex;
176
        }
177
        assertNotNull("NullPointerException not thrown!", npe);
178
        npe = null;
179
        try {
180
            new CallbackReferences.Weak<Object>(new Object(), null);
181
        } catch (NullPointerException ex) {
182
            npe = ex;
183
        }
184
        assertNotNull("NullPointerException not thrown!", npe);
185
    }
186
    
187
    private static class RunnableRef extends CallbackReferences.WeakCallback<Object>
188
    implements Runnable {
189
        public boolean wait;
190
        public boolean entered;
191
        public boolean executed;
192
        
193
        public RunnableRef (Object o) {
194
            super(o);
195
        }
196
        
197
        @Override
198
        public synchronized void run () {
199
            entered = true;
200
            if (wait) {
201
                // notify we are here
202
                notify ();
203
                try {
204
                    wait ();
205
                } catch (InterruptedException ex) {
206
                }
207
            }
208
            executed = true;
209
            
210
            notifyAll ();
211
        }
212
    }
213
    
214
    private static class ExpensiveRef extends CallbackReferences.WeakCallback<Object>
215
    implements Runnable {
216
        public boolean executed;
217
        private final String name;
218
        
219
        public ExpensiveRef (Object o, String name) {
220
            super(o);
221
            this.name = name;
222
        }
223
        
224
        @Override
225
        public synchronized void run () {
226
            executed = true;
227
            try {
228
                Thread.sleep(10);
229
                System.gc();
230
                Thread.sleep(10);
231
            } catch (InterruptedException iex) {}
232
            notifyAll ();
233
            //System.err.println(name+" executed.");
234
        }
235
    }
236
    
237
}

Return to bug 257013