PluggableEventList implementation - first attempt at supporting binding

View: New views
20 Messages — Rating Filter:   Alert me  
< Prev | 1 - 2 | Next >

PluggableEventList implementation - first attempt at supporting binding

by Kevin Day :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi all-

Awhile back James suggested an approach for implementing a TransformedEventList that allows you to replace it's source list.  This type of thing is needed for support of binding libraries where the source list often comes from a bean selected in the user interface.  This kind of implementation is tricky b/c the PluggableEventList and the source list will often have different locks and notification chains.

I've put together a first cut at the implementation, and I was wondering if I could get feedback on the implementation.  I  am especially interested in feedback on what I've done with the locks.  Also, does the name PluggableEventList make intuitive sense?

I'm attaching both the source and unit test for your review and comment (I hope attachments go through on the listserv).

Thanks in advance,

- K


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...

PluggableEventListTest.java (3K) Download Attachment
PluggableEventList.java (7K) Download Attachment

Re: PluggableEventList implementation - first attempt at supporting binding

by Ken Orr :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Kevin,

Could you describe a concrete usage scenario for this class? I need a little more context to understand whats going on!

Thanks,
-Ken

Kevin Day wrote:
Hi all-

Awhile back James suggested an approach for implementing a TransformedEventList that allows you to replace it's source list.  This type of thing is needed for support of binding libraries where the source list often comes from a bean selected in the user interface.  This kind of implementation is tricky b/c the PluggableEventList and the source list will often have different locks and notification chains.

I've put together a first cut at the implementation, and I was wondering if I could get feedback on the implementation.  I  am especially interested in feedback on what I've done with the locks.  Also, does the name PluggableEventList make intuitive sense?

I'm attaching both the source and unit test for your review and comment (I hope attachments go through on the listserv).

Thanks in advance,

- K
 
 
---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@glazedlists.dev.java.net
For additional commands, e-mail: users-help@glazedlists.dev.java.net

re[2]: PluggableEventList implementation - first attempt at supporting binding

by Kevin Day :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Absolutely.  Here's the background that led to this development:


Let's say that you have some GL list(s) in your model:

BasicEventList modelList1;


You also have a GL chain that is tied explicitly to your GUI:

<some input list> -> SortedList -> FilterList -> EventTableModel



In the regular world of GL, when you construct your GUI, you would just take the BasicEventList from your model, and use it while creating your GL chain in the GUI - as follows:

modelList1 -> SortedList -> FilterList -> EventTableModel

The issue with this is that it ties the GUI to a specific instance of the model.  Swapping the model out involves literally re-constructing the GL chain that is used by the GUI, then swapping the JTable's table model out.  So if you wanted your GUI to display modelList2 instead of modelList1, you'd have to reconstruct the entire chain as follows:

modelList2 -> SortedList -> FilterList -> EventTableModel


that's a lot of code that has to be captured in an event handler (or multiple event handlers).  Especially for an operation that is really in the domain of data->gui binding.




What the new class attempts to do is to make it so that you can leave a given GL chain in place, and just swap out the source model.  So, at application startup, you create:

modelList1 -> PluggableEventList -> SortedList -> FilterList -> EventTableModel

then at a later time, you need to switch the GUI so it displays modelList2, you just call PluggableEventList#setSource(modelList2), and the chain becomes:

modelList2 -> PluggableEventList -> SortedList -> FilterList -> EventTableModel



Now, it may seem that I haven't saved any time here, because we still have to call setSource in an event handler somewhere - but when you introduce a bindings framework, you can just bind the currently selected modelList into the PluggableEventList's Source property, and the binding framework takes care of everything else.  Instead of having to explicitly code event handlers, you set up the GUI, and say that it should always display the appropriate table for the current 'selected' modelList.





To give a more concrete example, the exact GUI layout in our app is:

JTree on the left
JTable on the right

Each entry in the tree resembles a set of data.  As you select different entries in the tree, the table on the right should reflect the data set of the selected entry.

Object architecture involves the selected node in the JTree exposing a get/setDataSet() property method.  This property is backed by a GlazedList.

The JTable on the right uses a GL chain to provide sorting/filtering/etc... on the list as it is displayed to the user (basically, GUI type operations).

PluggableEventList -> SortedList -> FilterList -> EventTableModel

We are using a bindings framework (JGoodies bindings), so we need to be able to bind the GlazedList from the getDataSet() property of the selected element in the JTree to the input list of the GL chain that is used by the GUI.  We do this with a single bind operation during GUI setup:

Bind(PluggableEventList#Source, JTree#SelectedItem#dataList)   (this is pseudo code, but it gives the idea)




In the current GL architecture, it is not possible to replace the input list of a given GL list chain.  To get the desired functionality, I would have to explicitly listen for selection change events on the JTree, destroy the GL list chain currently backing the JTable, then build an entirely new GL list chain to back the JTable.

This forces us out of a 'binding and action' development style, and into an 'event handling' development style.  Over the years, we have found that GUI based apps are significantly easier to extend and maintain when they are developed without explicit event handlers (thus the big push for binding solutions!).


In short, the PluggableEventList allows the developer to swap out the underlying list for any GL chain, so it becomes possible to bind the JTable declaratively.  I also have an implementation of a class called IndirectEventList that subclasses PluggableEventList to allow the 'source' to be specified as a JGoodies bound property, instead of an explicit list.  I'm attaching that class and unit tests for those of you who are familiar with JGoodies Bindings (these, obviously, depend on the having the JGoodies Bindings jar in your classpath).  The unit test demonstrates how a source list is bound from a JavaBean property into the list chain, and how updating the value in the Bean causes the GL chain to automatically reflect the new list.


Please let me know your thoughts,

- K


----------------------- Original Message -----------------------
 
From: Ken Orr <ken.orr@...>
To: users@...
Cc:
Date: Mon, 28 Apr 2008 04:49:59 -0700 (PDT)
Subject: Re: PluggableEventList implementation - first attempt at supporting binding
 

Hi Kevin,

Could you describe a concrete usage scenario for this class? I need a little
more context to understand whats going on!

Thanks,
-Ken


Kevin Day wrote:

>
> Hi all-
>
> Awhile back James suggested an approach for implementing a
> TransformedEventList that allows you to replace it's source list.  This
> type of thing is needed for support of binding libraries where the source
> list often comes from a bean selected in the user interface.  This kind of
> implementation is tricky b/c the PluggableEventList and the source list
> will often have different locks and notification chains.
>
> I've put together a first cut at the implementation, and I was wondering
> if I could get feedback on the implementation.  I  am especially
> interested in feedback on what I've done with the locks.  Also, does the
> name PluggableEventList make intuitive sense?
>
> I'm attaching both the source and unit test for your review and comment (I
> hope attachments go through on the listserv).
>
> Thanks in advance,
>
> - K
>  
>  
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@...
> For additional commands, e-mail: users-help@...
>
--
View this message in context: http://www.nabble.com/PluggableEventList-implementation---first-attempt-at-supporting-binding-tp16883071p16937347.html
Sent from the GlazedLists - User mailing list archive at Nabble.com.


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...

IndirectEventListTest.java (4K) Download Attachment
IndirectEventList.java (3K) Download Attachment

Re: PluggableEventList implementation - first attempt at supporting binding

by Gerrit Cap :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hello Kevin,


For a project we have been using glazed lists intensively and also in combination with the jgoodies binding library and thus had the same need as you to be able to switch one eventlist as a value of a model property to another value (meaning another eventlist).

I guess that our original version quite looked very similar to your version and looking at your version I noticed a couple of problems all involving multi threading and several threads accessing and modifying lists. I will post our version of the pluggable event list soon as I have to remove some integration stuff with jgoodies binding to have our "clean" pluggable eventlist that compiles using only the glazed list code as base.

Gerrit.

Kevin Day wrote:
Hi all-

Awhile back James suggested an approach for implementing a TransformedEventList that allows you to replace it's source list.  This type of thing is needed for support of binding libraries where the source list often comes from a bean selected in the user interface.  This kind of implementation is tricky b/c the PluggableEventList and the source list will often have different locks and notification chains.

I've put together a first cut at the implementation, and I was wondering if I could get feedback on the implementation.  I  am especially interested in feedback on what I've done with the locks.  Also, does the name PluggableEventList make intuitive sense?

I'm attaching both the source and unit test for your review and comment (I hope attachments go through on the listserv).

Thanks in advance,

- K

--------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscribe@... For additional commands, e-mail: users-help@...

-- 
-------------------- Marble Consulting ----------------------
Gerrit Cap                      http://www.marble.be/
OO Solutions Engineer           Gerrit.Cap@...
Marble Consulting               gsm : +32 475 72.94.36
Vinkenbosstraat 13              tel : +32 16 89.50.55
B-3001 Heverlee                 fax : +32 16 89.50.58    

re[2]: PluggableEventList implementation - first attempt at supporting binding

by Kevin Day :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Gerrit-

I look forward to your post - especially in the locking areas (that was the one area that I was the most concerned with, as I'm very new to the GL code base, and just getting my head around the basics of their locking and event notification models).

If it makes it easier for you, I'd be happy to take what you have and split out the Pluggable form of it from the Indirect form (I think this provides a nice separation of logic - the Pluggable version would be a good addition to the GL library, and the Indirect version would be a good addition to the JGoodies library).

Thanks much,

- K


----------------------- Original Message -----------------------
 
From: Gerrit Cap <Gerrit.Cap@...>
To: users@...
Cc:
Date: Mon, 05 May 2008 11:06:56 +0200
Subject: Re: PluggableEventList implementation - first attempt at supporting binding
 
Hello Kevin,


For a project we have been using glazed lists intensively and also in combination with the jgoodies binding library and thus had the same need as you to be able to switch one eventlist as a value of a model property to another value (meaning another eventlist).

I guess that our original version quite looked very similar to your version and looking at your version I noticed a couple of problems all involving multi threading and several threads accessing and modifying lists. I will post our version of the pluggable event list soon as I have to remove some integration stuff with jgoodies binding to have our "clean" pluggable eventlist that compiles using only the glazed list code as base.

Gerrit.

Kevin Day wrote:
Hi all-

Awhile back James suggested an approach for implementing a TransformedEventList that allows you to replace it's source list.  This type of thing is needed for support of binding libraries where the source list often comes from a bean selected in the user interface.  This kind of implementation is tricky b/c the PluggableEventList and the source list will often have different locks and notification chains.

I've put together a first cut at the implementation, and I was wondering if I could get feedback on the implementation.  I  am especially interested in feedback on what I've done with the locks.  Also, does the name PluggableEventList make intuitive sense?

I'm attaching both the source and unit test for your review and comment (I hope attachments go through on the listserv).

Thanks in advance,

- K

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


--
-------------------- Marble Consulting ----------------------
Gerrit Cap                      http://www.marble.be/
OO Solutions Engineer           mailto:Gerrit.Cap@...
Marble Consulting               gsm : +32 475 72.94.36
Vinkenbosstraat 13              tel : +32 16 89.50.55
B-3001 Heverlee                 fax : +32 16 89.50.58    

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


RE: re[2]: PluggableEventList implementation - first attempt at supporting binding

by gerrit.cap :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hello Kevin,

Unfortunately the mail server here doesn't allow .java extensions so I just included the source code here. Just take your scissors and go and play with the new toy.

The original source we use is tested, but as I said I had to modify it a bit as
        a) it had internal time statistics so that stuff got away
        b) internally we rely on locks with a timeout to get some kind of feedback when deadlocks occur
        c) it used to implement propertychangelistener with the bindings stuff so the propertychanged method is now replaced by setMember
as a result: this code is untested.

A good unit test would be if a bunch of threads would be created that in parallel try to read from this list, write to the list and replace the member.

Of course in good tradition with the real glazedlists this class is thread-safe-ready. Meaning, we tried to make its internal workings thread safe byt as a user of this class you are still responsible to acquire a readlock when reading from it (e.g. requesting the size or requesting an iterator and looping over that iterator) or a writelock when writing to the list.

Gerrit.

------------ Cut Here ---------------
package glazedlists.ext;

import java.util.Collection;
import java.util.Random;

import ca.odell.glazedlists.AbstractEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.util.concurrent.Lock;
import ca.odell.glazedlists.util.concurrent.LockFactory;
import ca.odell.glazedlists.util.concurrent.ReadWriteLock;

/**
 * The PluggableList allows the member list to be changed to another list.
 *
 */
public class PluggableList<E> extends AbstractEventList<E> implements ListEventListener<E> {

        private EventList<E> member = null;

        private final ReadWriteLock myLock = LockFactory.DEFAULT.createReadWriteLock();

        public PluggableList(EventList<E> member) {
                super();
                this.member = member;
                this.readWriteLock = new PluggableListReadWriteLock();
                if (member != null) {
                        this.member.addListEventListener(this);
                }
        }

        public void setMember(EventList<E> member) {
                EventList<E> newMember = null;
                EventList<E> oldMember = null;
                this.myLock.writeLock().lock();
                try {
                        oldMember = this.member;
                        newMember = member;
                        if (newMember == oldMember) {
                                return;
                        }
                        if (oldMember != null) {
                                oldMember.getReadWriteLock().readLock().lock();
                        }
                        if (newMember != null) {
                                newMember.getReadWriteLock().readLock().lock();
                        }
                        this.member = newMember;
                        // The order of addListener and removeListener doesn't matter
                        // because of the readLocks we have acquired.
                        if (oldMember != null) {
                                oldMember.removeListEventListener(this);
                        }
                        if (newMember != null) {
                                newMember.addListEventListener(this);
                        }
                        if ((oldMember != null) || (newMember != null)) {
                                this.updates.beginEvent();
                                int oldSize = oldMember == null ? 0 : oldMember.size();
                                int newSize = newMember == null ? 0 : newMember.size();
                                int minimumSize = Math.min(oldSize, newSize);
                                if (minimumSize > 0) {
                                        this.updates.addUpdate(0, minimumSize - 1);
                                }
                                if (oldSize > newSize) {
                                        this.updates.addDelete(minimumSize, oldSize - 1);
                                } else if (newSize > oldSize) {
                                        this.updates.addInsert(minimumSize, newSize - 1);
                                }
                                this.updates.commitEvent();
                        }
                } finally {
                        if (newMember != null) {
                                newMember.getReadWriteLock().readLock().unlock();
                        }
                        if (oldMember != null) {
                                oldMember.getReadWriteLock().readLock().unlock();
                        }
                        this.myLock.writeLock().unlock();
                }
        }

        public void listChanged(ListEvent<E> listChanges) {
                this.updates.forwardEvent(listChanges);
        }

        protected boolean isWritable() {
                return true;
        }

        // Delegate all modification requests to our unique member
        public void add(int index, E value) {
                this.member.add(index, value);
        }

        public boolean add(E value) {
                return this.member.add(value);
        }

        public boolean addAll(Collection<? extends E> values) {
                return this.member.addAll(values);
        }

        public boolean addAll(int index, Collection<? extends E> values) {
                return this.member.addAll(index, values);
        }

        public E remove(int index) {
                return this.member.remove(index);
        }

        public boolean remove(Object toRemove) {
                return this.member.remove(toRemove);
        }

        public boolean removeAll(Collection<?> collection) {
                return this.member.removeAll(collection);
        }

        public boolean retainAll(Collection<?> values) {
                return this.member.retainAll(values);
        }

        public E set(int index, E value) {
                return this.member.set(index, value);
        }

        public int size() {
                if (this.member != null) {
                        return this.member.size();
                }
                return 0;
        }

        public E get(int index) {
                return this.member.get(index);
        }

        /**
         * Releases the resources consumed by this {@link TransformedList} so that it
         * may eventually be garbage collected.
         *
         * <p>A {@link TransformedList} will be garbage collected without a call to
         * {@link #dispose()}, but not before its source {@link EventList} is garbage
         * collected. By calling {@link #dispose()}, you allow the {@link TransformedList}
         * to be garbage collected before its source {@link EventList}. This is
         * necessary for situations where a {@link TransformedList} is short-lived but
         * its source {@link EventList} is long-lived.
         *
         * <p><strong><font color="#FF0000">Warning:</font></strong> It is an error
         * to call any method on a {@link TransformedList} after it has been disposed.
         */
        public void dispose() {
                if (this.member != null) {
                        this.member.removeListEventListener(this);
                }
        }

        private class PluggableListReadWriteLock implements ReadWriteLock {

                Lock readLock;

                Lock writeLock;

                public PluggableListReadWriteLock() {
                        this.readLock = new PluggableListReadLock();
                        this.writeLock = new PluggableListWriteLock();
                }

                public Lock readLock() {
                        return this.readLock;
                }

                public Lock writeLock() {
                        return this.writeLock;
                }
        }

        private abstract class PluggableListAbstractLock implements Lock {

                private final Random randomSleep = new Random();

                private final Lock lock;

                public PluggableListAbstractLock(Lock lock) {
                        this.lock = lock;
                }

                public void lock() {
                        boolean acquired = false;
                        long stopTime = 0;
                        while (!acquired) {
                                this.lock.lock();
                                if (PluggableList.this.member != null) {
                                        acquired = this.getLock().tryLock(); //consider try lock with timeout
                                        if (!acquired) {
                                                this.lock.unlock();
                                                try {
                                                        Thread.sleep(5 + this.randomSleep.nextInt(10));
                                                } catch (InterruptedException e) {
                                                        // ignored
                                                }
                                        }
                                } else {
                                        acquired = true;
                                }
                        }
                }

                public boolean tryLock() {
                        boolean success = this.lock.tryLock();
                        if (success) {
                                if (PluggableList.this.member != null) {
                                        success = this.getLock().tryLock();
                                        if (!success) {
                                                this.lock.unlock();
                                        }
                                }
                        }
                        return success;
                }

                public void unlock() {
                        if (PluggableList.this.member != null) {
                                this.getLock().unlock();
                        }
                        this.lock.unlock();
                }

                public abstract Lock getLock();
        }

        private class PluggableListReadLock extends PluggableListAbstractLock {

                public PluggableListReadLock() {
                        super(PluggableList.this.myLock.readLock());
                }

                public Lock getLock() {
                        return PluggableList.this.member.getReadWriteLock().readLock();
                }
        }

        private class PluggableListWriteLock extends PluggableListAbstractLock {

                public PluggableListWriteLock() {
                        super(PluggableList.this.myLock.writeLock());
                }

                public Lock getLock() {
                        return PluggableList.this.member.getReadWriteLock().writeLock();
                }
        }

}
------------ Cut Here ---------------

-----Original Message-----
From: Kevin Day [mailto:kevin@...]
Sent: Monday, May 05, 2008 6:45 PM
To: Glazed Lists
Subject: re[2]: PluggableEventList implementation - first attempt at supporting binding

Gerrit-

I look forward to your post - especially in the locking areas (that was the one area that I was the most concerned with, as I'm very new to the GL code base, and just getting my head around the basics of their locking and event notification models).

If it makes it easier for you, I'd be happy to take what you have and split out the Pluggable form of it from the Indirect form (I think this provides a nice separation of logic - the Pluggable version would be a good addition to the GL library, and the Indirect version would be a good addition to the JGoodies library).

Thanks much,

- K


----------------------- Original Message -----------------------
 
From: Gerrit Cap <Gerrit.Cap@...>
To: users@...
Cc:
Date: Mon, 05 May 2008 11:06:56 +0200
Subject: Re: PluggableEventList implementation - first attempt at supporting binding
 
Hello Kevin,


For a project we have been using glazed lists intensively and also in combination with the jgoodies binding library and thus had the same need as you to be able to switch one eventlist as a value of a model property to another value (meaning another eventlist).

I guess that our original version quite looked very similar to your version and looking at your version I noticed a couple of problems all involving multi threading and several threads accessing and modifying lists. I will post our version of the pluggable event list soon as I have to remove some integration stuff with jgoodies binding to have our "clean" pluggable eventlist that compiles using only the glazed list code as base.

Gerrit.

Kevin Day wrote:
Hi all-

Awhile back James suggested an approach for implementing a TransformedEventList that allows you to replace it's source list.  This type of thing is needed for support of binding libraries where the source list often comes from a bean selected in the user interface.  This kind of implementation is tricky b/c the PluggableEventList and the source list will often have different locks and notification chains.

I've put together a first cut at the implementation, and I was wondering if I could get feedback on the implementation.  I  am especially interested in feedback on what I've done with the locks.  Also, does the name PluggableEventList make intuitive sense?

I'm attaching both the source and unit test for your review and comment (I hope attachments go through on the listserv).

Thanks in advance,

- K

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


--
-------------------- Marble Consulting ----------------------
Gerrit Cap                      http://www.marble.be/
OO Solutions Engineer           mailto:Gerrit.Cap@...
Marble Consulting               gsm : +32 475 72.94.36
Vinkenbosstraat 13              tel : +32 16 89.50.55
B-3001 Heverlee                 fax : +32 16 89.50.58    

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...



= = = = = = = = = = = = = = = = = = = = = = = = =
Fortis disclaimer :
http://www.fortis.be/legal/disclaimer.htm

Privacy policy related to banking activities of Fortis:
http://www.fortis.be/legal/privacy_policy.htm
= = = = = = = = = = = = = = = = = = = = = = = = =




---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


re[2]: re[2]: PluggableEventList implementation - first attempt at supporting binding

by Kevin Day :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Perhaps Jesse can provide some insight on my question about ListEventPublishers below (Jesse - I'm the tall bald guy you met at Java One - here's the discussion thread that I mentioned after the GL presentation).

Gerrit-

Sorry for taking so long to respond - Java One, kids sick, etc...

In reviewing the code, I have one question/comment:  One of the hard requirements on EventLists in a pipeline is that they share the same ListEventPublisher and the same lock.  You have handled the lock already by using a well defined lock sequence on both the list and the member.  But I think the ListEventPublisher requirement may have been overlooked.

The implementation below uses the AbstractEventList default constructor, which uses the 'default' ListEventPublisher.  This will work, but only if all EventLists are generated using that default publisher.  In a self contained app, this may very well be the case - but in situations where the app may be connecting to remote EventLists (or lists that require their own publisher for some reason), I'm pretty sure the updates will not propogate properly.  I could easily see this happening, for example, if different class loaders were used for the source vs destination list (like in an IOC or OSGi container)

I think that this was the reason that James suggested the alternative approach of syncing an internally captured source list with the member list.  This effectively bridges the two Publishers.  This should work, but it feels like there should be a more elegant solution.

I think another strategy would be to have some sort of proxying/bridging publisher implementation that would silently bridge the publishers (and allow swapping out of the member publisher).  As near as I can tell, doing this kind of proxying would involve providing the proxying publisher to the constructor of the AbstractEventList and update it's state in the setMember() method if the new member has a different publisher.  I'm not sure if this violates any implied contract of the ListEventPublisher implementation.  I'm thinking that it may be safe to do because we only forward the events (by the time we hit the forwarder the downstream dependency graph will have worked itself out).  If events are going to travel downstream, we'd also need to proxy in the other direction, in which case we'd need to either fire events during addX() calls, or possibly add a listener to the PluggableEventList itself to forward events downstream.  I think that it would be necessary to remove/disable that listener during the event forwarding in listChanged() unless the functionality of the ListEventPublisher prevents the infinte loop of notifications that would result.


Like I mentioned at the top of the email, maybe Jesse can shed some light on the feasability of this approach.  If he can't think of any reason for not doing this, I can put together an implementation.



Having this capability may also allow GL to remove the 'same publisher' requirement from GroupingList (not sure on that - GroupingList is muxing things, so bridging publishers may not work in this case).

Regardless, getting this working will be huge for IOC and bindings based implementations.

I'm heading out on travel - will respond to any thoughts/comments in 2 weeks.  Please let me know what you think of the above.

- K

Kevin Day
Trumpet, Inc.
www.trumpetinc.com


----------------------- Original Message -----------------------
 
From: <gerrit.cap@...>
To: <users@...>
Cc:
Date: Tue, 6 May 2008 10:30:40 +0200
Subject: RE:  re[2]: PluggableEventList implementation - first attempt at supporting binding
 
Hello Kevin,

Unfortunately the mail server here doesn't allow .java extensions so I just included the source code here. Just take your scissors and go and play with the new toy.

The original source we use is tested, but as I said I had to modify it a bit as
   a) it had internal time statistics so that stuff got away
   b) internally we rely on locks with a timeout to get some kind of feedback when deadlocks occur
   c) it used to implement propertychangelistener with the bindings stuff so the propertychanged method is now replaced by setMember
as a result: this code is untested.

A good unit test would be if a bunch of threads would be created that in parallel try to read from this list, write to the list and replace the member.

Of course in good tradition with the real glazedlists this class is thread-safe-ready. Meaning, we tried to make its internal workings thread safe byt as a user of this class you are still responsible to acquire a readlock when reading from it (e.g. requesting the size or requesting an iterator and looping over that iterator) or a writelock when writing to the list.

Gerrit.

------------ Cut Here ---------------
package glazedlists.ext;

import java.util.Collection;
import java.util.Random;

import ca.odell.glazedlists.AbstractEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import ca.odell.glazedlists.util.concurrent.Lock;
import ca.odell.glazedlists.util.concurrent.LockFactory;
import ca.odell.glazedlists.util.concurrent.ReadWriteLock;

/**
* The PluggableList allows the member list to be changed to another list.
*
*/
public class PluggableList<E> extends AbstractEventList<E> implements ListEventListener<E> {

   private EventList<E> member = null;

   private final ReadWriteLock myLock = LockFactory.DEFAULT.createReadWriteLock();

   public PluggableList(EventList<E> member) {
       super();
       this.member = member;
       this.readWriteLock = new PluggableListReadWriteLock();
       if (member != null) {
           this.member.addListEventListener(this);
       }
   }

   public void setMember(EventList<E> member) {
       EventList<E> newMember = null;
       EventList<E> oldMember = null;
       this.myLock.writeLock().lock();
       try {
           oldMember = this.member;
           newMember = member;
           if (newMember == oldMember) {
               return;
           }
           if (oldMember != null) {
               oldMember.getReadWriteLock().readLock().lock();
           }
           if (newMember != null) {
               newMember.getReadWriteLock().readLock().lock();
           }
           this.member = newMember;
           // The order of addListener and removeListener doesn't matter
           // because of the readLocks we have acquired.
           if (oldMember != null) {
               oldMember.removeListEventListener(this);
           }
           if (newMember != null) {
               newMember.addListEventListener(this);
           }
           if ((oldMember != null) || (newMember != null)) {
               this.updates.beginEvent();
               int oldSize = oldMember == null ? 0 : oldMember.size();
               int newSize = newMember == null ? 0 : newMember.size();
               int minimumSize = Math.min(oldSize, newSize);
               if (minimumSize > 0) {
                   this.updates.addUpdate(0, minimumSize - 1);
               }
               if (oldSize > newSize) {
                   this.updates.addDelete(minimumSize, oldSize - 1);
               } else if (newSize > oldSize) {
                   this.updates.addInsert(minimumSize, newSize - 1);
               }
               this.updates.commitEvent();
           }
       } finally {
           if (newMember != null) {
               newMember.getReadWriteLock().readLock().unlock();
           }
           if (oldMember != null) {
               oldMember.getReadWriteLock().readLock().unlock();
           }
           this.myLock.writeLock().unlock();
       }
   }

   public void listChanged(ListEvent<E> listChanges) {
       this.updates.forwardEvent(listChanges);
   }

   protected boolean isWritable() {
       return true;
   }

   // Delegate all modification requests to our unique member
   public void add(int index, E value) {
       this.member.add(index, value);
   }

   public boolean add(E value) {
       return this.member.add(value);
   }

   public boolean addAll(Collection<? extends E> values) {
       return this.member.addAll(values);
   }

   public boolean addAll(int index, Collection<? extends E> values) {
       return this.member.addAll(index, values);
   }

   public E remove(int index) {
       return this.member.remove(index);
   }

   public boolean remove(Object toRemove) {
       return this.member.remove(toRemove);
   }

   public boolean removeAll(Collection<?> collection) {
       return this.member.removeAll(collection);
   }

   public boolean retainAll(Collection<?> values) {
       return this.member.retainAll(values);
   }

   public E set(int index, E value) {
       return this.member.set(index, value);
   }

   public int size() {
       if (this.member != null) {
           return this.member.size();
       }
       return 0;
   }

   public E get(int index) {
       return this.member.get(index);
   }

   /**
    * Releases the resources consumed by this {@link TransformedList} so that it
    * may eventually be garbage collected.
    *
    * <p>A {@link TransformedList} will be garbage collected without a call to
    * {@link #dispose()}, but not before its source {@link EventList} is garbage
    * collected. By calling {@link #dispose()}, you allow the {@link TransformedList}
    * to be garbage collected before its source {@link EventList}. This is
    * necessary for situations where a {@link TransformedList} is short-lived but
    * its source {@link EventList} is long-lived.
    *
    * <p><strong><font color="#FF0000">Warning:</font></strong> It is an error
    * to call any method on a {@link TransformedList} after it has been disposed.
    */
   public void dispose() {
       if (this.member != null) {
           this.member.removeListEventListener(this);
       }
   }

   private class PluggableListReadWriteLock implements ReadWriteLock {

       Lock readLock;

       Lock writeLock;

       public PluggableListReadWriteLock() {
           this.readLock = new PluggableListReadLock();
           this.writeLock = new PluggableListWriteLock();
       }

       public Lock readLock() {
           return this.readLock;
       }

       public Lock writeLock() {
           return this.writeLock;
       }
   }

   private abstract class PluggableListAbstractLock implements Lock {

       private final Random randomSleep = new Random();

       private final Lock lock;

       public PluggableListAbstractLock(Lock lock) {
           this.lock = lock;
       }

       public void lock() {
           boolean acquired = false;
           long stopTime = 0;
           while (!acquired) {
               this.lock.lock();
               if (PluggableList.this.member != null) {
                   acquired = this.getLock().tryLock(); //consider try lock with timeout
                   if (!acquired) {
                       this.lock.unlock();
                       try {
                           Thread.sleep(5 + this.randomSleep.nextInt(10));
                       } catch (InterruptedException e) {
                           // ignored
                       }
                   }
               } else {
                   acquired = true;
               }
           }
       }

       public boolean tryLock() {
           boolean success = this.lock.tryLock();
           if (success) {
               if (PluggableList.this.member != null) {
                   success = this.getLock().tryLock();
                   if (!success) {
                       this.lock.unlock();
                   }
               }
           }
           return success;
       }

       public void unlock() {
           if (PluggableList.this.member != null) {
               this.getLock().unlock();
           }
           this.lock.unlock();
       }

       public abstract Lock getLock();
   }

   private class PluggableListReadLock extends PluggableListAbstractLock {

       public PluggableListReadLock() {
           super(PluggableList.this.myLock.readLock());
       }

       public Lock getLock() {
           return PluggableList.this.member.getReadWriteLock().readLock();
       }
   }

   private class PluggableListWriteLock extends PluggableListAbstractLock {

       public PluggableListWriteLock() {
           super(PluggableList.this.myLock.writeLock());
       }

       public Lock getLock() {
           return PluggableList.this.member.getReadWriteLock().writeLock();
       }
   }

}
------------ Cut Here ---------------

-----Original Message-----
From: Kevin Day [mailto:kevin@...]
Sent: Monday, May 05, 2008 6:45 PM
To: Glazed Lists
Subject: re[2]: PluggableEventList implementation - first attempt at supporting binding

Gerrit-

I look forward to your post - especially in the locking areas (that was the one area that I was the most concerned with, as I'm very new to the GL code base, and just getting my head around the basics of their locking and event notification models).

If it makes it easier for you, I'd be happy to take what you have and split out the Pluggable form of it from the Indirect form (I think this provides a nice separation of logic - the Pluggable version would be a good addition to the GL library, and the Indirect version would be a good addition to the JGoodies library).

Thanks much,

- K


----------------------- Original Message -----------------------

From: Gerrit Cap <Gerrit.Cap@...>
To: users@...
Cc:
Date: Mon, 05 May 2008 11:06:56 +0200
Subject: Re: PluggableEventList implementation - first attempt at supporting binding

Hello Kevin,


For a project we have been using glazed lists intensively and also in combination with the jgoodies binding library and thus had the same need as you to be able to switch one eventlist as a value of a model property to another value (meaning another eventlist).

I guess that our original version quite looked very similar to your version and looking at your version I noticed a couple of problems all involving multi threading and several threads accessing and modifying lists. I will post our version of the pluggable event list soon as I have to remove some integration stuff with jgoodies binding to have our "clean" pluggable eventlist that compiles using only the glazed list code as base.

Gerrit.

Kevin Day wrote:
Hi all-

Awhile back James suggested an approach for implementing a TransformedEventList that allows you to replace it's source list.  This type of thing is needed for support of binding libraries where the source list often comes from a bean selected in the user interface.  This kind of implementation is tricky b/c the PluggableEventList and the source list will often have different locks and notification chains.

I've put together a first cut at the implementation, and I was wondering if I could get feedback on the implementation.  I  am especially interested in feedback on what I've done with the locks.  Also, does the name PluggableEventList make intuitive sense?

I'm attaching both the source and unit test for your review and comment (I hope attachments go through on the listserv).

Thanks in advance,

- K

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@...
For additional commands, e-mail: users-help@...


Re: PluggableEventList implementation - first attempt at supporting binding

by Gerrit Cap :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Kevin,

You are absolutely right, we totally forgot about the list event
publishers. I think your solution about the proxying stuff is probably
the right way to go, similar to our embedded type of read write lock and
I will try to have a look at it the following days but like you I am
also heading out on travel and it might be postponed till end of june
because I hae some other priorities as well before I leave on holidays...


Gerrit

Kevin Day wrote:

> Perhaps Jesse can provide some insight on my question about ListEventPublishers below (Jesse - I'm the tall bald guy you met at Java One - here's the discussion thread that I mentioned after the GL presentation).
>
> Gerrit-
>
> Sorry for taking so long to respond - Java One, kids sick, etc...
>
> In reviewing the code, I have one question/comment:  One of the hard requirements on EventLists in a pipeline is that they share the same ListEventPublisher and the same lock.  You have handled the lock already by using a well defined lock sequence on both the list and the member.  But I think the ListEventPublisher requirement may have been overlooked.
>
> The implementation below uses the AbstractEventList default constructor, which uses the 'default' ListEventPublisher.  This will work, but only if all EventLists are generated using that default publisher.  In a self contained app, this may very well be the case - but in situations where the app may be connecting to remote EventLists (or lists that require their own publisher for some reason), I'm pretty sure the updates will not propogate properly.  I could easily see this happening, for example, if different class loaders were used for the source vs destination list (like in an IOC or OSGi container)
>
> I think that this was the reason that James suggested the alternative approach of syncing an internally captured source list with the member list.  This effectively bridges the two Publishers.  This should work, but it feels like there should be a more elegant solution.
>
> I think another strategy would be to have some sort of proxying/bridging publisher implementation that would silently bridge the publishers (and allow swapping out of the member publisher).  As near as I can tell, doing this kind of proxying would involve providing the proxying publisher to the constructor of the AbstractEventList and update it's state in the setMember() method if the new member has a different publisher.  I'm not sure if this violates any implied contract of the ListEventPublisher implementation.  I'm thinking that it may be safe to do because we only forward the events (by the time we hit the forwarder the downstream dependency graph will have worked itself out).  If events are going to travel downstream, we'd also need to proxy in the other direction, in which case we'd need to either fire events during addX() calls, or possibly add a listener to the PluggableEventList itself to forward events downstream.  I think that it would be necessary to remove/disable that listener during the event forwarding in listChanged() unless the functionality of the ListEventPublisher prevents the infinte loop of notifications that would result.
>
>
> Like I mentioned at the top of the email, maybe Jesse can shed some light on the feasability of this approach.  If he can't think of any reason for not doing this, I can put together an implementation.
>
>
>
> Having this capability may also allow GL to remove the 'same publisher' requirement from GroupingList (not sure on that - GroupingList is muxing things, so bridging publishers may not work in this case).
>
> Regardless, getting this working will be huge for IOC and bindings based implementations.
>
> I'm heading out on travel - will respond to any thoughts/comments in 2 weeks.  Please let me know what you think of the above.
>
> - K
>
> Kevin Day
> Trumpet, Inc.
> www.trumpetinc.com
>
>
> ----------------------- Original Message -----------------------
>  
> From: <gerrit.cap@...>
> To: <users@...>
> Cc:
> Date: Tue, 6 May 2008 10:30:40 +0200
> Subject: RE:  re[2]: PluggableEventList implementation - first attempt at supporting binding
>  
> Hello Kevin,
>
> Unfortunately the mail server here doesn't allow .java extensions so I just included the source code here. Just take your scissors and go and play with the new toy.
>
> The original source we use is tested, but as I said I had to modify it a bit as
>    a) it had internal time statistics so that stuff got away
>    b) internally we rely on locks with a timeout to get some kind of feedback when deadlocks occur
>    c) it used to implement propertychangelistener with the bindings stuff so the propertychanged method is now replaced by setMember
> as a result: this code is untested.
>
> A good unit test would be if a bunch of threads would be created that in parallel try to read from this list, write to the list and replace the member.
>
> Of course in good tradition with the real glazedlists this class is thread-safe-ready. Meaning, we tried to make its internal workings thread safe byt as a user of this class you are still responsible to acquire a readlock when reading from it (e.g. requesting the size or requesting an iterator and looping over that iterator) or a writelock when writing to the list.
>
> Gerrit.
>
> ------------ Cut Here ---------------
> package glazedlists.ext;
>
> import java.util.Collection;
> import java.util.Random;
>
> import ca.odell.glazedlists.AbstractEventList;
> import ca.odell.glazedlists.EventList;
> import ca.odell.glazedlists.TransformedList;
> import ca.odell.glazedlists.event.ListEvent;
> import ca.odell.glazedlists.event.ListEventListener;
> import ca.odell.glazedlists.util.concurrent.Lock;
> import ca.odell.glazedlists.util.concurrent.LockFactory;
> import ca.odell.glazedlists.util.concurrent.ReadWriteLock;
>
> /**
> * The PluggableList allows the member list to be changed to another list.
> *
> */
> public class PluggableList<E> extends AbstractEventList<E> implements ListEventListener<E> {
>
>    private EventList<E> member = null;
>
>    private final ReadWriteLock myLock = LockFactory.DEFAULT.createReadWriteLock();
>
>    public PluggableList(EventList<E> member) {
>        super();
>        this.member = member;
>        this.readWriteLock = new PluggableListReadWriteLock();
>        if (member != null) {
>            this.member.addListEventListener(this);
>        }
>    }
>
>    public void setMember(EventList<E> member) {
>        EventList<E> newMember = null;
>        EventList<E> oldMember = null;
>        this.myLock.writeLock().lock();
>        try {
>            oldMember = this.member;
>            newMember = member;
>            if (newMember == oldMember) {
>                return;
>            }
>            if (oldMember != null) {
>                oldMember.getReadWriteLock().readLock().lock();
>            }
>            if (newMember != null) {
>                newMember.getReadWriteLock().readLock().lock();
&g