Tips on developing Eclipse plugins – V.

Complex systems usually need some kind of asynchronous messaging and caching for better performance. And yes, that’s pretty much what this article is about. Let’s dive into the following issues:

  • Custom event listeners
  • Soft reference cache
  • Preventing memory leaks when using cached objects & listeners

Creating and using custom event listeners

Listeners are great when it comes to the communication of objects that should stay decoupled to a high degree. They are used just about everywhere (e.g. SWT) and sometimes it would be nice to add custom listeners to our objects. And that’s showtime for ListenerList class from org.eclipse.core.runtime package. It is fully thread-safe list designed for storing lists of listeners (optimized for frequent reads and infrequent writes).

Typical custom class would contain something like:

[java]
public class ClassWithListeners {
private ListenerList listeners;

public void addMyEventListener(IMyListener listener) {
listeners.add(listener);
}

public void removeMyEventListener(IMyListener listener) {
listeners.remove(listener);
}

private void fireMyEvent(IFile[] files) {
Object[] listeners = listeners.getListeners();
for (int i = 0; i < listeners.length; i++) ((IMyListener) listeners[i]).myEventFired( /* some data */ ); } } [/java]

With IMyListener being:

[java]
public interface IMyListener {
void myEventFired(/* some data */);
}
[/java]

So when our event happens in our class ClassWithListeners we notify all listeners (that have been previously registered by addMyEventListener()) by calling fireMyEvent().

Its always good practice to unregister by calling removeMyEventListener() when we are no longer interested in the event or when the owning objects are going to be disposed. To see how to achieve this automatically read on.

Creating soft reference cache

Common scenario is to have some objects that are very expensive (time consuming) to create but once created they can be used many times as they don’t change (or at least not too often).

Such objects are ideal candidates for some kind of caching. Naive approach is to create these objects at the start of program and keep them in memory until the end of the program. It sure works but the price of slower program startup and bigger memory footprint may be too high.

First improvement would be lazy initialization (do not create object until first needed). Second and here described improvement is soft reference caching whose main advantage is that cached objects can be garbage-collected when needed (to release memory for other objects) and recreated when they are to be used again.

The simplest cache might look like this:

[java]
Map cache =
new HashMap();

public synchronized ExpensiveObject getCached(String key) {
if (cache.get(key) != null && cache.get(key).get() != null) {
return cache.get(key).get();
} else {
//object was never cached or has been
//garbage-collected so create it again
cache.put(key, new ExpensiveObject());
}
}
[/java]

Of course, there is room for improvements (generics, cache entries invalidation etc.). Without much trouble a whole generic cache can be written.

Preventing memory leaks when using cached objects & listeners

If we combine both above described steps and cache objects that register some listeners to other objects we might end up having memory leaks in our application. Sample scenario:

[java]
//implemented like ClassWithListeners
ObjectThatLivesTheWholeApplicationLife object =
new ObjectThatLivesTheWholeApplicationLife();

SoftReferenceCache.put(“CachedObject”, new ExpensiveObject(object));

class ExpensiveObject {
public ExpensiveObject(ObjectThatLivesTheWholeApplicationLife object) {
object.addMyEventListener(new IMyListener() {
public void myEventFired() {
//we use reference to ExpensiveObject.this here
}
});
}
}
[/java]

The problem with this scenario is that ExpensiveObject will never be garbage-collected even if memory runs low. That’s because ObjectThatLivesTheWholeApplicationLife holds instance of IMyListener that holds strong (non garbage-collectable) reference to an instance of ExpensiveObject.

Possible way to solve it is to wrap IMyListener into weak reference (which is garbage-collectable if there are no other strong references to the object). Moreover the wrapper will automatically unregister event handler if its owner (ExpensiveObject) is disposed.

Such wrapper could look like this:

[java]
class MyListenerWeakWrapper implements IMyListener {
private WeakReference listenerRef;
private ObjectThatLivesTheWholeApplicationLife eventSource;

public MyListenerWeakWrapper(IMyListenerlistener,
ObjectThatLivesTheWholeApplicationLife eventSource) {
listenerRef = new WeakReference(listener);
this.eventSource = eventSource;
}

public void myEventFired() {
IMyListener listener = listenerRef.get();

if (listener == null) {
eventSource.removeMyEventListener(listener);
} else {
listener.fired();
}
}
}
[/java]

And the usage could be either on the side of ExpensiveObject:

[java]
class ExpensiveObject {
public ExpensiveObject(final ObjectThatLivesTheWholeApplicationLife object) {
object.addMyEventListener(MyListenerWeakWrapper(new IMyListener() {
public void myEventFired() {
//…
}
}, object));
}
}
[/java]

or on the side of ObjectThatLivesTheWholeApplicationLife:

[java]
//…
public void addMyEventListener(IMyListener listener) {
listeners.add(new MyListenerWeakWrapper(listener, this));
}
//…
[/java]

Tags: , , , , ,

One Response to “Tips on developing Eclipse plugins – V.”

  1. […] bookmarks tagged eclipse Tips on developing Eclipse plugins – V. saved by 1 others     SSJDarkFlare bookmarked on 01/22/08 | […]