Page 1 of 2 12 LastLast
Results 1 to 10 of 18

Thread: Somethings to think about

  1. #1
    Join Date
    Jun 2005
    Location
    Yorkshire, England
    Posts
    9

    Default Somethings to think about

    About six months ago I designed and built a very lightweight framework for a Swing application. On reading through the Spring RCP User Documentation, it seems to share some ideas, although the emphases is quite different. My framework provided the application with a state machine rather than help with coding views, menus or controls.

    Commands take the machine from one state to another. The state defines the views shown and the commands that are enabled. The commands were loosely based on Struts actions, with an execute method that returns the name of the next state rather than a reference to the next JSP or action.

    The components

    Command Factory

    The command factory that manufactures command beans that the developer codes the execute method to do a particular piece of logic and return the name of the next state. Commands can also provide an isEnabled method that can disable the command even if the machine is in a state that declares that command. Controls and KeyStrokes that have disabled commands attached are disabled and controls are greyed out. The configuration wires up the keystroke and the button top text for each command. The application nominates one command as the boot command. This takes the state machine to the initial state and can be used to perform intialisation.

    The State Factory

    The state factory returns descriptions of states. A state bean holds:
    1. the set of panels,
    2. the InputMap/ActionMap for the state's commands
    3. the default button and default focus component.
    When a state becomes active, each panel will be attached to a corresponding containing panel. The application was designed with a static set of containers. For more complex applications I would have a view container factory with the state nominating a set of containers

    The Panel Factory

    The panels are coded by a developer. Panels (views) can optionally implement an interface whose methods are called when:
    1. the panel is being displayed because of a shift in state.
    2. a command is executed while a panel is shown.
    I would liked to have introduced the idea of forms being passed to panels.

    SwingAction

    An implementation of javax.swing.Action that holds a command bean as a property, and passes it to a frame's request processor when the performAction is executed. The developer gets these from the comand factory to attach commands to buttons or other controls, and the state factory uses them to attach commands to ActionMaps.

    The Request Processor

    This manages:
    1. The execution of the command
    2. The detaching and attaching of panels to containers for the new state.
    3. The attaching of the correct InputMap/ActionMap to the frame for the state.
    4. The disabling and enabling of SwingActions depending on wheither their commands are associated with the current state.
    5. The setting of the focus and the default button for the state.


    This arrangement relieves the developer of the burden of managing the display of views and coding InputMaps/ActionMaps. I'm pretty pleased with the result. It's a bit simplistic, and probably only suits certain types of application, but then it was only intended for use in a point of sale system. I understand

    The Model

    However, one thing I really feel that I got wrong was the way I dealt with the model. I went with the bound JavaBeans approach to the model. Changes to a bean's properties fire change events to any registered listeners. The listeners then re-read the property and if they are controls re-draw themselves. This sounds great, but it doesn't work well when your data model is really an object graph rather than a single bean. When your Employee bean refers to an Address bean, and you want to swap your Employee bean for another, you end up with a load of plumbing to cope with re-bind the controls that were bound to the Address bean properties. In fact what really happens (if you are as dense as me) is that you write code to get the values out of your beans and put them into controls (and visa versa). When you look at indexed properties (or the List/Map/Set properties you need for Hibernate) it just gets worse.

    I've been pondering this for a few months, and I now think I have a neat way of doing this. I have even partly coded it. It is just a shame it is all too late for the above project. However, I thought I'd share the idea. You might like it, you may think it is really old hat, or you may just laugh derisively, I don't mind.

    Here goes:

    I still using bound JavaBeans. They are generated from Hibernate mapping files, but I guess AOP could be used to fire the change events.

    Then, rather than have controls register property change listeners with the beans directly, they register with a proxy, and the proxy registers a single listener with the bean. The proxy forwards the property change events from the bean to all its listeners, who read and update the property via the proxy. Once you have a set of control classes that bind to proxies in this way, creating swing panels with bound controls becomes easy:
    Code:
    public class EmployeePanel extends JPanel {
    	private BeanProxy employeeBeanProxy;
    
    	public EmployeePanel (BeanProxy employeeBeanProxy){
    		this.employeeBeanProxy = employeeBeanProxy;
    		this.add(new TextControl(employeeBeanProxy, "forename"));
    		this.add(new TextControl(employeeBeanProxy, "surname"));
    	}
    }
    When a new bean arrives from the server, it is put into the proxy by the workflow logic.
    Code:
    	employeeBeanProxy.setBean(employeeBean);
    The proxy re-binds to the new bean and fires property change events to all the listeners to indicate that all the properties have changed.

    Here's the clever bit: to cope with a graph of beans, further proxies register listeners with the first proxy. The initial proxy becomes the root of a graph of proxies that reflects the structure of the graph of beans. When the first bean is swapped or a watched property changes, the chained proxies take the new property value (a JavaBean of course) and bind to it.
    Code:
    public class EmployeePanel extends JPanel {
    	private BeanProxy employeeBeanProxy;
    	private BeanProxy addressBeanProxy;
    
    	public EmployeePanel (BeanProxy employeeBeanProxy){
    		this.employeeBeanProxy = employeeBeanProxy;
    		this.add(new TextControl(employeeBeanProxy, "forename"));
    		this.add(new TextControl(employeeBeanProxy, "surname"));
    		this.addressBeanProxy = new BeanProxy(employeeBeanProxy, "address");
    		this.add(new TextControl(addressBeanProxy, "line1"));
    		this.add(new TextControl(addressBeanProxy, "line2"));
    		...
    	}
    }
    Still when a new employee bean arrives from the server, all the workflow logic needs to do is put it into the root bean proxy.
    Code:
    	employeeBeanProxy.setBean(employeeBean);
    This gives the controls something stable to bind to. As you can see they can be bound to proxies before any beans are created. Controls can just register with a proxy when they are created, update themselves when they get a property change event, and write any user changes back via the proxy.


    Indexed properties and Lists properties are slightly more complex, but I'm working on it. List properties require an implementation of List that fires property change events (perhaps I really should get around looking at AOP). In fact I am planning to use extend an abstract implementation of List that exposes the items in the list as an indexed property. Ultimately I want to be able to extend JTable so that it can bind to a List property like this:
    Code:
    public class CompanyPanel extends JPanel {
    	private BeanProxy companyBeanProxy;
    
    	public CompanyPanel (BeanProxy companyBeanProxy){
    		this.companyBeanProxy = companyBeanProxy;
    		this.add(new BeanTable(companyBeanProxy, "employeeList",
    			new String[]{"forename", "surname"}));
    		...
    	}
    }
    This table will display the properties from all the beans in the list, I should have then only have to do presentation coding, but nothing to do with keeping the data upto date, other that set the company bean into the proxy.

    There are further ideas circling to do with controls that look at the property type and edit it accordingly, or even including BeanInfo attributes for properties to provide validation and formatting info, but that's probably not going to fit in with RCP.

    I hope you found something interesting in this diatribe.

    Reg.

  2. #2
    Join Date
    Aug 2004
    Location
    Melbourne, Australia
    Posts
    335

    Default

    Reg,

    interesting post, thanks. I'd say that some form of state machine support will certainly be part of spring rich though not our initial release.

    With regards to your described model implementation we actually support all of this right now so you may want to have a closer look at our code. Check out the ValueModel and BeanPropertyAccessStrategy classes for the bean proxy functionality and BufferedCollectionValueModel for dealing with the List problem. We do automatic binding and type conversion dependent on the bound properties type (see Binding, BindingSelectionStrategy, Converter, DefaultConversionService and the various Binding implementations).

    Ollie

  3. #3
    Join Date
    Jun 2005
    Location
    Yorkshire, England
    Posts
    9

    Default

    Thanks Ollie. I will dig a bit deeper. I look forward to using Spring RCP in the future.

    Will you be providing an implementation of java.util.List that fires property change events when items in the list are changed? Hibernate's handling of relationships seems to rely on lists as properties. However, Hibernate does some naughty substitution of these lists (maps and sets) unless they implement org.hibernate.usertype.UserCollectionType. Will you provide a version for this too?

    P.S. I'm sure that with care large parts of the state machine could be made independant of Swing. So may be it could be used for say SWT (although I know little of SWT).

    Reg

  4. #4
    Join Date
    Aug 2004
    Location
    Melbourne, Australia
    Posts
    335

    Default

    Will you be providing an implementation of java.util.List that fires property change events when items in the list are changed?
    Yes we do. See our ObservableList interface and ListListModel implementation.

    Ollie

  5. #5
    Join Date
    Aug 2004
    Posts
    229

    Default

    Concerning the state machine idea, I think this would be a very good addition to Spring-rich, but we certainly don't want to duplicate any effort. I haven't looked at Spring's WebFlow module yet, but based on its description it sounds very much like what we want. And since Keith developed it, I'm sure it is abstracted enough that we could create an implementation for Swing/UI. It would probably be less effort to adapt Spring WebFlow than develop something from scratch, but the main benefit is that we could leverage all the development and attention WebFlow gets.
    Concerning an implementation of java.util.List that fires events as the list changes, GlazedLists does this well now (and there is some integration code between GlazedLists and spring-rich already in spring-rich). GlazedLists also provides all sorts of adapter classes for both Swing and SWT UIs. Before you reinvent any wheels, please take a look at GlazedLists and see if it does what you need: http://publicobject.com/glazedlists/ - as you can see, I'm quite the GlazedLists convert.

    - Andy

  6. #6
    Join Date
    Jun 2005
    Location
    Yorkshire, England
    Posts
    9

    Default

    Thanks for the GlazedLists reference. The EventList looks like just the thing I am looking for, now I just need to get hibernate use it. Rats - single inheritance.

    I've got to say that building a gui application in terms of a state machine, can give it some much needed structure, without requiring that much actual framework code.

  7. #7
    Join Date
    Jan 2005
    Location
    Ottawa Canada
    Posts
    14

    Default

    Reg,

    I really like the ideas and concepts in your post… and it is never too late for good ideas. I just spent a week with Keith Donald to explore the possibility of using Spring Rich Client instead of our home grown framework. During this week, we had some discussions that are surprisingly close to what you have just stated, including the idea of integrating Spring Rich Client and Spring Web Flow. Keith is receptive to new ideas that strengthen the framework.

    Specifically, I agree that business applications could really benefit from being driven from a state machine similar to Spring Web Flow. We sometimes accidentally merge concepts because they are typically used together, but that can reduce the flexibility of the system (not to mention making it harder to test).

    I think that the following concepts should be completely separate.

    - The Command’s View
    - Command Events
    - Actions (commonly triggered by a Command Event)
    - Navigation (what happens after an action is complete)

    I plan to follow your lead and write up some ideas on how the architecture could work and post it to this forum for discussion. I’ll also be converting parts of my home grown rich client framework to Spring and let the code tell me what is a good idea and what isn’t. I really believe though that the navigation part of an application can be separated from the Command object and handled by a state machine.

    Jim Leask

  8. #8
    Join Date
    Aug 2004
    Posts
    229

    Default

    I agree. Any sizeable UI application either reinvents this wheel, or ends up a huge coding mess. If Spring-rich provided this kind of support... well... it would be unstoppable.
    In our case, our "reinvention" is that the UI is totally driven by our workflow engine (Con:cern, in our case). We use workflow for both huge processes that coordinate many resources (including people) and small flows through a UI involving a single person. One nice thing about this approach is that at any point we can hand a flow off to another user if needed. For example, you may have a workflow for setting up a new customer. This workflow drives the UI, at least until the current user is no longer able to continue with the workflow (they may not have security privaleges to determine the credit limit of the new customer, for example), so the flow gets passed off to another user (getting added to their todo list).

    - Andy

  9. #9
    Join Date
    Jan 2005
    Location
    Ottawa Canada
    Posts
    14

    Default

    Yup - that sounds very familiar. We are doing similar things with security privaleges and complex business rules driving the navigation, as I suspect all business applications are. Our navigation problem ended up in the 'huge coding mess' category. It works (works pretty well actually), but there definitely must be a better and easier way.

    Interestingly,some of our work flows span a web application and rich client application. I don't see any reason why we can't have both web and rich client applications driven from the same workflow engine. (throw in the mobile applications too for that matter - it is all one business workflow)

    Jim

  10. #10
    Join Date
    Jun 2005
    Location
    Yorkshire, England
    Posts
    9

    Default

    Quote Originally Posted by leask
    Interestingly,some of our work flows span a web application and rich client application. I don't see any reason why we can't have both web and rich client applications driven from the same workflow engine.
    Interestingly I have been pondering using the same workflow engine driving two different user interfaces, but for a slightly different reason.

    The point-of-sale app I am working on has two UIs. One is a swing interface, the other is a two line external display. This VDU displays the name of an item being sold and it's price, or the total order amount, or the change due to the customer. (You can imagine the type of stuff).

    So now I am thinking in terms of registering a StateListener with the state machine for each type of GUI. This would allow using the state machine with SWT or with a Web framework.

    With this separation comes another aspect to think about. Driving the state machine, and causing the commands to be executed. Point of sale machines can be driven by external devices such as a card reader or a PIN entry keypad, but they have no UI. There are number of issues here with threads, which I am not sure how to deal with. With swing a lot of things must happen on the event dispatch thread, but these other devices will not be calling on that thread.

    Also back with commands and states, more ideas:

    1. Sometimes it would be good to map the result of a command to a new state outside of the command. Struts does this. Your action might indicate that the data the user entered was good, or it might indicate it was bad. The machine would go to different states depending on the result.

    2. Lots of states share sets of commands. You don't want to be repeating them all for each state.

    3. Some commands are available in lots of states, but at some later stage (at the end of some process), another command needs to bring us back to state it all started from. So rather than a simple "result A sends us to State X", we want to push the current state onto a stack and goto the new state. Another command result needs to be able to "pop" this state. A GUI can be affected by this. It might want to restore focus, or even text selection.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •