|
From: Keith D. <kd...@cs...> - 2004-07-07 05:20:23
|
I've considerably refined several areas of the rich client codebase this =
past weekend. Specifically:
1. A rich client's application lifecycle is much more defined, with =
hooks for inserting custom initialization logic within that lifecycle. =
Related, the Application is now a generic service locator and overall =
controller / manager for the app. No more hard wired stuff in there =
(for example, the setupActionHandling() problem as Ollie / Ronald =
mentioned.) The lifecycle delegate (aka application advisor) is now the =
place where application-specific initialization & destruction logic is =
defined.
2. The command framework in the rcp.command package is now "the" gui =
action framework for rcp; obsoleting the code in the rcp.action package. =
In general this framework is more powerful, better designed, and easier =
to use. I would like to remove the rcp.action code as soon as possible.
3. Initial support for multiple Application Windows per Application is =
committed.
I've updated the Petclinic sample app to demonstrate each of these major =
additions. Here are some details on each:
Regarding lifecycle:
- When a Spring Rich client application starts, the ApplicationLauncher =
is responsible for bootstrapping the application's main context, =
triggering the creation of the single global "Application" instance. =
This Application singleton is injected a configured =
"ApplicationLifecycle" instance, which it delegates to at well-defined =
points within the Application startup and shutdown process to facilitate =
customization. For example, before the application initializes, it =
calls "onPreInitialize(this)" on the lifecycle object, allowing for =
custom initialization logic. Before the first window is displayed, it =
calls "showIntro" and "onPreWindowOpen", allowing for configuration of =
the window (or popping up a login prompt.) The Lifecycle object is also =
responsible for returning a configured set of "command groups" for each =
opened window as it is created. These command groups are used to =
configure the new window's menu bar, tool bar, and status bar (which are =
now all optional.) So in general, the Application manages instanting =
new windows, pages, views, etc. as needed in response to user events, =
and the lifecycle object is the delegate that is passed these objects at =
the appropriate times for customization (where customization may be =
programatic or pulled from a Spring bean factory.)
Regarding commands:
- I've hinted about the power of the new command framework in a previous =
email. Here is a off the cuff feature break down:
- The hiearchy of commands is now much better defined - you now =
have:
ActionCommand - Subclasses are exectuable gui actions (e.g. =
RenameCommand, OpenNewWindowCommand, ExitCommand); each subclass =
implements the "doExecuteCommand()" method.
=20
TargetableActionCommand - commands whose appearance is centrally =
managed but whose implementation changes in response to user events; for =
example, switching views. These commands are also useful when you want =
to handle a command invocation, but can't subclass ActionCommand =
(perhaps your object is part of another inheritence hierarchy.)
ToggleComand - an action command that is a toggleable selection; =
"on/off"
Command groups - are command containers and are factories for =
all your "grouping controls" - e.g menu bars, tool bars, button bars, =
menus, status bars, radio button groups (exclusive groups), etc. They =
don't execute anything, they group other commands. Groups can nest =
other groups.
- Commands are factories for the GUI controls that make sense for =
their particular command class. For example, ActionCommands can produce =
JButtons and menu items that, when clicked, execute the command. Toggle =
Commands produce check box menu items or toggle buttons. Command groups =
produce all your "bars" as mentioned previously. A single command =
(group or action) can manage one to many created controls. "Manage" =
meaning if the command is changed centrally (for example, a property =
changes or a new member is added to a group), all bound controls are =
updated.
- All command control construction is delegated to extensible =
control factories to facilitate customization & integration with custom =
controls.
- Support for action command interceptors; logic to execute before / =
after each action command.
- Support for command execution parameters; abritrary data that =
should be passed to a command upon execution. Parameters are "one off" =
hints to the command, cleared after each invocation. Useful for =
decoupling the command from callers who get the data the command needs =
to execute.
- Each Command has a "face descriptor" which manages its visual =
appearance (configured automatically for you using a CommandConfigurer, =
where typical implementations are BeanPostProcessors.) A command's =
visibility and enabled state (be it a group or a action command) are =
both controllable; any bound controls automatically update. A command's =
visual appearance can be configured differently based on the context in =
which it is bound to a particular control (for example, actions added to =
tool bars generally have tool tips, but those added to menus do not.)
- Support for global commands is finally nailed down. Global =
commands -- instances of TargetableActionCommand -- are managed by each =
application window instance; implementation is delegated to the active =
view within the window (and changes as the active view is switched by =
the user.) If no implementation exists for a global command when a view =
is activated, that global comand is automatically disabled. A command =
manager (aka registry) is owned by each instantiated window.
- As hinted at above, the command lifecycle is finally correct. =
Commands are instantiated on a per window basis, managed by that windows =
CommandManager. Take a look at the petclinic sample for an illustration =
of this; the new "commands-context.xml" file declares each command =
object and the command manager as a "singleton" bean; however, a new =
associated bean factory is instantiated on each new ApplicationWindow =
creation. This is the appropriate cardinality; gui actions/commands are =
managed by their parent window and shouldn't be global application =
objects. (as an aside, if you look close, you'll see the "View" =
cardinality is still wrong; views should also be managed by their =
containing windows... a view descriptor is global, but not the view =
itself...that's a TODO for sure :-))
- Command groups can be updated dynamically at runtime after the =
application is started. For example, perhaps you want to defer =
contributing a set of actions to the tool bar command group until a view =
is activated, and remove them when the view is closed. Each command =
group has an expansion point where additional members may be appended =
dynamically. Any bound controls are automatically rebuilt as new =
members are added. Commands to update may be located using a convenient =
file-path like notation.e.g "fileMenu/newMenu/myCommand".
What's next?
- We want the Petclinic sample to really start taking advantage of some =
of these new features by adding in additional functionality typical of a =
standard enterprise j2ee app. Things like login authentication, =
preferences, better illustration of integration with middle tier =
services, etc. In general we want to further develop the sample as soon =
as possible--Ben is helping out here which is great.
- I think we're on our way to release candidate form... documentation =
still needs considerable work, but we're getting there. I think a =
remaining "killer feature" we need is support for multiple views on the =
same page, and views that are dockable/stackable (integration with a =
slick windowing system.)
Feedback very much welcome! Keith |