Let's take the bus. The Swing Event Bus.

So continuing yesterday's entry about SOA for Swing and event buses, I thought it could be a good idea to take the Swing Event Bus first. Just to explore how far we can go with it.

I received some emails (and just one comment, thanks Tim!) on this so I thought it could be a good idea to share my thoughts with you (well, after all that's what a blog is about, right). But, more importantly, I would like you so thare ideas with me. So let's participate, suggest (and criticize, of course).

Our starting point

To clarify let's assume we have an application with just four major macro-components:

  • A Filesystem Viewer (FV). A component that visually shows the contents of the file system. Say with a JTree or a JTreeTable. This component is responsible or detecting selections of files, double clicks and drag-and-drop operations.
  • A Status bar (SB). Responsible for showing status messages.
  • An editor window (ED). Responsible for opening text files.
  • A Menu bar (MB). With the usual actions for opening files, etc. Of course some of the items in the menus are enabled and disabled depending, say, on the type of the file and the status (saved/unsaved) of the editor.
Yesterday I described some of the horrors of the Observer pattern in complex GUIs. This is, how the event-source to event-listener one-to-one relationship is a nightmare for big applications. There's another description of some of the problems at Martin Fowler's site (see "Observer Gotchas"), just if you're interested. (By the way, you may find the event aggregator pattern of interest too).

So, to summarize, we want do build a Swing Event Bus that efficiently delivers events generated by these macro-components. Either synchronously or asynchronously. Our main objective is to make these macro-components as loosely coupled as possible. Ideally each one of these four macro-components will know nothing about the rest, although they will be orchestrated and will jointly cooperate to build the user interface of a simple text editor.

(I would like to add a feature here too: the whole thing should be extremely simple to use).

Design Idea I: Different bus lines?

Now let's see how other people are doing this. Let's see further by standing on the shoulders of giants. Let's learn from what it's already done.

The very first feature that comes to my mind is the need for different bus lines. This is what JMS calls Topics and what Werx calls Channels.

The benefit of this, of course, is that we don't have to deliver the events to all bus listeners. We don't have to stop the bus in all bus stops in the city. We reduce the number of bus stops to those in the bus line. Events are delivered (through a channel/topic) to a subset of listeners. And the traffic is more efficient, and our qualities of service improve.

Design Idea II: Separating concerns

I like separation of concerns. Each macro-component must be responsible for its own GUI. Events internal to the macro-component should be handled internally, using event sources and event listeners as usual.

The macro-components should communicate with the outside world by posting specific events to a topic/channel in the bus. For instance, the FV could generate FileSelectedEvents and FileDoubleClickedEvents to the "FV" topic in the bus. And the editor could post EditorSaveEvent, or whatever other, in the "editor" topic in the bus. Don't you think?

Design Idea III: Mediator Pattern?

As Tim suggested there should be a mediator somewhere to coordinate events and macro-components. I think the Mediator Pattern fits nicely:

The Mediator pattern uses an object to coordinate state changes between other objects. Putting the logic in one object to manage state changes of other objects, instead of distributing the logic over the other objects, results in a more cohesive implementation of the logic and decreased coupling between the other objects
Do you think this is a good idea? I see the controller in the MVC here, right? If so, shall this Mediator have direct references to the macro-components? I mean, there're two ways for the mediator to command something to the macro-components:
  • A) Having references to macro-components. Say the mediator has an instance of the "EditorPanel" and just invokes "Editorpanel.open( aFile )".
  • B) On the other hand this mediator could just send an EditorOpenEvent through the "edit" topic of the bus, so that the editor opens it.
I'm afraid that A) above is asking for trouble. I was wondering if that by using "B)" above things should be easier to test functionally? I mean, by storing (and then repeating in order) events received in the bus we could make functional tests easier to reproduce. Am I right here?

Design Idea III: Inversion of control

I received a pointer to Mike Aizatsky's article about Inversion of Control with listeners. I'm not very good at IoC so I don't know if this applies or not here. Does it? How?

Well, that's all today (I'm too tired). I'd like to hear from you!
Cheers,
Antonio

Comentarios:

I think you're on the right track.

I wrote yesterday about separating the events into COmmandEvents and StateEvents. Now that you've clarified the needs of the app I can give you pertinent examples.

Let's use the nomenclature of a Topic for a category of events. You can create series of events for File's as such:

CommandEvents: SystemSelectFile, SystemEditFile, SystemCloseFile, SystemMoveFile, SystemSaveFile, etc...

StateEvents: SystemFileSelected, SystemFileEdited, SystemFileSaved, etc...

StateEvents can be used to ...(i.e)

SystemFileEdited

Update the status bar with name of the file etc... (SystemFileClosed as well)

SystemFileSaved

Update the State of the system to indicate that the file does not need to be saved?

For the file Topic your CommandEvents may need to be vetoable, in the case where a file is already being edited, you send SystemEditFile and prompt the user to save and they hit cancel. Your event would be vetoed and you would not continue on to Edit the file.

SystemEditFile could go something like this:

Fire the SystemEditFile (vetoable, System Topic) event from the FileViewer (after a sucessful SystemSelectFile event?) which will be handled by the SystemMediator. The SystemMediator checks to see if there is a file currently being edited (in some SystemContext perhaps?)

The SystemMediator Displays a SaveDialog (Y, N, Cancel)

If the user responds Y: fire a saveFile (File Topic) event to the editor fire a fileSaved (File Topic) event from the editor or mediator (your choice) If N: fire a closeFile (File Topic) event to the editor fire a fileClosed (File Topic) event from the editor or mediator (your choice)

If cancel: veto the SystemEditFile Event back to the FileViewer

Keep in mind that all CommandEvents can be vetoable even if they wouldn't normally veto (in case of an error). It's the mediator's responsibility to handle or not handle vetoes, as well as the series of Macro Component events that will be dispatched and their order... This would give you a pretty good implementation of design ideas 1, 2, and 3. I hope that helps, or at least makes sense... Tim

Enviado por Tim Osten en septiembre 21, 2005 a las 09:16 PM CEST #

This is a great topic to discuss. I have been trying to figure out a way to make my Platonos framework (www.platonos.org) allow our dynamic plugin engine to connect plugins in various ways. Our engine models the Eclipse engine, so we support extension points and extensions. This is great for resolving plugins to one another and allowing one plugin to query the plugin repository for any plugins that have provided extensions to it. It doesn't however handle in a good fashion a "bus" as you describe. It would be great if a plugin could just attach to the bus and get ALL events coming down the pipe OR register for specific messages it is interested in.

One issue I am not sure about is how is this much different than Swing event thread other than its for pieces of an application instead of components that make up those pieces. I fully agree that each module should use the normal Swing event system for components/models/view as they do now. But it seems your analogy of a bus is identical except that it interconnects modules.

This leads into the next problem I have with this. How can one module not know/care about another and yet listen for specific events that are generated by a module it has no clue about? If module A defines and fires off A_EVENT_TYPE, then module B would have to know about A to accept A_EVENT_TYPE and do something with it. That makes B dependent on A. Personally, I hate dependencies. I would much rather avoid plugin dependencies, although our engine does support it. I would much rather have a "Hey, A, if you are there, I want to listen and handle for your A events. If you are not there, I'll either 'unload' cause I depend on you to function, or I'll sit and wait for other modules that I depend upon that ARE present". I like this type of dependency far better, as it's easier to manage in my opinion. This would almost work in our engine, except that we also want to support library plugins. For example, an Ant library wrapper so other plugins can make use of the ant.jar through depending on the plugin. We of course do our classloader magic. Every plugin has its own classloader, and we delegate to other plugins to look for classes that are depended upon and all that jazz. Works very well.

On another angle of dilema I am having with my framework, I want plugins to easily add menu items that are visually context sensitive. Meaning, if I add a navigator like plugin, I want to control buttons like copy, paste, cut, etc to enable/disable them when certain events become true. But this all has to be dynamic. If one plugin provides a GUI menu and toolbar system, ALL other plugins that want to add menus/toolbar items as well as have them update when certain events occur, must be aware of and depend on that plugin. This is fine, I believe. But it makes it less decoupled as you suggest with the event bus. It seems to me the best of all three worlds may work out in the end. Each plugin (in my case) that provides some sort of GUI (generally a JPanel or JComponent of some sort that is displayed) will use the normal Swing MVC. It may also hook into other plugins via the extension point/extension mechanism made so popular by Eclipse. Finally, using an event bus (possibly provided by a plugin), any plugin can listen to all or specific events sent down the event bus.

I do have one other issue with all this. The ability to veto events. Meaning, if plugin A, B and C all subscribe to the same Open File event. And lets say A and B both try to do something with the file that locks it, well, the 2nd one that tries to do something will end up with some exception, probably due to file locking. However, how do you handle the "who wins" situation? What decides that A's handling is more important than B's handling of the event, thus A should veto any other plugin from receiving the event?

I really wish I could finish my dang framework! I think a lot of Java Desktop developers would find it helpful in handling a lot of the grunt work, much like Eclipse RPC only with some differences as well.

Enviado por Kevin en septiembre 21, 2005 a las 10:50 PM CEST #

Kevin,

In response to your questions...

"One issue I am not sure about is how is this much different than Swing event thread other than its for pieces of an application instead of components that make up those pieces. I fully agree that each module should use the normal Swing event system for components/models/view as they do now. But it seems your analogy of a bus is identical except that it interconnects modules."

The method of event delivery can vary from implementation to implementation. Some apps may deliver events that come on the Event Dispatch Thread (EDT) others may have to convert the event to the EDT, or both (Consider firing SystemEditFile to an open application when a user double clicks on the file in Windows Explorer, the event could be passed to the existing app via TCP/IP). It is different because of that fact. Events may or may not be delivered on the event thread (i.e. server apps)

"How can one module not know/care about another and yet listen for specific events that are generated by a module it has no clue about? If module A defines and fires off A_EVENT_TYPE, then module B would have to know about A to accept A_EVENT_TYPE and do something with it. That makes B dependent on A."

In this case B would not necessarily need to know about A, only about the A_EVENT_TYPE. For example: A fires connecting, connected. B doesn't care what A is only that B needs to respond to A_EVENT_TYPE: connecting, connected. Those are state events, A is connecting, A is connected. CommandEvent listeners don't necessarily care who tells them what to do, only that they have to do it (like a good soldier!). “A” would only care that someone told him to connect...

"I do have one other issue with all this. The ability to veto events. Meaning, if plugin A, B and C all subscribe to the same Open File event. And lets say A and B both try to do something with the file that locks it, well, the 2nd one that tries to do something will end up with some exception, probably due to file locking. However, how do you handle the "who wins" situation? What decides that A's handling is more important than B's handling of the event, thus A should veto any other plugin from receiving the event?"

To answer this question I'll start by saying that CommandEvents, which OpenFile would be classified as, usually have only one listener (barring a logging listener or something of that nature), the component that will perform the CommandEvent. This is an issue of responsibility, i.e. one component has all the facilities with which to perform the command. Vetoing a CommandEvent is acceptable because the CommandEvent is either completed or not, in which case subsequent CommandEvents may not be fired (see my SystemEditFile example).

Having multiple listeners for a CommandEvent would mean that multiple components would have responsibility for performing the CommandEvent. This is counter to the idea of cohesion, where one component has definite responsibility for a task. Additionally, you would have to be careful in the ordering of the listeners to ensure that the components responded in the correct order to the CommandEvent (not always guaranteed). A more effective means of handling this problem is throught the use of a mediator, having the mediator handle the CommandEvent and using it to fire separate CommandEvents (in order) to the interested components. This would look like the System Mediator firing an OpenFileEvent to a FileMediator who fired distinct events to the components in order. It seems as if your plugins should be listening for the fileOpened event and then interacting with the file.

I hope that makes sense.

Tim

Enviado por Tim Osten en septiembre 22, 2005 a las 12:49 PM CEST #

Hi,

I guess I never answered the math question earlier so I'll repost :) Nice discussion, and something I've not seen addressed much.

I've been using the event listener framework with much success on my recent project.

It basically allows the creation of your own events and event listeners. The events can have multiple types that get translated into calling different methods within the listener, much along the lines of awt events like MouseListener. This I've found is very useful for grouping events by application symantics. They also have a channel concept to group event dispatching. Allows my various macro components to be completly decouped. They just fire off events and/or listen to the events they are interested in.

The only tricky thing I've found is in designing the granularity of the events and event types. ie when to have a different event rather than another type of the same event.

Jonny

Enviado por Jonny Wray en septiembre 22, 2005 a las 02:05 PM CEST #

Hi Antonio,

At http://superficial.sourceforge.net you will see that I have been working on very similar problems, and it is interesting that Superficial uses several of your 'design ideas' ie separation of concerns, mediator and inversion of control.

Perhaps the main difference is that Superficial abstracts away the GUI toolkit and event loop as the 'facet layer'.

Superficial is sufficiently complex that it needs a white paper, so I've attempted to make it clearer with a detailed worked example based on Martin Fowler's GUI patterns papers.

Perhaps Superficial includes ideas you can use.

Regards, David Wright

Enviado por David Wright en septiembre 30, 2005 a las 03:29 AM CEST #

Enviar un comentario:
Los comentarios han sido deshabilitados.
About

swinger

Search

Archives
« abril 2014
lunmarmiéjueviesábdom
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Hoy