Skip to main content

Enhance Listeners in Swing (Ordered, WeakReferences)

15 replies [Last post]
mgrev
Offline
Joined: 2003-08-12
Points: 0

Since there are no way of defining the relative ordering of the listeners in Swing (the order is "undefined") it is hard to "snatch" events from a component that you can't, or don't want to, subclass. You might also want to be able to "pick up" events that haven't been caught (consumed) by a component and thus would like to register yourself last in the listener list chain.

I suggest that API for defining the ordered listeners be added. If using the old API (today) the listeners would be added at level "NORMAL". Some other standard levels could easily be defined. Maybe even with the possibility to “listen to the listener list”, mostly for debugging maybe.

I know that you can never be "guaranteed" of being the first listener since there might be someone else registering a listener with an even lower order/level. This would practically not be a problem though, IMO.

This would compensate for the lack of pre- and post handling in Java, if you know what I mean.

On the same subject I would also want to add support for optionally adding the listeners wrapped in WeakReferences. This is good for defying memory leaks. I alway use this technique when I roll my own listener handling (which I almost always do, since I find the Swing one rather limited;) ).
It is important to write in the JavaDoc, in bold, that it should be used when the listeners haven't got any other strong reference holing on to it. For instance anonymous inner classes can't be added as weak references since they would go away almost immediately.

Basically add:

addXXXListener(XXXListener listener, int level, boolean asWeakReference);

A version without level must be there as well of course.

Implementing this would actually make using swing easier, since memory leaks can be avoided (for many cases anyway) and the confusing "undefined" order of listener notification can be remedied.

Since adding this to every Swing class would make a lot of code bloating, I suggest putting all logic in a standard EventHandler that is subclassable to implements all the different types of Listeners, possibly via reflection.Having a separate class that are handling all the logic would make it a snap, and without code bloat, to add thing such as listeners to the listener list and logging.

Cheers,
Mikael Grev

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
denismo
Offline
Joined: 2003-06-28
Points: 0

I agree with you. There are, indeed, the situations, when it is important to enforce some ordering among event listeners. Pre- and post-processing levels are the common examples, but there are some other useful semantic levels. We are thinking about adding the API for them.

mgrev
Offline
Joined: 2003-08-12
Points: 0

Sounds great. :D

Consider Weak references to the listener (as an option) as well. Even though not all seem to know about it, spending some quality time with one's favotite profiler finds those nice little paths between listeners, the "Observable" object and the root node.

Cheers,
Mikael Grev

mthornton
Offline
Joined: 2003-06-10
Points: 0

Instead of adding WeakReference support to the components, create a proxy object which holds the WeakReference.

class WeakActionListener extends WeakReference implements WeakReference {

public WeakActionListener(Object source, ActionListener target) {...}
}

then to use it

component.addActionListener(new WeakActionListener(component, realListener));

The WeakActionListener calls removeActionListener on the source object when its reference is lost (via reflection unfortunately).

OK, so this approach relies on the standard naming convention (or you could optionally supply the name of the remove method).

zander
Offline
Joined: 2003-06-13
Points: 0

That solution makes a lot of sense.

A widget has a reference to your proxy object and your proxy has a reference to your controller object (for callback)
The proxy object can have a reference to the widget to remove itself, but I doubt its needed.

In this scenario, when the widget should be GCed, it can be. Same with the proxy object. No weak references needed at all. So it sounds good, but why the WRs?

Which reference am I missing?

If your library requires you to use weak references so much, I recommend you take a look at the action package of UICompiler which I have been using in lots of applications and even with a profiler I can tell you it does not have these problems you describe.
http://uic.sf.net/api/20/uic/action/package-summary.html
Perhaps you should let your apps use this library (or at least its ideas) instead.

mthornton
Offline
Joined: 2003-06-10
Points: 0

I agree that most of the time weak references shouldn't be necessary for event listeners on widgets. Usually an apparent need indicates a structural problem with the application. However I am unable to exclude the possibility that it may sometimes be appropriate (perhaps if the parts you need to change are not in your control). So for those rare occasions a solution which doesn't rquire changes to either source or sink seems appropriate.

jessewilson
Offline
Joined: 2003-06-14
Points: 0

I think that the solution to this problem lies in creating a ListenerProxy.

Add only one listener to your JButton, the ListenerProxy.
Then add all the actual listeners that you want to your ListenerProxy.

The ListenerProxy can then manage all the special features that you want:
- weak references
- listener ordering

And I think there are some other special features that might be useful:
- notify listener on worker thread
- filter events

Such a proxy might even make a good candidate for a java.net project!

-Jesse
http://publicobject.com/glazedlists/

zander
Offline
Joined: 2003-06-13
Points: 0

> Such a proxy might even make a good candidate for a java.net project!

As pointed out elsewhere in this thread; the by you suggested solution is already implemented in the http://uicompiler.dev.java.net project, using a worker thread and a lot more.
Its open source and Free, please take a look.

zander
Offline
Joined: 2003-06-13
Points: 0

> Since there are no way of defining the relative
> ordering of the listeners in Swing (the order is
> "undefined") it is hard to "snatch" events from a
> component that you can't, or don't want to, subclass.

Swing provides ample opportunity to redirect native events away from the components using the focus framework. Its a whole different level of working and basically I feel its not a good idea to make 2 layers responsible for this functionality.
Why won't the focus policy work for you?

Next to that; which components can not be subclassed? I can't think of any at all. And on the "don't want to", thats not really a good argument..

> You might also want to be able to "pick up" events
> that haven't been caught (consumed) by a component
> and thus would like to register yourself last in the
> listener list chain.
Sure its possible see: Toolkit.addAWTEventListener

> I suggest that API for defining the ordered listeners
> be added.

Since you request this, I feel you have misunderstood the whole distribution of all AWTEvent objects in Swing. Not only is this not possible without a major overhoal of the focus technology and the event handling in Swing, its really not what you want anyway.

> This would compensate for the lack of pre- and post
> handling in Java, if you know what I mean.

I'm not sure I do.

> On the same subject I would also want to add support
> for optionally adding the listeners wrapped in
> WeakReferences. This is good for defying memory
> leaks.

I'm pretty sure this is not worth the effort; if you use the MVC pattern this effectively is never a problem. I can't really think of situations where the controller class dies before all its widgets do. And in the rare cases where that does happen you should design your solution to do garbage collection correctly.

> Implementing this would actually make using swing
> easier, since memory leaks can be avoided (for many
> cases anyway) and the confusing "undefined" order of
> listener notification can be remedied.
>
> Since adding this to every Swing class would make a
> lot of code bloating, I suggest putting all logic in
> a standard EventHandler that is subclassable to
> implements all the different types of Listeners,
> possibly via reflection.Having a separate class that
> are handling all the logic would make it a snap, and
> without code bloat, to add thing such as listeners to
> the listener list and logging.

I feel this is quite a low-level solution to the problems you have been seeing, and I feel that the solution you are asking for is not going to solve the problem very satisfactorily.

A better alternative is to build a framework using these events with an API that everyone understands and which considers these issues, as well as other issues mentioned in several places. The problem (for example) that events can take some time before they are processed and the data that caused the event to fire has changed already in the source widget.

I know this will work since I already use such a framework in all my projects, the framework is in the development version of the UICompiler (http://uic.sf.net/api/20/uic/action/package-summary.html) open source toolset.

mgrev
Offline
Joined: 2003-08-12
Points: 0

Ok, I'll explain. Lets say I am (which is true) writing a decorator for JComponents. For instance adding a close button (drawn with Java2D) to a JButton (stupid example, but anyway). Now in my API I just get the JComponent to add a close button to. In this context I will try to break down your comments.

> Why won't the focus policy work for you?

Focus Policy is about changing the focus cycle. I'm not interested in focus at all. I'm just interested in my JComponent that I am decorating.

> Next to that; which components can not be subclassed? I can't think of any at all. And on the "don't want to", thats not really a good argument..

As long as they don't add dynamic runtime subclassing I'm out of luck subclassing the button sent to my API.

> Sure its possible see: Toolkit.addAWTEventListener

I know of AWTEventListener and use it for some other stuff. However I'm just interested in events actually ending up at my decorated button. It is actually not possible to intercept events that [b]for sure[/b] will only end up at my monitored button. As you no doubt will say that it [b]is[/b], I will explain myself.
The user of my API might have installed an AWTEventHandler of his own, or there might be a GlassPane (with a registered MouseListener) that will be catching them first and not redispatching them for whatever reason. I can't check all those things at runtime, it would in effect be impossible. :)

> you have misunderstood the whole distribution of all AWTEvent objects in Swing.

I have been programming Swing for about five years. That doesn't mean I haven't misunderstod it, but I think I know enough to make this suggestion on a good ground.

> I'm pretty sure this is not worth the effort; (regarding WeakReferences)

It would be a very little effort. I know, I do it all the time. Besides, here are the reason: When I register myself (my long lasting decorator class) to the decorated button I create a strong reference between my class and the button. If the "owner" of the button decides that it should be destroyed, (it might be in a Dialog for instance) that button, and possibly the whole dialog, won't be garbage collected because of this single strong reference. This is probably the single most reason for memory leaks, yet very little effort (WeakReferences) can actually make those kind of leaks less of a problem. There are ways around this particular problem, but none are very "clean" and foolproof.

The only thing that needs to be enhanced is the EventListenerList (similar) implementations. They need to be ordered and optionally be able to wrap week references around the listeners. And best of all, it will not break old code!

Unless.. I have missunderstood everything; in which case I retract this proposal, and start to claim the exact opposite! ;)

Cheers,
Mikael Grev

zander
Offline
Joined: 2003-06-13
Points: 0

> Ok, I'll explain. Lets say I am (which is true)
> writing a decorator for JComponents. For instance
> adding a close button (drawn with Java2D) to a
> JButton (stupid example, but anyway). Now in my API I
> just get the JComponent to add a close button to. In
> this context I will try to break down your comments.

Ok; so you want to have an existing component have it change the functionality when part of the button is clicked to call your method instead of its own.
That is not decorating in that decorating only adds, it does not change anything in any way. You are using the wrong design pattern and are getting lost on your way to make it work.

I'll explain how I came to that (sudden) conclusion;
Any widget defines _exactly_ what happens in its bounding rect. A Button defines that if you click on it it gets pressed, a JPanel defines that it forwards the clicks to child components.
The decoration pattern (http://www.javaworld.com/javaworld/jw-04-2004/jw-0412-decorator.html) never changes behavior it only adds to it.
Saying you want the inside of the JButton to do something that it was not designed to do (in contrast to doing something where it did not respond before) means you need to extend the widget.

Whatever changes you try to make to Swing the chosen solution will never work perfectly.

The only way to make your solution work is to extend JButton and add the functionality you want in that class.

> > I'm pretty sure this is not worth the effort;
> (regarding WeakReferences)
>
> It would be a very little effort. I know, I do it all
> the time.

The effort I was talking about is not about coding effort; its about doing many more checks and creating queues to get rid of all those wearReference objects from the listener-lists, and there are a LOT in swing.
The extra complexity is one thing; the extra loops and references are the things that I point to as the obvious things that its not worth it.

> Besides, here are the reason: When I
> register myself (my long lasting decorator class) to
> the decorated button I create a strong reference
> between my class and the button. If the "owner" of
> the button decides that it should be destroyed, (it
> might be in a Dialog for instance) that button, and
> possibly the whole dialog, won't be garbage collected
> because of this single strong reference.

So, you have a decorator for one particular button, and you keep a reference to that decorator for a very long time? Thats not really a good idea to begin with and you should consider just creating a new instance of that decorator class to let it garbage collect together with the target component.
You are, naturally, aware that only objects that are referenced (indirectly or not) by a root point will be kept around. So 2 objects pointing to each other will just get GC-ed.

So, either you are worrying about nothing, or you should use more instances instead of one.

> Unless.. I have missunderstood everything; in which
> case I retract this proposal, and start to claim the
> exact opposite! ;)

As you point out in your first post; the solution you propose is not a 100% correct solution. For that reason alone I fear you will have to change so you start programming [i]with[/i] swing instead of trying to find the crack in the framework to slip through.

I hope you can fix your problem with this info!

mgrev
Offline
Joined: 2003-08-12
Points: 0

> ... You are using the wrong design pattern ...

I didn't talk about the decorator [i]pattern[/i] now did I? :)

About effort: Since I have this (ordered and WeakReferenced Listener handler) working already in my app, I [b]know[/b] it doen't take a lot of effort. I would say it took me about four hours, including testing.

> I hope you can fix your problem with this info!

What problem? I just had a solution that I use for my "Observable" classes that I find to be quite good, and suggested it be included in the SDK. That's it. I did howerver point to some possible "problems" it could solve in a, to my liking anyway, simpler way.

Cheers,
Mikael Grev

zander
Offline
Joined: 2003-06-13
Points: 0

> > ... You are using the wrong design pattern ...
>
> I didn't talk about the decorator [i]pattern[/i] now
> did I? :)

Whatever you name it; the description of the example was not a decorator but your proposed solution was.
Any question for new technology which makes the framework (Swing) harder to maintain should IMO come with a reason why the current version does not fullfill your expectations.
You failed to point out any such examples.
In other words; don't fix what aint broke and please tell is why it is broken when you ask for the API to be fixed.

> About effort: Since I have this (ordered and
> WeakReferenced Listener handler) working already in
> my app, I [b]know[/b] it doen't take a lot of effort.

I'll repeat what I said above; its not about human effort, its about memory consumption and processing power.
Its not worth the effort for a corner case or two.

mgrev
Offline
Joined: 2003-08-12
Points: 0

> its about memory consumption and processing power.

You're right, there would be a whopping 16 bytes (or so) memory consuption overhead for every time the user adds his listener as a weak reference. For 1000 weak referenced JComponents, that would be unbearable. 16k!! Yikes,
having custom proxy objects for all of those would be much better...

There would also be an instanceof extra done in every loop. That's not good performance wise. It would hog down the CPU for sure.

Yep you're right. Not a good idea. Too much resourses are spent. ;)

Now, can I interest you in some after hours reading?

http://www.onjava.com/pub/a/onjava/2004/04/28/hardcorejava.html?page=2&x...

http://www.javaworld.com/javaworld/javatips/jw-javatip79.html

http://www.ftponline.com/Archives/premier/mgznarch/javapro/1999/06jun99/...

Also make sure you read "Bitter Java chapter 6", which goes into the anti pattern with memory leak collections.
It's at: http://www.manning-source.com/books/tate/tate_ch06.pdf

zander
Offline
Joined: 2003-06-13
Points: 0

> > its about memory consumption and processing power.
>
> You're right, there would be a whopping 16 bytes (or
> so) memory consuption overhead for every time the
> user adds his listener as a weak reference. For 1000
> weak referenced JComponents, that would be
> unbearable. 16k!! Yikes,
> having custom proxy objects for all of those would be
> much better...

It would be nice if you actually knew what you were talking about before you went ahead and mocked my ideas..
First of all; all lists that store WeakReferences have to be cleaned up themselves or you'll have always growing lists. This is generally done using a queue[1]; but that means you have to have loads and loads of references from all those lists to that one thread doing the cleanup.
References like that and a new thread and the 8-byte-offset probably makes your solution more expensive.
Oh; wait; the WeakReference object itself takes just about as much space as the proxyObject itself, making the proxy object cheaper..

> Yep you're right. Not a good idea. Too much resourses
> are spent. ;)

Please do your homework so you won't make a fool of yourself in public..

1: see java.lang.ref.ReferenceQueue

mgrev
Offline
Joined: 2003-08-12
Points: 0

You argue almost like Gerard Bauer when cornered, mate. Could you please adjust your tone a bit, there is no need for hostility.

I use the Static ReferenceQueue.NULL and clean the queue on occation for add operations. (I do not use the implememtation that they use for WeakHashMap). The cleanup takes about 0.0001 milliseconds or so and happens [b]very[/b] seldom in a normal GUI application. Almost never actually. The practical (measured) memory consuption is negligable.

Don't you have any improvement ideas of your own to post in the main thread, if you are such a good coder you say you are (I have no reason to doubt it) you should have a lot to offer for the community. :)

Cheers,
Mikael Grev