spring rich client

43
Spring Rich Client A practical introduction An introduction to the Spring Rich Client framework Lieven DOCLO

Upload: diavoloet

Post on 21-Apr-2015

21 views

Category:

Documents


5 download

TRANSCRIPT

Page 1: Spring Rich Client

Spring Rich Client

A practical introduction

An introduction to the Spring Rich Client framework Lieven DOCLO

Page 2: Spring Rich Client

2

CONTENTS

Introduction ........................................................................................................................................................................................ 5

Overview ......................................................................................................................................................................................... 5

Getting our hands dirty ................................................................................................................................................................... 5

The Spring Rich Client framework, a quick dissection ........................................................................................................................ 8

Anatomy of a rich client application ............................................................................................................................................... 8

Application windows ....................................................................................................................................................................... 8

Views ............................................................................................................................................................................................... 9

Commands ...................................................................................................................................................................................... 9

Messages and icons ...................................................................................................................................................................... 11

Other components ........................................................................................................................................................................ 11

Applications ...................................................................................................................................................................................... 12

Application lifecycle ...................................................................................................................................................................... 12

Hooking into a lifecycle ................................................................................................................................................................. 12

Application .................................................................................................................................................................................... 12

Example: Adding login functionality to the application ................................................................................................................ 12

Other possible uses ....................................................................................................................................................................... 13

Playing with the statusbar ............................................................................................................................................................ 13

Elaborate example: Adding multiple toolbars to the screen ........................................................................................................ 14

Commands ........................................................................................................................................................................................ 17

Why the abstraction ..................................................................................................................................................................... 17

How to create a simple command ................................................................................................................................................ 17

Configure a programmatically created command ........................................................................................................................ 17

Changing the command to a visual component ........................................................................................................................... 17

Command internationalization and images .................................................................................................................................. 18

Grouping commands ..................................................................................................................................................................... 18

Building a command group in a Spring context ............................................................................................................................ 18

Getting a Spring configured command in code ............................................................................................................................ 19

Views ................................................................................................................................................................................................. 20

What is a view ............................................................................................................................................................................... 20

View descriptors ........................................................................................................................................................................... 20

Creating views ............................................................................................................................................................................... 20

Page 3: Spring Rich Client

3

Creating a view descriptor for a view ........................................................................................................................................... 21

Showing the view in the application ............................................................................................................................................. 21

Binding and forms ............................................................................................................................................................................. 22

Formmodels and valuemodels ...................................................................................................................................................... 22

What is a valuemodel ............................................................................................................................................................... 22

What is a formmodel ................................................................................................................................................................ 22

The default formmodel ............................................................................................................................................................. 22

Buffering ................................................................................................................................................................................... 22

Read-only manipulation ............................................................................................................................................................ 23

Validation .................................................................................................................................................................................. 23

Creating formmodels ................................................................................................................................................................ 23

Binding .......................................................................................................................................................................................... 24

What is binding ......................................................................................................................................................................... 24

How does binding work and what does it do ........................................................................................................................... 24

Binders ...................................................................................................................................................................................... 24

Binder examples........................................................................................................................................................................ 24

Creating your own binder ......................................................................................................................................................... 24

Forms ............................................................................................................................................................................................ 26

What is a form........................................................................................................................................................................... 26

Creating a form ......................................................................................................................................................................... 26

Form builders ............................................................................................................................................................................ 28

Binder selection ........................................................................................................................................................................ 28

Internationalization................................................................................................................................................................... 29

Adding forms to forms, aka child forms .................................................................................................................................... 29

Form validation ......................................................................................................................................................................... 29

Form component interceptors ...................................................................................................................................................... 30

Introduction .............................................................................................................................................................................. 30

Creating your own interceptor ................................................................................................................................................. 30

Configuration ............................................................................................................................................................................ 30

Built-in interceptors .................................................................................................................................................................. 31

Validation .......................................................................................................................................................................................... 34

Why validation .............................................................................................................................................................................. 34

Page 4: Spring Rich Client

4

Validation choices ......................................................................................................................................................................... 34

Rule validation .............................................................................................................................................................................. 34

Using the rule framework ......................................................................................................................................................... 34

Constraints ................................................................................................................................................................................ 34

Validation triggers ..................................................................................................................................................................... 35

Dependent properties ............................................................................................................................................................... 35

Hibernate validator integration .................................................................................................................................................... 35

Valang validation framework integration ..................................................................................................................................... 35

integrating your own validation framework ................................................................................................................................. 35

Exception handling ............................................................................................................................................................................ 36

Avoiding try-catch constructs ....................................................................................................................................................... 36

Registering an exception handler ................................................................................................................................................. 36

Build-in exception handlers .......................................................................................................................................................... 37

Log silently ................................................................................................................................................................................ 37

Show a message dialog ............................................................................................................................................................. 37

Hibernate validation exception handling .................................................................................................................................. 38

Custom exception handlers ...................................................................................................................................................... 38

Using the right exception handler for the right exception ........................................................................................................... 38

Simple delegation ..................................................................................................................................................................... 39

Unwrapping exceptions ............................................................................................................................................................ 39

Security ............................................................................................................................................................................................. 41

Integrating security in a GUI application....................................................................................................................................... 41

Spring Security integration ........................................................................................................................................................... 41

Turning on security awareness of the GUI .................................................................................................................................... 41

Securing commands ...................................................................................................................................................................... 42

Wizards ............................................................................................................................................................................................. 43

Why use wizards ........................................................................................................................................................................... 43

Creating a wizard .......................................................................................................................................................................... 43

Showing a wizard .......................................................................................................................................................................... 43

Page 5: Spring Rich Client

5

INTRODUCTION

OVERVIEW

Rich clients are becoming increasingly more popular nowadays. A quick search through Google on “rich client framework java”

return over 200.000 results.

These frameworks can be divided into 2 distinct categories:

The Swing or SWT based frameworks, designed for out-of-browser applications, to be used with technologies like Java

Webstart.

The Rich Internet Application (RIA) frameworks, designed for providing a rich client experience within the confines of a

web browser (such as Mozilla or Internet Explorer).

Today, RIA frameworks make up the bulk of the rich client frameworks on the market today. Among these you’ll find names like

Flex, Google Web Toolkit, Ice Faces, … Their popularity is mainly caused by the ease of deployment: no installation is needed ,

almost every user already has a Web browser.

For Swing (or SWT) based application, the landscape is a bit different. There are only a handful of complete frameworks on the

market:

Eclipse RCP, the platform on which the Eclipse IDE has been built

Netbeans RCP, the platform on which the Netbeans IDE has been built

These frameworks are complete platforms, which have proven themselves through their respective IDE’s to showcase their

possibilities.

However, these possibilities come at a steep cost. With Eclipse RCP, for example, you’ll be straying of the known Swing path and

enter the world of SWT, and both don’t play well together. With Netbeans RCP, you’re mostly confined to the Netbeans IDE to

do your development (that is, if you want to develop quickly).

Both platforms can be quite cumbersome and come with a complete baggage package bundled. It’s complexity can be

overwhelming for a standard Swing developer and does not always promote effective, good programming style.

The Spring Rich Client framework does not promote itself as a complete platform. Instead, it provides developer a clear and easy

way to build enterprise-class applications, without straying too far from the standard, well-known path, whilst ensuring enough

flexibility to cope with the difficult issues coupled to the development of enterprise applications. Built on the strong foundations

of the Spring Framework, it combines the best practices advocated by the Spring Framework with an component-based

abstraction on top of Swing to ease development of Swing rich client applications.

This introduction will try to cover as much ground as possible to let you hit the ground running when starting with a Spring Rich

Client application.

GETTING OUR HANDS DIRTY

We’ll start off the introduction with a concise example of a Spring Rich Client application.

As with all Spring applications, Spring beans need to be configured within the container. Currently in Spring Rich Client, only XML

based configuration has been tested, although with the new Spring 2.5 release and it’s component-scanning abilities, it should

be possible to narrow your configuration down to the essentials. This is something that will possibly be improved in future

releases.

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-

beans-2.0.xsd">

<bean id="application" class="org.springframework.richclient.application.Application">

Page 6: Spring Rich Client

6

<constructor-arg index="0" ref="applicationDescriptor" />

<constructor-arg index="1" ref="lifecycleAdvisor" />

</bean>

<bean id="lifecycleAdvisor"

class="org.springframework.richclient.samples.simple.app.SimpleLifecycleAdvisor">

<property name="windowCommandBarDefinitions"

value="org/springframework/richclient/samples/simple/ui/commands-context.xml" />

<property name="startingPageId" value="initialView" />

<property name="windowCommandManagerBeanName" value="windowCommandManager" />

<property name="menubarBeanName" value="menuBar" />

<property name="toolbarBeanName" value="toolBar" />

</bean>

<bean id="initialView"

class="org.springframework.richclient.application.support.DefaultViewDescriptor">

<property name="viewClass" value="org.springframework.richclient.samples.simple.ui.InitialView" />

<property name="viewProperties">

<map>

<entry key="firstMessage" value="firstMessage.text" />

<entry key="descriptionTextPath"

value="org/springframework/richclient/samples/simple/ui/initialViewText.html" />

</map>

</property>

</bean>

<bean id="serviceLocator"

class="org.springframework.richclient.application.ApplicationServicesLocator">

<property name="applicationServices" ref="applicationServices" />

</bean>

<bean id="applicationServices"

class="org.springframework.richclient.application.support.DefaultApplicationServices" />

<bean id="applicationEventMulticaster"

class="org.springframework.context.event.SimpleApplicationEventMulticaster" />

<bean id="applicationDescriptor"

class="org.springframework.richclient.application.support.DefaultApplicationDescriptor">

<property name="version" value="1.0" />

</bean>

<bean id="applicationObjectConfigurer" depends-on="serviceLocator"

class="org.springframework.richclient.application.config.DefaultApplicationObjectConfigurer">

</bean>

<bean id="lookAndFeelConfigurer"

class="org.springframework.richclient.application.config.JGoodiesLooksConfigurer">

<property name="popupDropShadowEnabled" value="false" />

<property name="theme">

<bean class="com.jgoodies.looks.plastic.theme.ExperienceBlue" />

</property>

</bean>

<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">

<property name="basenames">

<list>

<value>org.springframework.richclient.samples.simple.ui.messages</value>

<value>org.springframework.richclient.application.messages</value>

</list>

</property>

</bean>

<bean id="imageResourcesFactory" class="org.springframework.context.support.ResourceMapFactoryBean">

<property name="locations">

<list>

<value>classpath:org/springframework/richclient/image/images.properties</value>

<value>classpath:org/springframework/richclient/samples/simple/ui/images.properties</value>

</list>

</property>

</bean>

Page 7: Spring Rich Client

7

<bean id="imageSource" class="org.springframework.richclient.image.DefaultImageSource">

<constructor-arg index="0" ref="imageResourcesFactory" />

<property name="brokenImageIndicator"

Value="/org/springframework/richclient/images/alert/error_obj.gif" />

</bean>

<bean id="formComponentInterceptorFactory"

class="org.springframework.richclient.form.builder.support.ChainedInterceptorFactory">

<property name="interceptorFactories">

<list>

<bean

class="org.springframework.richclient.form.builder.support.ColorValidationInterceptorFactory">

<property name="errorColor" value="255,245,245" />

</bean>

<bean class="org.springframework.richclient.form.builder.support.OverlayValidationInterceptorFactory" />

<bean class="org.springframework.richclient.text.TextComponentPopupInterceptorFactory" />

<bean class="org.springframework.richclient.list.ComboBoxAutoCompletionInterceptorFactory" />

</list>

</property>

</bean>

<bean id="rulesSource"

class="org.springframework.richclient.samples.simple.domain.SimpleValidationRulesSource" />

<bean id="conversionService"

class="org.springframework.richclient.application.DefaultConversionServiceFactoryBean">

<property name="formatterFactory">

<bean class="org.springframework.richclient.samples.simple.ui.SimpleAppFormatterFactory" />

</property>

</bean>

</beans>

This sample is in fact on of the Spring Rich Client samples included in the sources, some minor adjustments not taken into

account. When running the simple project, the output will look something like this:

Page 8: Spring Rich Client

8

THE SPRING RICH CLIENT FRAMEWORK, A QUICK DISSECTION

ANATOMY OF A RICH CLIENT APPLICATION

A Swing rich client in its basic form is quite simple:

An application consists of a application windows, which in most cases contains:

The main content pane

One or more navigational structures, such as a menu bar or a toolbar

A statusbar to show all sorts of messages to the user

These can be augmented with global search fields, help functions or other components that an application might require.

APPLICATION WINDOWS

An application window is the foundation on which every rich client is built. Without an application window, you don’t have a

place to put the components that make up your screens.

In Spring Rich Client, the application window is represented by the ApplicationWindow class. The application window builds the

visual window component and shows it to the user.

Application windows are not created individually in Spring Rich Client. A factory pattern, called ApplicationWindowFactory

handles instantiation of an application window. This way windows are created in a clear and uniform way throughout the

application, without bothering the developer.

In our hands-dirty example, you will not find this factory. Spring Rich Client does not need to have a ApplicationWindowFactory

defined. If it is not configured, Spring Rich Client will fall back on a default implementation within the framework, consisting of a

simple view area with a menubar, statusbar and toolbar.

You can, however, define your own application factory. To do this, you just need to implement an ApplicationWindowFactory

and define a bean in your application context with your new class. Spring Rich Client will pick up the class and use it instead of

the default implementation.

The above mechanism, also known as service location, is widely used throughout Spring Rich Client, to facilitate configuration

for the developers.

Navigation

Content

Status bar

Page 9: Spring Rich Client

9

You can for example create an application window factory that leaves out the toolbar, but creates a outlook-like bar or task-

pane oriented navigation on the left side instead.

VIEWS

Application windows are containers for views. A view can be seen as an individual window representing a specific state within

the application.

Views are the main view area of your applications and will contain the bulk of the user interaction, unless you’ve chosen for a

dialog-based approach. You can for example have a flow of views representing a process within your application, or a view

containing a table which, when double-clicked, shows a detail of the selected item.

In our first example, a view shows a HTML file together with a message from a resource bundle.

Implementing a view is quite simple. When viewing the example view, you’ll see this:

public class InitialView extends AbstractView {

// omitted for brevity...

/**

* Create the actual UI control for this view. It will be placed into the window according to the layout of

* the page holding this view.

*/

protected JComponent createControl() {

// In this view, we're just going to use standard Swing to place a

// few controls.

// The location of the text to display has been set as a Resource in the

// property descriptionTextPath. So, use that resource to obtain a URL

// and set that as the page for the text pane.

JTextPane textPane = new JTextPane();

JScrollPane spDescription = getComponentFactory().createScrollPane(textPane);

try {

textPane.setPage(getDescriptionTextPath().getURL());

}

catch (IOException e) {

throw new RuntimeException("Unable to load description URL", e);

}

JLabel lblMessage = getComponentFactory().createLabel(getFirstMessage());

lblMessage.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));

JPanel panel = getComponentFactory().createPanel(new BorderLayout());

panel.add(spDescription);

panel.add(lblMessage, BorderLayout.SOUTH);

return panel;

}

}

AbstractView mandates that you implement the createControl method. This method can deliver any JComponent to show its

contents. In most cases, this will be some sort of container or panel.

COMMANDS

The entire menu bar system (and derived navigational structures) are command based. In essence, this means you’ll never make

a JMenu or JMenuItem manually again. Ever. Simply put, you’ll create a command, which contains code that needs to be

executed (for example, change a view or print the current selected item). Spring Rich Client will handle the creation of the visual

components and couple the command behavior to the visual component’s behavior.

Page 10: Spring Rich Client

10

In Spring Rich Client, the command context is a separate context that needs to be defined in the main application context

lifecycle.

In our example, the command context looks like this:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-

beans-2.0.xsd">

<bean id="windowCommandManager"

class="org.springframework.richclient.application.support.ApplicationWindowCommandManager">

</bean>

<bean id="menuBar" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list>

<ref bean="fileMenu" />

<ref bean="windowMenu" />

<ref bean="helpMenu" />

</list>

</property>

</bean>

<bean id="toolBar" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list/>

</property>

</bean>

<bean id="fileMenu" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list>

<bean class="org.springframework.richclient.command.support.ExitCommand" />

</list>

</property>

</bean>

<bean id="windowMenu" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list>

<bean class="org.springframework.richclient.command.support.NewWindowCommand" />

<value>separator</value>

<bean class="org.springframework.richclient.command.support.ShowViewMenu" />

</list>

</property>

</bean>

<bean id="helpMenu" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list>

<ref bean="helpContentsCommand" />

<value>separator</value>

<ref bean="aboutCommand" />

</list>

</property>

</bean>

<bean id="helpContentsCommand" class="org.springframework.richclient.command.support.HelpContentsCommand">

<property name="helpSetPath" value="help/simple.hs" />

</bean>

<bean id="aboutCommand" class="org.springframework.richclient.command.support.AboutCommand" />

</beans>

As you can see, no Swing-like components are being used to create the menu.

Page 11: Spring Rich Client

11

MESSAGES AND ICONS

Internationalizing your application can be quite an important part of your work, and sometimes also one of the most time-

consuming too.

Spring Rich Client supports internationalization through resource bundles defined in the application context (see the

messageSource bean). For example, Spring Rich Client provides a mechanism to set the title of your application through the

application descriptor. If you set the key applicationDescriptor.title to some value, that value will show up as the title of your

application.

Messages are found through MessageSource implementation, for which the resource bundle implementation is perhaps the

most widely used.

The same is true for application icons and images in general. The key applicationDescriptor.icon sets the application’s icon. Like

messages, the images are found through a ImageSource, which contains property files with the keys and the images that go with

the keys, and the location where the images can be found (in the jar for example, or somewhere else).

OTHER COMPONENTS

Spring Rich Client also contains other components to make rich client programming easier. A form framework that handles easy

form creation, undo functionality and validation. Exception handling to show visually attractive messages when your application

goes down the drain. A wizard framework to make data input for the not-so-tech-savvy users simpler and much more.

We’ll discuss these into detail later.

Page 12: Spring Rich Client

12

APPLICATIONS

APPLICATION LIFECYCLE

The lifecycle in a Spring Rich Client application is what it says it is. It’s the behavior of the application through the time when it is

running. What the application should do at startup, what it needs to show, how it should behave when it’s being closed… these

are all aspects of the application lifecycle.

In Spring Rich Client, an ApplicationLifecycleAdvisor manages the lifecycle of the application. In our example, the lifecycle

advisor handles the creation of the command context, what view should be showed initially, what command bean should be

used for the menu, etc…

HOOKING INTO A LIFECYCLE

In the application lifecycle you can intervene on points within startup, shutdown or other events such as opening a window.

These hooks are present in the ApplicationLifecycleAdvisor and can for example be used to ask data needed before startup.

You could also stop an application from exiting (by asking a confirmation for example).

APPLICATION

An application in Spring Rich Client is comprised of 2 things: a lifecycle advisor (how it should behave) and a descriptor (what it

should do).

The Application class is also the class that starts the actual application: it calls the startup hooks, and shows the initial window

and view.

EXAMPLE: ADDING LOGIN FUNCTIONALITY TO THE APPLICATION

Logging into an application is quite common behavior. You’ll probably want to show the login dialog before showing your

application window (you might want to personalize the application window based on who has logged in).

In Spring Rich Client, this means after the commands have been created (which is one step before the window gets created and

showed). In this example, we’ll let a imaginary LoginHandler class check whether we can login or not. For the record, basic login

functionality has been included into Spring Rich Client, but this would bring us out of scope for this example. We’ll discuss the

security features later.

Assuming our handler will show a login screen and handle the login logic, we only need to hook it into the application lifecycle

advisor:

public class LoginLifecycleAdvisor extends DefaultApplicationLifecycleAdvisor

{

private LoginHandler handler;

public void setHandler(final LoginHandler handler)

{

this.handler = handler;

}

public void onCommandsCreated(final ApplicationWindow window)

{

super.onCommandsCreated(window);

handler.doLogin();

}

}

Page 13: Spring Rich Client

13

As you can see, this is quite easy. Off course you’d want to know when a user is already logged in and just creates a new

application window (which also triggers this method), so it doesn’t show this login window again, something like holding a

security context within your application.

OTHER POSSIBLE USES

Lifecycle advisor subclassing can also be handy in other aspects. Any Spring Rich Client application knows which lifecycle advisor

is used for its execution. You can ask this by calling

Application.instance().getLifecycleAdvisor()

You could for example create an application window factory that delivers different application window applications based on

which lifecycle advisor was used:

public class DefaultApplicationWindowFactory implements ApplicationWindowFactory

{

private static final Log logger = LogFactory.getLog(DefaultApplicationWindowFactory.class);

public ApplicationWindow createApplicationWindow()

{

ApplicationLifecycleAdvisor lifecycleAdvisor = Application.instance().getLifecycleAdvisor();

if (lifecycleAdvisor instanceof OutlookNavigatorApplicationLifecycleAdvisor)

{

return OutlookNavigatorApplicationWindowFactory.create();

}

else if (lifecycleAdvisor instanceof TaskPaneNavigatorApplicationLifecycleAdvisor)

{

return TaskPaneNavigatorApplicationWindowFactory.create();

}

return new DefaultApplicationWindow();

}

static class TaskPaneNavigatorApplicationWindowFactory

{

public static ApplicationWindow create(boolean onlyOneExpanded)

{

...

}

}

static class OutlookNavigatorApplicationWindowFactory

{

public static ApplicationWindow create()

{

...

}

}

}

PLAYING WITH THE STATUSBAR

Changing the status bar can be done by calling the statusbar through the lifecycle advisor:

Application.instance().getLifecycleAdvisor().getStatusBar();

Again, if you’re creating your own lifecycle advisor, you’re able to override this method and perhaps supply your own status bar

implementation.

The standard status bar supports:

Displaying messages, normal messages as well as errors

Containing a progress monitor to track long-running processing (and cancelling them)

Your status bar may hold a clock, the current logged in user, the connected server, …

Page 14: Spring Rich Client

14

ELABORATE EXAMPLE: ADDING MULTIPLE TOOLBARS TO THE SCREEN

In this example we’ll add multiple toolbar support in Spring Rich Client.

Management of the standard toolbar is done in three places:

The application lifecycle advisor manages the bean name of the toolbar

The application window puts the actual toolbar on screen

The application window configurer manages the visibility of the toolbar

For this example, I’ll create the first two (and an factory for the application window).

First, we’re going to extends the default lifecycle advisor with the ability to enter multiple toolbars, while maintaining backwards

compatibility.

public class CustomApplicationLifecycleAdvisor extends DefaultApplicationLifecycleAdvisor

{

private List toolBarBeanNames;

public void setToolBarBeanNames(List toolBarBeanNames)

{

this.toolBarBeanNames = toolBarBeanNames;

}

public void setToolbarBeanName(String toolbarBeanName)

{

toolBarBeanNames = new ArrayList();

toolBarBeanNames.add(toolbarBeanName);

}

public CommandGroup[] getToolBarCommandGroups()

{

if(toolBarBeanNames == null || toolBarBeanNames.size() == 0)

{

return new CommandGroup[] { new CommandGroup() };

}

else

{

CommandGroup[] groups = new CommandGroup[toolBarBeanNames.size()];

for (int i = 0; i < toolBarBeanNames.size(); i++)

{

groups[i] = getCommandGroup(toolBarBeanNames.get(i).toString());

}

return groups;

}

}

}

Next, we’re going to create an application window that is able to show the multiple toolbars

public class MultipleToolbarApplicationWindow extends DefaultApplicationWindow

{

private CommandGroup[] toolBarCommandGroups;

protected void init()

{

super.init();

if(getAdvisor() instanceof CustomApplicationLifecycleAdvisor)

{

this.toolBarCommandGroups = ((CustomApplicationLifecycleAdvisor) getAdvisor()).getToolBarCommandGroups();

}

else

{

this.toolBarCommandGroups = new CommandGroup[] {getAdvisor().getToolBarCommandGroup()};

}

}

protected JComponent createToolBarControl() {

Page 15: Spring Rich Client

15

JPanel panel = new JPanel();

panel.setLayout(new GridLayout(toolBarCommandGroups.length, 1));

for (int i = 0; i < toolBarCommandGroups.length; i++)

{

CommandGroup toolBarCommandGroup = toolBarCommandGroups[i];

JComponent toolBar = toolBarCommandGroup.createToolBar();

toolBarCommandGroup.setVisible( getWindowConfigurer().getShowToolBar() );

panel.add(toolBar);

}

return panel;

}

}Finally, a factory to create the new application window

public class MultipleToolbarApplicationWindowFactory implements ApplicationWindowFactory

{

public ApplicationWindow createApplicationWindow()

{

return new MultipleToolbarApplicationWindow();

}

}

Now, to configure the multiple toolbars, here are the beans you need to configure. First the application lifecycle in the main

application context

<bean id="lifecycleAdvisor" class="org.springframework.richclient.test.CustomApplicationLifecycleAdvisor">

<property name="windowCommandBarDefinitions" value="ui/commands-context.xml" />

<property name="startingPageId" value="initialView" />

<property name="windowCommandManagerBeanName" value="windowCommandManager" />

<property name="menubarBeanName" value="menuBar" />

<property name="toolBarBeanNames">

<list>

<value>toolBar</value>

<value>toolBarCopy</value>

</list>

</property>

</bean>

You also need to define the application window factory in your context, so it’ll use that one.

<bean id="appWindowFactory" class="org.springframework.richclient.test.MultipleToolbarApplicationWindowFactory"/>

And then the toolbars in the command context

<bean id="toolBar" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list>

<value>newCommand</value>

<value>saveCommand</value>

<value>printCommand</value>

<value>separator</value>

<value>propertiesCommand</value>

</list>

</property>

</bean>

<bean id="toolBarCopy" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list>

<value>newCommand</value>

<value>saveCommand</value>

<value>printCommand</value>

<value>separator</value>

<value>propertiesCommand</value>

</list>

</property>

</bean>

Page 16: Spring Rich Client

16

That’s all there is to it. I’ve started from a project created with the maven archetype, so you can try it out if you want. The result

will be something like this

Page 17: Spring Rich Client

17

COMMANDS

WHY THE ABSTRACTION

Creating an abstraction over an action-based operation is quite logical when you think about it. You have behavior, which can be

visually represented in different ways in Swing: a menu, a button or even just a clickable label.

Managing this behavior can be cumbersome if you need to maintain it, especially when certain behavior is to be represented

many times and in different ways.

HOW TO CREATE A SIMPLE COMMAND

Let’s make a simple command that shows a messagebox when executed:

public class MessageBoxCommand extends ActionCommand

{

protected void doExecuteCommand()

{

JOptionPane.showMessageDialog(Application.instance().getActiveWindow().getControl(), "Hello world!");

}

}

Creating a new command can be done by extending ActionCommand. This class mandates that you implement the

doExecuteCommand method, which represents the behavior of the command.

CONFIGURE A PROGRAMMATICALLY CREATED COMMAND

Programmatically created commands need to be configured, since Spring Rich Client heavily relies on the Spring container to do

the configuring. So when you create command objects in code, you’ll have to do the configuring yourself.

This can be done by calling the command configurer through the service locator in Spring Rich Client:

MessageBoxCommand command = new MessageBoxCommand();

commandConfigurer = (CommandConfigurer) ApplicationServicesLocator.services().getService(

CommandConfigurer.class);

commandConfigurer.configure(command);

After the command has been configured, it can be used to create components.

CHANGING THE COMMAND TO A VISUAL COMPONENT

After a command is configured, it can be used to create visual components. These can be:

Menu items

Buttons

Creating a visual component from a command is easy. Every command has methods to create visual components, with the

command’s behavior coupled to it.

For buttons this is

command.createButton(...);

For menu items this is

command.createMenuItem(...);

Both methods have various parameter configurations, for more information on which to use in your scenario I refer to the

JavaDoc documentation.

Page 18: Spring Rich Client

18

COMMAND INTERNATIONALIZATION AND IMAGES

A button needs a text label, perhaps an icon. We’ll now show you how you can put messages on your commands and link icons

to them.

When configuring a command, Spring Rich Client sets a “face descriptor” on the command. This face descriptor manages how

the command looks like. A face descriptor is put on the command by utilizing the id of the command. In a Spring configured

command, this is the id of the Spring bean. Programmatically created commands need to set their command ids themselves. If

you don’t set the command’s id, Spring Rich Client will fallback to the camel-cased class name of the command class. In our

example, this will be the case, as we haven’t set the id.

The label of the command, and other text related messages for that matter, is found in the message source configured for the

Spring Rich Client application. To find the label of a command, Spring Rich client searches for:

[face descriptor id].label = The label of the command

Similarly, you can put a caption on a command. In the case of a JButton, this will translate itself into a tooltip message:

[face descriptor id].caption = The caption of the command

Commands can also have icons coupled to them. Spring Rich Client will look for the message keys and images in the image

source configured for the application. To find the icon for a command, Spring Rich client will search for:

[face descriptor id].icon = some_icon.png

GROUPING COMMANDS

Commands can also be groups. Command groups in Spring Rich Clients are composite commands. They can be used to create

menus, button stacks and other aggregate components.

Command groups behave similarly to commands (command groups are subclasses of commands), so the configuration works in

the same way. They can also be configured in the Spring context, as well as programmatically. However, in the latter case, the

same rules as commands apply.

Adding commands to a command group is easy, just call the add method.

Creating a visual component of a command group works the same way as commands. Spring Rich Client provides functionality

for:

Menus

Vertical button stacks

Horizontal button bars

Toolbars

Popup menus

See the JavaDoc documentation for more information on the usage of these creation methods. With some effort, you can even

make trees, taskpanes or outlook-bar style grouping.

BUILDING A COMMAND GROUP IN A SPRING CONTEXT

Given our hands-dirty example, we’ll look at a command group configured there:

<bean id="windowMenu" class="org.springframework.richclient.command.CommandGroupFactoryBean">

<property name="members">

<list>

<bean class="org.springframework.richclient.command.support.NewWindowCommand" />

<value>separator</value>

Page 19: Spring Rich Client

19

<bean class="org.springframework.richclient.command.support.ShowViewMenu" />

</list>

</property>

</bean>

As you can see, creating a command group is straightforward. You create a command group through a factory bean and set the

members.

You may have noticed the “separator” value in the members. Spring Rich Client has facilitated adding separators to menus and

toolbars by adding this as a shortcut.

Other shortcuts that can be used as values are:

glue

Add a glue between commands

command:xyz

Add a command with id xyz to the group. You could use this instead of bean references

group:abc

Add a command group with id abc to the group. You could use this instead of bean references

Not only commands or shortcuts can be added. Also just ordinary JComponent can be added to command groups. This way you

can easily add a textfield to a toolbar.

These command groups can then be referenced for example in the lifecycle advisor to be used as the application’s menu bar,

tool bar or other navigation you might have implemented.

GETTING A SPRING CONFIGURED COMMAND IN CODE

Getting a command that you have configured in a Spring context can be done by searching for it based on its id:

Application.instance().getActiveWindow().getCommandManager().getCommand(commandId);

This will return the command if it can find a command with that id, or null otherwise. You don’t need to configure this command

after you’ve found it, Spring Rich Client has already handled this for you.

Page 20: Spring Rich Client

20

VIEWS

WHAT IS A VIEW

A view is a visual representation of concepts within your application. Everything you show in the main application window is

contained within a view.

There can be multiple views on an application page, but only one view is visible at a time. View instances encapsulate the

creation of and access to the visual presentation of the underlying control. A view's descriptor, which is effectively a singleton,

can be asked to instantiate new instances of a single view for display within an application with multiple windows. In other

words, a single view instance is never shared between windows.

VIEW DESCRIPTORS

Every view has a view descriptor. It’s metadata about a view; a view descriptor is effectively a singleton view definition. A

descriptor also acts as a factory which produces new instances of a given view when requested, typically by a requesting

application page. A view descriptor can also produce a command which launches a view for display on the page within the

current active window.

View descriptors produce the page components (in this case views) that will be shown to the users.

CREATING VIEWS

Creating a new view is done through subclassing the AbstractView class. This class mandates you to implement one method:

createControl. In our example, the initial view class looks like this:

public class InitialView extends AbstractView

{

// omitted for brevity

protected JComponent createControl()

{

// In this view, we're just going to use standard Swing to place a

// few controls.

// The location of the text to display has been set as a Resource in the

// property descriptionTextPath. So, use that resource to obtain a URL

// and set that as the page for the text pane.

JTextPane textPane = new JTextPane();

JScrollPane spDescription = getComponentFactory().createScrollPane(textPane);

try {

textPane.setPage(getDescriptionTextPath().getURL());

}

catch (IOException e) {

throw new RuntimeException("Unable to load description URL", e);

}

JLabel lblMessage = getComponentFactory().createLabel(getFirstMessage());

lblMessage.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));

JPanel panel = getComponentFactory().createPanel(new BorderLayout());

panel.add(spDescription);

panel.add(lblMessage, BorderLayout.SOUTH);

return panel;

}

}

Page 21: Spring Rich Client

21

CREATING A VIEW DESCRIPTOR FOR A VIEW

To show a view on screen, you need to create a view descriptor. For the initial view in the example, the following view

descriptor was used:

<bean id="initialView" class="org.springframework.richclient.application.support.DefaultViewDescriptor">

<property name="viewClass" value="org.springframework.richclient.samples.simple.ui.InitialView" />

<property name="viewProperties">

<map>

<entry key="firstMessage" value="firstMessage.text" />

<entry key="descriptionTextPath"

value="org/springframework/richclient/samples/simple/ui/initialViewText.html" />

</map>

</property>

</bean>

This is the standard view descriptor definition. You need to give the view class to be used to show the component. Additionally,

you can set the properties on an instance of that view by populating the viewProperties map. These properties have to

correspond to standard JavaBeans property setters. In this case, the view class has a setFirstMessage and a

setDescriptionTextPath method.

SHOWING THE VIEW IN THE APPLICATION

Setting the current view is done by using a ShowViewCommand. This command sets the view of the application window in which

the command is located.

To create such a command, use the following bean definition:

<bean id="initialCommand" class="org.springframework.richclient.command.support.ShowViewCommand">

<property name="viewDescriptor" ref=”initialView” />

</bean>

You can now use this command in your menu, or create a button in another view to switch to the defined view.

Page 22: Spring Rich Client

22

BINDING AND FORMS

FORMMODELS AND VALUEMODELS

WHAT IS A VALUEMODEL

A valuemodel provides a way for listening for changes on a property. In its simplest form, it is a property listener wrapper

around a particular property. Its purpose is to track change, so that secondary functionality such as undo functionality and

validation can be provided.

WHAT IS A FORMMODEL

A formmodel is a wrapper around a particular instance of an object. In essence, it is a consisting of valuemodels for the various

properties of an object. It handles the overall state of the object.

THE DEFAULT FORMMODEL

The default formmodel in Spring Rich Client handles more than just the overall state of the object by managing its valueobject. It

also provides :

Buffering of values, effectively providing undo functionality

Dirty tracking

Validation possibilities through validators

Set certain properties of an object to read-only, even if they have setters

Creating a formmodel of any given object can be done through this:

MyObject object = new MyObject();

ValidatingFormModel model = new DefaultFormModel(object);

From then on, you can set the values of the object through the valueobject.

model.getValueModel(“someProperty”).setValue(“xyz”)

Valuemodels in formmodels are created on-demand. After creating a formmodel on an object, no valuemodels are present until

you start calling for them. Spring Rich Client will then make these on-demand.

BUFFERING

Buffering provides the necessary plumbing needed for undo functionality. When changing values of property, a buffered

valuemodel will still hold the old values and can revert to these if necessary.

MyObject object = new MyObject();

object.setXyz(“xyz”);

ValidatingFormModel model = new DefaultFormModel(object);

model.getValueModel(“xyz”).setValue(“abc”);

// object hasn’t changed, object.getXyz() will return xyz

model.commit();

// object has changed, object.getXyz() will return abc

Calling revert() before a commit on a formmodel will return all properties to their original values. Individual valuemodels can be

reverted too by calling revert() on them.

Page 23: Spring Rich Client

23

READ-ONLY MANIPULATION

An entire formmodel can be set to be read-only by using the setReadOnly(…) method.

Setting individual properties read-only is a little bit more complicated. Out of the box, Spring Rich Client will inspect the object

and determine whether a property is read-only, based on the existence of a setter method for that property.

However, there might be cases where you’d want to deliberately change the read-only behavior of a property, even if it has a

setter. The fact whether a property is set as read-only is held by field metadata.

For any given property you can ask the formmodel for the field metadata by calling

FieldMetaData meta = model.getFieldMetaData(“xyz”);

Through this field metadata, you can set the read-only property of a property

meta.setReadOnly(true);

Obviously, trying to set a property that has no setter to writable will cause an exception when the valuemodels are committed

(and the respective setters are called).

VALIDATION

The default form model also contains functionality for validating the enclosed values. The validation is done through Spring Rich

Client’s own validation subsystem by utilizing validators. We’ll discuss the details of these validators in detail later.

When a property is changed, the validator will be called to check whether the object is still in a consistent state. If not, the

validator will produce validation errors, which then can be showed to the user through various means.

Setting a validator on a formmodel is done through

model.setValidator(someValidator)

After that, the validation is automatically turned on. If you needed to, you could turn it off by calling

model.setValidating(false)

A model can be validated at any time. A model is aware whether has validation errors, and if so, contains a collection of these.

For more information on this, I refer to the JavaDocs on ValidatingFormModel and DefaultFormModel

CREATING FORMMODELS

To create a formmodel, Spring Rich Client has provided a factory class that can create various formmodels. This class is called

FormModelHelper.

For example, if you want to create a formmodel of an object, the simplest way would be:

FormModelHelper.createFormModel(new SomeObject(), “formModelId”);

With the FormModelHelper, you can create:

Default formmodels (with validation and buffering)

Unbuffered formmodels

Child formmodels of existing formmodels

Formmodels, at this time, are object based. To create a formmodel, you need to be able to create an object of the class to be

utilized by the formmodel.

Page 24: Spring Rich Client

24

There are implementations on the way to make these class-based, but these are still in development and shaky at best at the

moment.

BINDING

WHAT IS BINDING

Binding in Spring Rich Client encompasses the connection between a visual component and the state of a certain property.

HOW DOES BINDING WORK AND WHAT DOES IT DO

Binding is done through a valuemodel. A binding covers only one property at a time, most of the time (a binding could be done

by aggregating different valuemodels, but that’s way out of scope for this introduction).

A binding will transfer all property changes to the object behind it, and vice versa. It’s bound to a particular formmodel and

property (and therefore, a valuemodel), and is responsible for creating the visual component

BINDERS

Binders a factories for bindings. Generally, for each sort of binding you’ll use in your application, you’ll have one (or more, if

there are specific variants of certain bindings that may be occurring).

BINDER EXAMPLES

In Spring Rich Client, a number of binders have been implemented out of the box.

TextComponentBinder: can handle text-type variables like strings

CheckBoxBinder: can handle Boolean-type variables

ListBinder: can handle lists

And many more…

For Java 5+ there is even a binder available for enums, which visually is represented by a combobox.

CREATING YOUR OWN BINDER

Say we want to create a binder for JodaTime’s classes. Java’s standard date classes are bad to work with, so this example may

even be somewhat useful.

We start of by creating a the binder. A binder is able to bind any JComponent to a value, so we’ll use SwingX’s JXDatePicker class

to visually represent the date.

public class JodaTimeDateTimeBinding extends CustomBinding implements PropertyChangeListener

{

private final JXDatePicker datePicker;

private final boolean readOnly;

private boolean isSettingText = false;

public JodaTimeDateTimeBinding(FormModel model, String path, JXDatePicker datePicker, boolean readOnly)

{

super(model, path, DateTime.class);

this.datePicker = datePicker;

this.readOnly = readOnly;

}

@Override

protected void valueModelChanged(Object newValue)

{

isSettingText = true;

Page 25: Spring Rich Client

25

setDatePickerValue((DateTime) newValue);

readOnlyChanged();

isSettingText = false;

}

private void setDatePickerValue(DateTime dateTime)

{

if (dateTime == null)

{

datePicker.setDate(null);

}

else

{

datePicker.setDate(dateTime.toDate());

}

}

@Override

protected JComponent doBindControl()

{

setDatePickerValue((DateTime) getValue());

datePicker.getEditor().addPropertyChangeListener("value", this);

return datePicker;

}

public void propertyChange(PropertyChangeEvent evt)

{

if (!isSettingText && !isReadOnly())

controlValueChanged(new DateTime(datePicker.getDate()));

}

@Override

protected void readOnlyChanged()

{

datePicker.setEditable(isEnabled() && !this.readOnly && !isReadOnly());

}

@Override

protected void enabledChanged()

{

datePicker.setEnabled(isEnabled());

readOnlyChanged();

}

}

As you can see the class does the 2 way binding. This part

@Override

protected void valueModelChanged(Object newValue)

{

isSettingText = true;

setDatePickerValue((DateTime) newValue);

readOnlyChanged();

isSettingText = false;

}

handles the propagation of changes in the formmodel to the actual component, whereas the property change listener (which in

this case is the binder itself, handled by

public void propertyChange(PropertyChangeEvent evt)

{

if (!isSettingText && !isReadOnly())

controlValueChanged(new DateTime(datePicker.getDate()));

}

The isSettingText flag is there to prevent cyclic calls (formmodel changes component, which change the formmodel, which

changes, …).

Page 26: Spring Rich Client

26

Binding the control to the value is done through the doBindControl() method. This method is called to wire the component to

the binding and prepares all the plumbing to make the binding work.

Creating the binder is most of the time the easiest job of the two.

public class JodaTimeDateTimeBinder extends org.springframework.richclient.form.binding.support.AbstractBinder

{

private boolean defaultsSet = false;

private boolean readOnly = false;

public JodaTimeDateTimeBinder()

{

super(DateTime.class);

}

public void setReadOnly(boolean readOnly)

{

this.readOnly = readOnly;

}

@SuppressWarnings("unchecked")

protected JComponent createControl(Map context)

{

JXDatePicker datePicker = new JXDatePicker();

datePicker.setEditor(new DateTextField());

return datePicker;

}

@SuppressWarnings("unchecked")

protected Binding doBind(JComponent control, FormModel formModel, String formPropertyPath, Map context)

{

if (!defaultsSet)

{

Map<Object, Object> defaults = UIManager.getDefaults();

defaults.put("JXDatePicker.longFormat", "EEE dd/MM/yyyy");

defaults.put("JXDatePicker.mediumFormat", "dd/MM/yyyy");

defaults.put("JXDatePicker.shortFormat", "dd/MM");

defaultsSet = true;

}

return new JodaTimeDateTimeBinding(formModel, formPropertyPath, ((JXDatePicker) control), this.readOnly);

}

}

The createControl() method creates the control that is to be used in bindings. Every time a binding is done, a new control will be

created through this method.

The actual binding is done through the doBind(). It will create a binding, do some specific behavior in some case (here we’re

manipulating some UI properties to alter the JXDatePicker’s appearance.

FORMS

Now that we have covered the formmodels and the binding, we can now cover the combination of the both.

WHAT IS A FORM

Whereas a binding covers a single property, a form covers an entire object. It can contain many bindings, backed up by a

formmodel that wrapped the form’s object.

CREATING A FORM

Forms are created for a specific purpose and specific objects. Say we have the following object:

public class TestObject

Page 27: Spring Rich Client

27

{

private String field1;

private String field2;

public String getField1()

{

return field1;

}

public void setField1(String field1)

{

this.field1 = field1;

}

public String getField2()

{

return field2;

}

public void setField2(String field2)

{

this.field2 = field2;

}

}

If we want to make a form for this, we’ll might be using something like this

public class TestForm extends AbstractForm

{

public TestForm()

{

super(FormModelHelper.createFormModel(new TestObject(), "testForm"));

}

protected JComponent createFormControl()

{

JPanel content = new JPanel();

content.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

content.setLayout(new FormLayout(

new ColumnSpec[]

{

FormFactory.DEFAULT_COLSPEC,

FormFactory.LABEL_COMPONENT_GAP_COLSPEC,

FormFactory.DEFAULT_COLSPEC

},

new RowSpec[]

{

FormFactory.DEFAULT_ROWSPEC,

FormFactory.LINE_GAP_ROWSPEC,

FormFactory.DEFAULT_ROWSPEC

}

));

TextComponentBinder binder = new TextComponentBinder();

Map map = new HashMap();

content.add(new JLabel("Field 1"), new CellConstraints(1, 1));

content.add(binder.bind(getFormModel(), "field1", map).getControl(), new CellConstraints(3, 1));

content.add(new JLabel("Field 2"), new CellConstraints(1, 3));

content.add(binder.bind(getFormModel(), "field2", map).getControl(), new CellConstraints(3, 3));

return content;

}

}

This will result in a panel with 2 text fields next to each other, that represent the 2 fields of the object. This form can then be

used to show in a view or a dialog. Currently, there is no default view descriptor for forms, since these are mostly contained in

views in which they only make up a part of the screen (for example, in combination with a table).

Page 28: Spring Rich Client

28

FORM BUILDERS

As shown in the example above, forms can be created by using binders and bindings directly. However, for more elaborate form,

this method is not really usable (or readable for that matter).

To tackle this problem, Spring Rich Client has created form builders. Form builders make form creation a lot easier by providing

simple addition of properties, labels and other component to forms. Form builders use the binding factory facilities built into

Spring Rich Client.

The binding factory system can set default binders for certain types, so that you don’t need to worry how something should

look. It can also provide aliases for binders defined in the context, so that you can use these swiftly.

Building the same form with a form builder would result in

public class TestForm extends AbstractForm

{

public TestForm()

{

super(FormModelHelper.createFormModel(new TestObject(), "testForm"));

}

protected JComponent createFormControl()

{

TableFormBuilder builder = new TableFormBuilder(getBindingFactory());

builder.add("field1");

builder.row();

builder.add("field2");

JPanel panel = (JPanel) builder.getForm();

panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

return panel;

}

}

Much simpler and easy to read, isn’t it?

An additional advantage in using a form builder is that internationalized labels are supported out of the box. In the form builder

example no labels are coded, but the form builder will add them automagically.

Currently there is a form builder that works with JGoodies FormLayout named TableFormBuilder, and there is also one that

supports Java’s GridbagLayout. You can always create your own form builder by extending AbstractFormBuilder.

BINDER SELECTION

Spring Rich Client has a mechanism to automatically choose binders based on property names, types or even the used Swing

components. This is done through BinderSelectionStrategy implementations.

The standard implementation is the SwingBinderSelectionStrategy, which already has support for String and Boolean type fields.

If you want to expand this automatical binder selection, you can configure your own BinderSelectionStrategy in your Spring

context (which will be picked up by the service locator), and set for example the bindersForPropertyTypes map property. This

map matches property types to specific binders that you also have configured in your Spring context.

For example, if you want to change support String, Boolean and Date fields, you can define your selection strategy like this

<bean id="binderSelectionStrategy"

class="org.springframework.richclient.form.binding.swing.SwingBinderSelectionStrategy">

<property name="bindersForPropertyTypes" ref="propertyTypeBinders"/>

</bean>

Page 29: Spring Rich Client

29

<util:map id="propertyTypeBinders" key-type="java.lang.Class">

<entry key="java.lang.String" value-ref="stringBinder"/>

<entry key="java.util.Date" value-ref="dateBinder"/>

<entry key="java.lang.Boolean" value-ref="booleanBinder"/>

</util:map>

INTERNATIONALIZATION

Remember the id you can give to your formmodel? This is the part where it’s needed. Spring Rich Client will use the formmodel’s

is to create the key it’ll use to look up the label’s text.

Say your formmodel is named “personForm” and you have a field called “firstName”. Then in your message bundle you’ll have

to provide something like this:

personForm.firstName.label = First name

If no value is found for a key, Spring Rich Client will show the key instead. This way you can easily spot missing keys (and don’t

need to guess how they are named).

ADDING FORMS TO FORMS, AKA CHILD FORMS

Forms are just plain components. They can be added to forms as any other component. However, when creating a child form,

you need to make sure the formmodel of the child form is also a child of the formmodel of that form’s parent. That way, events

are carried over correctly.

Child forms can also be added by using the addChildForm(…) method on a form. This way, the formmodels between the two are

automatically linked. Bear in mind though, a setFormObject on a parent form does not cause a setFormObject on its children.

This is something you’ll have to handle yourself.

FORM VALIDATION

Form validation is done through the validation subsystem by validating the formmodel (and underlying valuemodels). Form

component interceptors such as the OverlayValidationInterceptorFactory can then show the validation errors to the user. For

more information on interceptor, read the next part.

Page 30: Spring Rich Client

30

FORM COMPONENT INTERCEPTORS

INTRODUCTION

Form component interceptors provide a way to intercept and add extra functionality to input components on a form.

The application context specifies the list of interceptors to attach to controls constructed by the platform. This allows for a

declarative model for specifying "additional" functionality to be added to various components.

Examples are interceptors to overlay validation error images and background color changes, provide popup menus in text fields,

and autocompletion (as you type) for comboboxes.

CREATING YOUR OWN INTERCEPTOR

To create your own FormComponentInterceptor, you have to provide both a FormComponentInterceptor and a

FormComponentInterceptorFactory implementation.

public interface FormComponentInterceptor {

public void processLabel(String propertyName, JComponent label);

public void processComponent(String propertyName, JComponent component);

}

CONFIGURATION

The configuration of the interceptors in the application context is done by defining the

FormComponentInterceptorFactory.

Sample configuration:

<bean id="formComponentInterceptorFactory"

class="org.springframework.richclient.form.builder.support.ChainedInterceptorFactory">

<property name="interceptorFactories">

<list>

<bean

class="org.springframework.richclient.form.builder.support.ColorValidationInterceptorFactory">

<property name="errorColor" value="255,200,200"/>

</bean>

<bean

class="org.springframework.richclient.form.builder.support.OverlayValidationInterceptorFactory"/>

<bean

class="org.springframework.richclient.form.builder.support.DirtyIndicatorInterceptorFactory"/>

<bean class="org.springframework.richclient.text.TextComponentPopupInterceptorFactory"/>

<bean

class="org.springframework.richclient.list.ComboBoxAutoCompletionInterceptorFactory"/>

</list>

</property>

</bean>

Page 31: Spring Rich Client

31

BUILT-IN INTERCEPTORS

There are a number of built-in interceptors provided with the framework. We’ll quickly explain them.

ERROR OVERLAY IMAGE

Shows an error image in the lower left corner of the component if the contents of the component is invalid. The image also has a

tooltip showing the validation message.

This class has been specifically made to work with Spring Rich Client’s validation framework and will show the errors coming

from that framework

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.OverlayValidationInterceptorFactory" />

ERROR BACKGROUND COLOR

Changes the background color of the form component when an invalid value is entered.

Properties:

errorColor: the background color

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.ColorValidationInterceptorFactory">

<property name="errorColor" value="255,200,200"/>

</bean>

TEXT EDITING POPUP (COPY, PASTE, UNDO,...)

Adds more advanced text editing functionality to text components. It adds a popup menu with

"undo/redo/cut/copy/paste/select all" items. It also adds the standard keyboard accelerators for these commands to the

component.

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.TextComponentPopupInterceptorFactory" />

Page 32: Spring Rich Client

32

COMBOBOX AUTOCOMPLETION

Adds auto completion to a combobox.

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.ComboBoxAutoCompletionInterceptorFactory" />

OVERLAY IMAGE INDICATING A CHANGED VALUE

Shows an image in the top left corner of the component if the contents of the component has been changed by the user. The

image also has a tooltip showing the original value. To the right of the image is a small revert button. Pushing this button

restores the original value in the component.

Properties:

includedFormModelIds: list of form models that should display the Dirty Indicator.

Only one of includedFormModelIds or excludedFormModelIds can be specified.

excludedFormModelIds: list of form models that should not display the Dirty Indicator

Only one of includedFormModelIds or excludedFormModelIds can be specified.

To configure this interceptor, you need to use this interceptor factory:

<!-- The login form will not show the Dirty Indicator -->

<bean class="org.springframework.richclient.form.builder.support.DirtyIndicatorInterceptorFactory">

<property name="excludedFormModelIds">

<list>

<value>loginForm</value>

</list>

</property>

</bean>

SELECT ALL TEXT

Selects all the text in text fields and spinners when they receive focus.

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.SelectAllInterceptorFactory" />

SETTING THE CARET TO THE BEGINNING OF THE FIELD

If the text is set in a text component, the caret position is set to the end of the text.

This means the beginning of the text will not be visible if the text is too long to fit in the text component.

This FormComponentInterceptor "fixes" this behavior, and sets the caret to position 0.

Page 33: Spring Rich Client

33

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.TextCaretFormComponentInterceptorFactory" />

SHOWING A TOOLTIP

If a form property has a caption defined in the messages.properties file it will be used as the tooltip for the form component.

Properties:

processComponent: default is true, determines whether the tooltip of the component is set.

processLabel: default is true, determines whether the tooltip of the label is set.

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.ToolTipInterceptorFactory" />

CHANGING THE RENDERING OF A CHECKBOX

Allows customization on how a CheckBox form property is rendered.

Properties:

showLabel: default is true, determines whether the label will be shown.

showText: default is false, determines whether the label text will be used as text for the checkbox itself.

textKey: default is "text", the key used to fetch the text to show.

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.CheckBoxFormComponentInterceptorFactory" />

SHOW THE CAPTION OF THE CURRENTLY FOCUSED COMPONENT IN THE STATUSBAR

Shows the caption of the form component in the statusbar when the component is focused.

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.ShowCaptionInStatusBarInterceptorFactory" />

SHOW THE DESCRIPTION OF THE CURRENTLY FOCUSED COMPONENT IN THE STATUSBAR

Shows the description of the form component in the statusbar when the component is focused.

To configure this interceptor, you need to use this interceptor factory:

<bean class="org.springframework.richclient.form.builder.support.ShowDescriptionInStatusBarInterceptorFactory" />

Page 34: Spring Rich Client

34

VALIDATION

WHY VALIDATION

Users make mistakes. They won’t admit it, but they do. As GUI developers, our task is to make sure that their mistakes don’t

crash or cripple our system. Input validation can help a great deal with this. Spring Rich Client supports validators for various

input. These are all subclasses of RichValidator.

VALIDATION CHOICES

Spring Rich Client has multiple validation strategies:

The built-in rules system

Using Hibernate Validator

Using Spring Modules’ Valang validation framework

A combination of any of the above

RULE VALIDATION

USING THE RULE FRAMEWORK

Spring Rich Client has created their own validation framework, which is rule based. A change to a value it put through a number

of checks, which are contained within a rule, and results in a success or a validation error.

The FormModelHelper can create formmodel with rule validation by providing a RulesSource in one of its factory methods.

A rule source contains different rules for a certain object type. If, for example, we want to validate that the field1 property of the

TestObject class is never empty, we can create a rulesource like this:

DefaultRulesSource source = new DefaultRulesSource();

Rules rules = new Rules(TestObject.class);

Constraints c = new Constraints();

rules.add(c.required("field1"));

source.addRules(rules);

CONSTRAINTS

Constraints contain the actual logic that checks the values.

The Constraints class contains a lot of predefined constraints. Among these you can find

Maximum length of string

Maximum value

Not null

Minimum value

Creating your own constraint is done by implementing the Constraint interface, which consists of one method

public interface Constraint

{

boolean test(Object argument);

}

This constraint will test any object. Most of the time, we’ll want to split up functionality to check individual properties, so that

we can reuse this logic elsewhere. Remember, the constraints you add to a rules source always need to be coupled to a

property.

Page 35: Spring Rich Client

35

For this, you’ll need to subclass AbstractPropertyConstraint, which needs a property name. Also, in it’s test method, it provides

an easy way to get values of individual properties.

Say we want to create a constraint that checks whether a String property’s value equals “RCP” (silly, but a good example). We’ll

end up with something like this:

public class RcpConstraint extends AbstractPropertyConstraint

{

protected boolean test(final PropertyAccessStrategy domainObjectAccessStrategy)

{

Object prop = domainObjectAccessStrategy.getPropertyValue(getPropertyName());

return !(prop instanceof String) || ((String) prop).equals("RCP");

}

}

This constraint you can then add to your rulessource for a specific property.

VALIDATION TRIGGERS

Validation on a property is triggered when that property is changed in its valuemodel. Spring Rich Client will search for rules for

that property and execute them.

DEPENDENT PROPERTIES

Some rules that are registered for a certain property need to be triggered when another property is changed (for example two

dates, for which the first needs to be before the last). Spring Rich Client supports this by overriding the isDependentOn(…)

method. Out-of-the-box, this method returns true if the parameters equals the property name for which the rule is defined.

However, you can add additional properties to this method. Every change in a property that returns true on this method will

cause this rule to be checked.

HIBERNATE VALIDATOR INTEGRATION

Hibernate Validator is a well-known framework for validating JavaBeans. It’s annotation-based, so you won’t find it in the

standard Spring Rich Client, but support is provided in the jdk5 module.

Simply put, if the object behind a form has Hibernate Validator annotations, by setting the validator to a

HibernateRulesValidator. This validator will check all property-based validations (no @AssertTrue support at the moment yet).

You can even turn off validation for certain properties (for example, a code is @NotNull, but in the GUI you’re allowed to leave it

empty because you’ll generate one on the fly when that’s the case).

VALANG VALIDATION FRAMEWORK INTEGRATION

For more information on the Valang validation framework, see the Valang documentation. Converting the rules source example

to Valang would result in something like this (I’ll show the bean definition):

<bean name="testObjectValidator" class="org.springmodules.validation.valang.ValangValidator">

<property name="valang">

<value><![CDATA[

{ field1 : (? IS NOT NULL) : 'Field 1 is required' : 'errors.required' : 'Field 1' }

]]>

You can then make a ValangRichValidator for a certain formmodel with that validator.

INTEGRATING YOUR OWN VALIDATION FRAMEWORK

If you want to integrate your own validation framework, you’ll need to subclass the RichValidator class and wire your validator

to produce validation results.

Page 36: Spring Rich Client

36

EXCEPTION HANDLING

AVOIDING TRY-CATCH CONSTRUCTS

Most runtime exceptions that are thrown are unexpected: we don't expect them to happen (especially during production) such

as:

NullPointerException: Didn't I double checked all my source code to avoid NPE's?

CvsParserException: Why did the user pick a html file when I asked him for a CVS file?

IDidNotKnowThisExistedRuntimeException: What the ...?

And if you do expect some of them, you usually can't really fix the problem, just deal with it:

Log the exception through a logging framework such as SLF or Log4J.

Notify the user that whatever he tried didn't work, preferably with an not-technical, exception-specific explanation.

Either shutdown the application or allow the user to continue (and try again).

You could use try-catch during every user action:

protected boolean onFinish() {

try {

form.getFormModel().commit();

// ...

getApplicationContext().publishEvent(new LifecycleApplicationEvent(eventType, getEditingContact()));

return true;

} catch (Throwable throwable) {

handleException(throwable);

}

}

But this is tedious and error prone:

It's easy to forget to try catch some code, which makes the exception escape to the top layer exception handler.

You could unwillingly eat the exception or not log it:

o If you handle an exception, but forget to log it and/or show it to the user.

o If you throw an exception in the catch or finally part, only the last exception bubbles up, effectively hiding the

real exception.

In production, this leads to discussions where the user is sure he did perform an action (which he did in this case) and the

programmer is sure the user didn't because the system didn't report anything and nothing has changed. If you notice that while

you are fixing a issue and an exception is being eaten (making it hard to identify the original issue), create a new issue because

exceptions are eaten and fix that first.

You are in danger to handle the same exception on 2 different layers, effectively logging it or notifying the user twice.

In some layers or parts of the application, it might not be clear if you need to notify the user (and which user) through a

swing dialog or JSP or web service response.

Spring Rich Client’s exception handling system uses the top layer exception handling. It expects that all other layers let the

exception bubble up.

REGISTERING AN EXCEPTION HANDLER

In the LifecycleAdvisor you can register a RegisterableExceptionHandler:

<bean id="lifecycleAdvisor" class="...LifecycleAdvisor">

<!-- ... -->

<property name="registerableExceptionHandler" ref="exceptionHandler" />

</bean>

<bean id="exceptionHandler" class="org.springframework.richclient.exceptionhandling.SilentExceptionHandler"/>

Page 37: Spring Rich Client

37

When an exception handler is registered, it will usually register itself as the UncaughtExceptionHandler on the threads.

However, the event thread catches a throwable thrown in any event, to prevent getting killed, so it also registers itself to the

event thread specifically (regrettably this is currently Sun JRE specific behavior).

BUILD-IN EXCEPTION HANDLERS

Most of these exception handlers are part of the spring-richclient-jdk5 module.

LOG SILENTLY

Logs a throwable but does not notify the user in any way. Normally it is a bad practice not to notify the user if something goes

wrong.

You can set a log level on it:

<bean class="org.springframework.richclient.exceptionhandling.SilentExceptionHandler">

<property name="logLevel" value="WARN"/>

<!-- ... -->

</bean>

This means that any exception handled by this exception handler will be logged at the warn level.

SHOW A MESSAGE DIALOG

Shows the exception in a dialog to the user (as well as logging it). You can set a log level and the icon of the dialog depends on

that log level. The shown dialog has a caption (= dialog title) and description (= dialog content), which are fetched from the i18n

messages files. There are 2 ways to resolve those messages: static or dynamic (default).

You can statically set the title and description by setting the messagesKey property. However, it's a lot more powerful to use

the default dynamic behavior based on the class of the exception. For example if a NumberFormatException is thrown, it

will first look for these i18n keys:

java.lang.NumberFormatException.caption=Not a number

java.lang.NumberFormatException.description=\

You did not enter a a valid number.\n\

\n\

Please enter a valid number.

If these messages keys don't exist, it will fall back to the parent class of NumberFormatException, which is

IllegalArgumentException:

java.lang.IllegalArgumentException.caption=...

java.lang.IllegalArgumentException.description=...

It will continue to fall back up the chain, untill it reaches Throwable. This allows you to direct all unexpected exceptions (for

example IDidNotKnowThisExistedRuntimeException) to a MessagesDialogExceptionHandler that logs

them as an error and shows a generic message. You can even use {0} in your i18n message to show the

exception.getMessage() in the description:

# Double quotes(") need to be escaped (\"), single quotes (') always seem to break the replacing of {0}.

java.lang.RuntimeException.caption = Unexpected general bug

java.lang.RuntimeException.description = \

The application experienced an unexpected bug,\n\

due to a programming error.\n\

\n\

The application is possibly in an inconsistent state.\n\

It is recommended to reboot the application.\n\

\n\

The exact bug is:\n\

{0}\n\

\n\

Page 38: Spring Rich Client

38

Please report this bug.

java.lang.Error.caption = Unexpected serious system failure

java.lang.Error.description = \

A serious system failure occured.\n\

\n\

The application is possibly in an inconsistent state.\n\

Reboot the application.\n\

\n\

The exact bug is:\n\

{0}\n\

\n\

Please report this bug.

Note that, although this dynamic system is pretty powerful and avoids a lot of boilerplate, it's usually not a replacement for

DelegatingExceptionHandler, because it doesn't allow you to assign different log levels, etc.

You can set a shutdown policy on a dialog exception handler:

<bean class="org.springframework.richclient.exceptionhandling.MessagesDialogExceptionHandler">

<property name="shutdownPolicy" value="ASK" />

<!-- ... -->

</bean>

This allows you to optionally enforce or propose a System.exit(1).

HIBERNATE VALIDATION EXCEPTION HANDLING

A special exception handler which can only handle an InvalidStateException thrown by Hibernate validator. It shows

the failed validations to a user in a list in a dialog. In most cases it's inferior to the HibernateRulesValidator which

validates before the user presses the commit button. But because the latter forces you to hand code @AssertTrue's and it

could be working on stale client-side data, it's actually a very nice backup to also configure this exception handler:

<!-- Inside a delage list of DelegatingExceptionHandler -->

<bean class="org.springframework.richclient.exceptionhandling.delegation.SimpleExceptionHandlerDelegate">

<property name="throwableClass">

<value type="java.lang.Class">org.hibernate.validator.InvalidStateException</value>

</property>

<property name="exceptionHandler">

<bean class="org.springframework.richclient.exceptionhandling.HibernateValidatorDialogExceptionHandler">

<property name="logLevel" value="INFO" />

<property name="shutdownPolicy" value="NONE" />

</bean>

</property>

</bean>

CUSTOM EXCEPTION HANDLERS

You can also extend AbstractLoggingExceptionHandler and implement this method:

public void notifyUserAboutException(Thread thread, Throwable throwable) {

// ...

}

This way, you could for example integrate e-mail functionality, heck, even IM functionality to the error notification towards the

user.

USING THE RIGHT EXCEPTION HANDLER FOR THE RIGHT EXCEPTION

There are bunch of build-in exception handlers, but usually there isn't one exception handler that fits alls exceptions. A

DelegatingExceptionHandler allows you to delegate an exception to the right exception handler. It accepts a delegate

list and traverses that list in order. The first delegate that can handle the exception, has to handle the exception and the rest of

the delegate list is ignored.

Page 39: Spring Rich Client

39

For example, here we configure authentication and authorization exceptions to a MessagesDialogExceptionHandler

with WARN messages, Hibernate exceptions to an INFO HibernateValidatorDialogExceptionHandler and the rest

to an ERROR MessagesDialogExceptionHandler.

<bean id="exceptionHandler"

class="org.springframework.richclient.exceptionhandling.delegation.DelegatingExceptionHandler">

<property name="delegateList">

<list>

<bean class="org.springframework.richclient.exceptionhandling.delegation.SimpleExceptionHandlerDelegate">

<property name="throwableClassList">

<list>

<value type="java.lang.Class">org.acegisecurity.AuthenticationException</value>

<value type="java.lang.Class">org.acegisecurity.AccessDeniedException</value>

</list>

</property>

<property name="exceptionHandler">

<bean class="org.springframework.richclient.exceptionhandling.MessagesDialogExceptionHandler">

<property name="logLevel" value="WARN" />

<property name="shutdownPolicy" value="NONE" />

</bean>

</property>

</bean>

<bean class="org.springframework.richclient.exceptionhandling.delegation.SimpleExceptionHandlerDelegate">

<property name="throwableClass">

<value type="java.lang.Class">org.hibernate.validator.InvalidStateException</value>

</property>

<property name="exceptionHandler">

<bean

class="org.springframework.richclient.exceptionhandling.HibernateValidatorDialogExceptionHandler">

<property name="logLevel" value="INFO" />

<property name="shutdownPolicy" value="NONE" />

</bean>

</property>

</bean>

<!-- The order is important: if Throwable would be first then the others would be ignored -->

<bean

class="org.springframework.richclient.exceptionhandling.delegation.SimpleExceptionHandlerDelegate">

<property name="throwableClass" value="java.lang.Throwable" />

<property name="exceptionHandler">

<bean class="org.springframework.richclient.exceptionhandling.MessagesDialogExceptionHandler">

<property name="logLevel" value="ERROR" />

<property name="shutdownPolicy" value="ASK" />

</bean>

</property>

</bean>

</list>

</property>

</bean>

SIMPLE DELEGATION

Processes the exception if it is an instance of throwableClass or the throwableClassList.

UNWRAPPING EXCEPTIONS

An exception purger allows you to cream off wrapper exceptions. This allows you to handle a chained exception in the chain of

the uncaught exception, instead of the uncaught exception itself. Almost all exception handlers and delegate's support the use

of a purger. DefaultExceptionPurger supports 2 ways to identify the depth to cream off: include or exclude based.

A chained exception of the type in the includeThrowableClassList is stripped from all it's wrapper exceptions and

handled by the exception handler or evaluated by the delegate. For example, we want to handle every

MySQLIntegrityConstraintViolationException even if it's wrapped:

Page 40: Spring Rich Client

40

<bean class="org.springframework.richclient.exceptionhandling.delegation.SimpleExceptionHandlerDelegate">

<property name="exceptionPurger">

<bean class="org.springframework.richclient.exceptionhandling.delegation.DefaultExceptionPurger">

<property name="includeThrowableClassList">

<list>

<value type="java.lang.Class">com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException</value>

</list>

</property>

</bean>

</property>

<property name="throwableClassList">

<list>

<value type="java.lang.Class">com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException</value>

</list>

</property>

<property name="exceptionHandler">

<!-- ... -->

</property>

</bean>

A chained exception of the type in the excludeThrowableClassList is stripped together with all it's wrapper exceptions

and it's cause is handled by the exception handler or evaluated by the delegate. For example the server wraps all exceptions in

an annoying, useless WrappingServiceCallException and we want to get rid of it:

<bean id="exceptionHandler"

class="org.springframework.richclient.exceptionhandling.delegation.DelegatingExceptionHandler">

<property name="exceptionPurger">

<bean class="org.springframework.richclient.exceptionhandling.delegation.DefaultExceptionPurger">

<property name="excludeThrowableClassList">

<list>

<value type="java.lang.Class">foo.bar.WrappingServiceCallException</value>

</list>

</property>

</bean>

</property>

<property name="delegateList">

<!-- ... -->

</property>

</bean>

Page 41: Spring Rich Client

41

SECURITY

INTEGRATING SECURITY IN A GUI APPLICATION

Integrating security into a GUI application can be cumbersome. The reason for this is:

Adding login and logout functionality can take a lot of work

Adjusting screens to handle with security limitations

Spring Rich Client integrates with Spring Security to provide a framework to handle security-based problems and offers a

solution for these.

SPRING SECURITY INTEGRATION

Simply integrating login functionality is easy: provide a login command in your commands context and make your lifecycle

advisor runs this command before showing the screen.

<bean id="loginCommand" class="org.springframework.richclient.security.LoginCommand"/>

public class PetClinicLifecycleAdvisor extends DefaultApplicationLifecycleAdvisor

{

// omitted for brevity

public void onCommandsCreated(ApplicationWindow window)

{

ActionCommand command = (ActionCommand) window.getCommandManager().getCommand("loginCommand",

ActionCommand.class);

command.execute();

}

}

The login command uses service location to find the application security manager. If no security manager is found in the context,

the default implementation will be used.

TURNING ON SECURITY AWARENESS OF THE GUI

Spring Rich Client has a bean post processor that can enable any class to be notified of security changes. You can define this post

processor as

<bean class=”org.springframework.richclient.security.SecurityAwareConfigurer”/>

Page 42: Spring Rich Client

42

If your classes implement either the AuthenticationAware or LoginAware interface, this bean postprocessor will make sure these

get notified of login changes.

SECURING COMMANDS

Securing commands is easy. AbstractCommand enables you to define a security controller (by its id), which then uses a access

decision manager to decide whether a command is allowed to be executed (by enabling/disabling it).

For example, a secured command bean in the commands context may look like this:

<bean id="newOwnerCommand" class="org.springframework.richclient.command.TargetableActionCommand">

<property name="commandExecutor" ref="newOwnerWizard" />

<property name=”securityControllerId” value=”mySecurityController”/>

</bean>

This assumes you have a security controller named mySecurityController defined in your application context, for example

TODO: rest of the text…

Page 43: Spring Rich Client

43

WIZARDS

WHY USE WIZARDS

Wizards make flow-like form entry easy. If you have a flow of screens, with the input of the first screen affecting the behavior

and possibly layout of one or more the next screens, wizards are an ideal way to accomplish this.

Spring Rich Client has built-in wizard functionality.

CREATING A WIZARD

In Spring Rich Client, a wizard consists of wizard pages. Every wizard page can determine which page is next or previous and

whether the wizard can stop at this page. Wizards are mostly form-based, which means validation is included as well. The wizard

framework will not allow a user to change to another screen as long as the current is invalid.

Creating a wizard is quite straightforward. You create pages (or even just forms) and add them to a wizard. Creating wizard will

most of the time mean subclassing the AbstractWizard class and building a wizard as a separate component.

public class MyWizard extends AbstractWizard

{

private MyForm form1;

private MyOtherForm form2;

public MyWizard()

{

initializeForms();

addForm(form1);

addForm(form2);

}

protected boolean onFinish()

{

form1.commit();

form2.commit();

doSomeLogic();

}

}

This wizard will make 2 forms and when the wizard completes (the user presses finish), it’ll do some logic.

SHOWING A WIZARD

There are various ways for showing a wizard. Standard, Spring Rich Client provides you with a dialog class that can show a wizard

(which most of the time is the way you’ll show a wizard).

You can off course create a view class for your wizard, so you can display your wizard as a view too. To implement this, take a

look at WizardDialog.