{"id":114,"date":"2009-12-01T16:01:00","date_gmt":"2009-12-01T15:01:00","guid":{"rendered":"http:\/\/bininda.com\/blog\/?p=114"},"modified":"2009-12-01T16:27:48","modified_gmt":"2009-12-01T15:27:48","slug":"weak-listeners","status":"publish","type":"post","link":"http:\/\/bininda.com\/blog\/2009\/12\/weak-listeners\/","title":{"rendered":"Weak Listeners"},"content":{"rendered":"<h2>Resource Deallocation in Java<\/h2>\n<blockquote><p>Resource deallocation in Java is no problem since Java has automatic garbage collection.<\/p><\/blockquote>\n<p>Right! &#8230;? &#8230;?? &#8230;<strong>Wrong!<\/strong><\/p>\n<p>Since Java has automatic garbage collection, resource deallocation is often taken care of for you,<br \/>\nbut when it is not, it becomes a real pain.<br \/>\nSince the language relies so heavily on garbage collection, it seems the language<br \/>\ndesigners forgot to include good mechanisms for the cases where garbage collection does<br \/>\nnot solve the problem (releasing external resources) or just does not happen automatically.<\/p>\n<p>A common scenario for memory leaks in Java is Listener registration and de-registration.<br \/>\nWhen you want one component to react to events in another component you usually use a listener<br \/>\npattern. The listening component registers itself as a callback client to an observable component.<\/p>\n<p>Finding the right place to register the listener usually is not a problem.<br \/>\nOften it is done in the constructor of the listening component or in some setup<br \/>\nmethod.<\/p>\n<p>Unregistering however is often not so obvious since there is no canonical tear down<br \/>\nmechanism in Java. You are often left with listeners staying registered in their<br \/>\nobservables and therefore not eligable for garbage collection.<\/p>\n<h2>Netbeans Solution<\/h2>\n<p>The Netbeans developers have addressed this problem by providing a class called<br \/>\nWeakListener. It works by inserting a WeakListener object between the listener<br \/>\nand the observable. The listener is only referenced weakly (with a WeakReference)<br \/>\nand therefore is automatically collected, when other references to the listener<br \/>\ngo away. The WeakListener class also unregisters itself automagically when it<br \/>\nnotices that its concrete listener has been collected. Unregistering the WeakListener<br \/>\nis only possible, if the observable conforms to certain conventions.<\/p>\n<p>This solution is great, when you have a given model and want to make sure, that<br \/>\nyour listeners do not remain referenced indefinitely.<\/p>\n<h2>My Solution<\/h2>\n<p>My solution works the other way around. The observable has support for weak listeners<br \/>\nbuilt in. It keeps its list of listeners in a WeakListener support class. Listeners<br \/>\ncan register themselves in the usual way. They may unregister themselves explicitly<br \/>\nor just count on being disconnected automatically.<\/p>\n<p>This solution is great, when you are designing a model (observable) and want to<br \/>\nmake life easier for your clients (listeners).<\/p>\n<h2>Example<\/h2>\n<h3>Observable<\/h3>\n<pre class=\"brush: java\">public class NumberModel {\r\n\r\n    private WeakNumberChangedListeners listeners =\r\n            new WeakNumberChangedListeners();\r\n\r\n    private int theNumber = 0;\r\n    private String name;\r\n\r\n    public NumberModel(String name)\r\n    {\r\n        this.name = name;\r\n    }\r\n\r\n    public synchronized void inc()\r\n    {\r\n        int oldNumber = theNumber;\r\n        theNumber++;\r\n        listeners.fire (new NumberChangedEvent(this, oldNumber, theNumber));\r\n    }\r\n\r\n    public synchronized void dec()\r\n    {\r\n        int oldNumber = theNumber;\r\n        theNumber--;\r\n        listeners.fire (new NumberChangedEvent(this, oldNumber, theNumber));\r\n    }\r\n\r\n    public synchronized int getNumber ( )\r\n    {\r\n        return theNumber;\r\n    }\r\n\r\n    public String getName()\r\n    {\r\n        return name;\r\n    }\r\n\r\n    public void addNumberChangedLisener (NumberChangedListener listener)\r\n    {\r\n        listeners.add (listener);\r\n    }\r\n\r\n    public void removeNumberChangedListener (NumberChangedListener listener)\r\n    {\r\n        listeners.remove (listener);\r\n    }\r\n}<\/pre>\n<h3>Listener Interface<\/h3>\n<pre class=\"brush: java\">public interface NumberChangedListener {\r\n\r\n    void numberChanged (NumberChangedEvent event);\r\n\r\n}<\/pre>\n<h3>WeakListeners class<\/h3>\n<pre class=\"brush: java\">public class WeakNumberChangedListeners\r\n        extends WeakListeners&lt;NumberChangedListener, NumberChangedEvent&gt;\r\n{\r\n\r\n    @Override\r\n    protected void fireOne(NumberChangedListener listener, NumberChangedEvent event)\r\n    {\r\n        listener.numberChanged (event);\r\n    }\r\n\r\n}\r\n}<\/pre>\n<h2>WeakListeners.java<\/h2>\n<pre class=\"brush: java\">package com.bininda.weaklistener;\r\n\r\nimport java.lang.ref.WeakReference;\r\nimport java.util.Collections;\r\nimport java.util.LinkedList;\r\nimport java.util.List;\r\nimport java.util.Set;\r\nimport java.util.WeakHashMap;\r\n\r\n\/**\r\n * A list of listeners that are only referenced weakly.\r\n * Listeners are registered using the add method. They may be deregistered\r\n * using the remove method, however if the listener is garbage collected, because\r\n * it is not hard referenced any more, it is automatically removed.\r\n * The method fireOne must be overwritten in a derived class.\r\n * @param I The listener interface\r\n * @param E The event type\r\n * @author bininda\r\n *\/\r\npublic abstract class WeakListeners&lt;I, E&gt; {\r\n\r\n    \/**\r\n     * Weak map of references to listeners.\r\n     * WeakHashMap has the nice feature of automatically getting rid of\r\n     * stale entries.\r\n     *\/\r\n    private Set&lt;I&gt; wSet = Collections.newSetFromMap(new WeakHashMap&lt;I, Boolean&gt;());\r\n\r\n    \/**\r\n     * Create an empty weak listener list.\r\n     *\/\r\n    public WeakListeners()\r\n    {\r\n\r\n    }\r\n\r\n    \/**\r\n     * Add a new listener to the list of registered listeners.\r\n     * If the listener has already been added, the only effect is diagnostic\r\n     * output.\r\n     * @param listener The new listener\r\n     *\/\r\n    public synchronized void add (I listener)\r\n    {\r\n        wSet.add (listener);\r\n    }\r\n\r\n    \/**\r\n     * Remove a registered listener from the list.\r\n     * If the listener has already been removed, output diagnostics.\r\n     * Note that automatic removal can not happen as long as someone still has\r\n     * a hard reference to the listener and is therefore able to call this method.\r\n     * @param listener\r\n     *\/\r\n    public synchronized void remove (I listener)\r\n    {\r\n        wSet.remove (listener);\r\n    }\r\n\r\n    \/**\r\n     * Fire one event to one registered listener.\r\n     * @param listener The listener to fire to\r\n     * @param event The event to pass to the listener.\r\n     *\/\r\n    abstract protected void fireOne(I listener, E event);\r\n\r\n    \/**\r\n     * Fire an event to all registered listeners.\r\n     * If diagnostics are enabled, listeners that have been collected automatically\r\n     * are printed out.\r\n     * @param event The event to fire to the listeners.\r\n     *\/\r\n    public synchronized void fire(E event)\r\n    {\r\n        {\r\n            List&lt;I&gt; lcp = new LinkedList&lt;I&gt;(wSet);\r\n            for (I l: lcp)\r\n            {\r\n                try\r\n                {\r\n                    fireOne (l, event);\r\n                }\r\n                catch (Exception e)\r\n                {\r\n                    e.printStackTrace();\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n}<\/pre>\n<ul>\n<li><a href=\"http:\/\/bits.netbeans.org\/dev\/javadoc\/org-openide-util\/org\/openide\/util\/WeakListeners.html\">Netbeans <code>org.openide.util.WeakListeners<\/code><\/a><\/li>\n<\/ul>\n<p><!-- Put @see and @since tags down here. --><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Resource Deallocation in Java Resource deallocation in Java is no problem since Java has automatic garbage collection. Right! &#8230;? &#8230;?? &#8230;Wrong! Since Java has automatic garbage collection, resource deallocation is often taken care of for you, but when it is not, it becomes a real pain. Since the language relies so heavily on garbage collection, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,22],"tags":[33,25,16],"_links":{"self":[{"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/posts\/114"}],"collection":[{"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/comments?post=114"}],"version-history":[{"count":5,"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/posts\/114\/revisions"}],"predecessor-version":[{"id":119,"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/posts\/114\/revisions\/119"}],"wp:attachment":[{"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/media?parent=114"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/categories?post=114"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bininda.com\/blog\/wp-json\/wp\/v2\/tags?post=114"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}