Using the Base Components of Spring Rich

The Spring Rich application consists of several key components that cooperate to create and manage the application's views and process commands the views generate in response to the user actions. In this section, we examine the key beans used in the Petclinic application.

Application and ApplicationLifecycleAdvisor

The most important beans that govern the lifecycle of a Spring Rich application are Application and ApplicationLifecycleAdvisor. The Application bean uses the ApplicationLifecycleAdvisor subclasses to manage the lifecycle of the application. The DefaultApplicationLifecycleAdvisor overrides the createWindowCommandManager() method to look up an instance of the ApplicationWindowCommandManager bean. This bean intercepts commands from the UI and performs the associated actions. The Petclinic application extends ApplicationLifecycleAdvisor in PetClinicLifecycleAdvisor and overrides the onPreWindowOpen() and onCommandsCreated() methods to perform extra initialization.

The declaration of ApplicationWindowCommandManager, stored in commands-context.xml, is shown in Listing B-6.

Listing B-6: ApplicationWindowCommandManager Bean Declaration

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"      ""> <beans>     <bean bold">windowCommandManager"          symbol">¿              ApplicationWindowCommandManager">         <property name="sharedCommandIds">             <list>              <!-- commands -->             </list>         </property>     </bean>          <bean           bold">AboutCommand">         <property name="aboutTextPath">             <value>                 org/springframework/richclient/samples/petclinic/about.txt             </value>         </property>     </bean> </beans>
image from book

The commands themselves are other Spring-managed beans, some of which are also declared in this file. The commands usually extend the ApplicationWindowAwareCommand class, which attaches the command to the appropriate user interface element and handles the events the user interface element generates.

Using Commands

Now take a look at aboutCommand. This command's definition is shown in Listing B-6 and it is implemented in the AboutCommand class. It extends the ApplicationWindowAwareCommand and most importantly, it overrides the doExecuteCommand() method to perform the required action (see Listing B-7).

Listing B-7: AboutCommand Implementation

image from book
package;      import; import;      public class AboutCommand extends ApplicationWindowAwareCommand {     private static final String ID = "aboutCommand";          private AboutBox aboutBox = new AboutBox();          public AboutCommand() {         super(ID);     }          public void setAboutTextPath(Resource path) {         this.aboutBox.setAboutTextPath(path);     }          protected void doExecuteCommand() {         aboutBox.display(getApplicationWindow().getControl());     }      }
image from book

This particular implementation shows a dialog window displaying the contents of the file specified in the path Resource; the user interface item and the result of the doExecuteCommand() method are shown in Figure B-3.

image from book
Figure B-3: About window

You can execute a command in another way, which is perhaps more suitable when you are implementing commands in your own application. You can use the TargetableActionCommand as a bean class and set its commandExecutor property to an instance of the bean that implements the ActionCommandExecutor interface. The ActionCommandExecutor interface contains a single method, execute(), that your class must implement to perform the actions of the command. This is clearly demonstrated in the NewOwnerWizard class shown in Listing B-8.

Listing B-8: NewOwnerWizard Implementation

image from book
package org.springframework.richclient.samples.petclinic.ui;      import org.springframework.richclient.application.event.LifecycleApplicationEvent; import org.springframework.richclient.command.ActionCommandExecutor; import org.springframework.richclient.forms.CompoundForm; import org.springframework.richclient.wizard.AbstractWizard; import org.springframework.richclient.wizard.FormBackedWizardPage; import org.springframework.richclient.wizard.WizardDialog; import org.springframework.samples.petclinic.Clinic; import org.springframework.samples.petclinic.Owner; import org.springframework.util.Assert;      public class NewOwnerWizard      extends AbstractWizard implements ActionCommandExecutor {          public void execute() {         if (wizardDialog == null) {             wizardDialog = new WizardDialog(this);             wizardForm = new CompoundForm();         }         wizardForm.setFormObject(new Owner());         wizardDialog.showDialog();     }     // other methods omitted }
image from book

The application context contains a newOwnerCommand bean definition that references the NewOwnerWizard (see Listing B-9).

Listing B-9: newOwnerCommand Bean Definition

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"      ""> <beans>     <bean bold">newOwnerCommand"          bold">TargetableActionCommand">         <property name="commandExecutor">             <ref bean="newOwnerWizard"/>         </property>     </bean> </beans>
image from book

We now know how to implement a command to perform the operations we need, however, we still do not know how to create a menu item or a toolbar button and associate it with the command.

Several beans of type CommandGroupFactoryBean with quite intuitive names take care of this. Now we go back to the file shown in Listing B-9 and add those beans into the context file. By default, DefaultApplicationLifecycleAdvisor looks up beans using the names shown in Listing B-10.

Listing B-10: Menu Bar and Toolbar Beans

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"      ""> <beans>          <bean bold">menuBar"          bold">CommandGroupFactoryBean">         <property name="members">             <list>                 <ref bean="fileMenu"/>                 <ref bean="editMenu"/>                 <ref bean="windowMenu"/>                 <ref bean="helpMenu"/>             </list>         </property>     </bean>          <bean bold">toolBar"          bold">CommandGroupFactoryBean">         <property name="members">             <list>                 <ref bean="newMenu"/>                 <value>saveAsCommand</value>             </list>         </property>     </bean>          <bean bold">fileMenu"          bold">CommandGroupFactoryBean">         <property name="members">             <list>                 <ref bean="newMenu"/>                 <value>separator</value>                 <ref bean="loginCommand"/>                 <ref bean="logoutCommand"/>                 <value>separator</value>                 <value>saveAsCommand</value>                 <value>separator</value>                 <value>propertiesCommand</value>                 <value>separator</value>                 <bean                     symbol">¿                         ExitCommand"/>             </list>         </property>     </bean>          <bean bold">newMenu"          bold">CommandGroupFactoryBean">         <property name="members">             <list>                 <ref bean="newOwnerCommand"/>             </list>         </property>     </bean>          <bean           >         <property name="members">             <list>                 <value>undoCommand</value>                 <value>redoCommand</value>                 <value>separator</value>                 <value>cutCommand</value>                 <value>copyCommand</value>                 <value>pasteCommand</value>                 <value>separator</value>                 <value>selectAllCommand</value>                 <value>deleteCommand</value>             </list>         </property>     </bean>          <bean bold">windowMenu"          bold">CommandGroupFactoryBean">         <property name="members">             <list>                 <bean                      symbol">¿                         NewWindowCommand"/>                 <value>separator</value>                 <bean                      symbol">¿                         ShowViewMenu"/>                 <value>separator</value>                 <ref bean="preferenceCommand"/>             </list>         </property>     </bean>          <bean bold">helpMenu"          bold">CommandGroupFactoryBean">         <property name="members">             <list>                 <ref bean="helpContentsCommand"/>                 <value>separator</value>                 <ref bean="aboutCommand"/>             </list>         </property>     </bean> </beans>
image from book

We can now create menu bars and toolbars and we can specify which commands to execute when the user selects an item from the menu or clicks a toolbar button. In most cases, executing a command results in another user interface element being displayed. Each Command can be bound to multiple user interface elements and any CommandGroup can group any number of pop-up menus, menus, menu bars, and toolbars. The newOwnerCommand, for example, is referenced from the fileMenu as well as the newMenu. In the next section, we are going to take a look at the views used in the Petclinic application.

Using Views

There are two major groups of user interface elements: views and forms. A view is an atomic part of the user interface (such as a tree view with a search box) that you can insert into another view or a form. A form represents a window displayed by the operating system.

An example of a view is the list of owners shown in the main window, while an example of a form is the New Owner dialog. Now we show you how the OwnerManager view is implemented and how it uses the OwnerGeneralForm and OwnerAddressForm to edit the selected owner. The definition of the ownerManagerView bean is shown in Listing B-11.

Listing B-11: OwnerManagerView Bean

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"      ""> <beans>     <bean bold">ownerManagerView"         symbol">¿             DefaultViewDescriptor">         <property name="viewClass">             <value>                 org.springframework.richclient.samples.petclinic.ui.¿                 OwnerManagerView             </value>         </property>         <property name="viewProperties">             <map>                 <entry key="clinic">                     <ref bean="clinic"/>                 </entry>             </map>         </property>     </bean> </beans>
image from book

The OwnerManagerView implementation is a subclass of the AbstractView, which allows our subclass to be used as a viewClass of DefaultViewDescriptor. This property is used in the DefaultViewDescriptor's createView() method, which instantiates the specified class. Alternatively, you can override DefaultViewDescriptor.createView() either by subclassing DefaultViewDescriptor or by using method injection.

Next, we want to find out how OwnerManagerView controls the context menu displayed when you right-click an owner, and how it displays the Edit Owner dialog. This is best demonstrated in the code in Listing B-12.

Listing B-12: OwnerManagerView Implementation

image from book
package org.springframework.richclient.samples.petclinic.ui;      // imports omitted      public class OwnerManagerView      extends AbstractView implements ApplicationListener {          private Clinic clinic;     private String ownerLastName = "";     private JTree ownersTree;     private DefaultTreeModel ownersTreeModel;     private RenameCommand renameCommand = new RenameCommand();     private DeleteCommandExecutor deleteExecutor = new DeleteCommandExecutor();     private PropertiesCommandExecutor propertiesExecutor =          new PropertiesCommandExecutor();          protected void registerLocalCommandExecutors(         PageComponentContext context) {         context.register(GlobalCommandIds.DELETE, deleteExecutor);         context.register(GlobalCommandIds.PROPERTIES, propertiesExecutor);     }          protected JComponent createControl() {         JPanel view = new JPanel(new BorderLayout());         createOwnerManagerTree();         JScrollPane sp = new JScrollPane(ownersTree);         view.add(sp, BorderLayout.CENTER);         return view;     }          private class PropertiesCommandExecutor          extends AbstractActionCommandExecutor {         private NestingFormModel ownerFormModel;         private CompositeDialogPage compositePage;              public void execute() {             final Owner owner = getSelectedOwner();             ownerFormModel = SwingFormModel.createCompoundFormModel(owner);             compositePage = new TabbedDialogPage("ownerProperties");             compositePage.addForm(new OwnerGeneralForm(ownerFormModel));             compositePage.addForm(new OwnerAddressForm(ownerFormModel));                  TitledPageApplicationDialog dialog =                  new TitledPageApplicationDialog(compositePage,                      getWindowControl()) {                 protected boolean onFinish() {                     ownerFormModel.commit();                     clinic.storeOwner(owner);                     ownersTreeModel.nodeChanged(getSelectedOwnerNode());                     return true;                 }             };             dialog.showDialog();         }     }      } 
image from book

The most important bits of the code are shown in Listing B-12. The first method called is the createControl(), which sets up the visual representation of the view. In Petclinic, the OwnerManagerView instance is realized by instances of JTree in JScrollPane. Next, the registerLocalCommandExecutors() method is called. This method registers commands local to the view. These commands are activated when the view receives focus. This is precisely what the implementation in Listing B-12 does: it registers deleteExecutor and propertiesExecutor, two logical actions that are shared between views.

Now we need to focus on propertiesExecutor, which is set to an instance of the inner class PropertiesCommandExecutor. This class is a subclass of AbstractActionCommandExecutor and overrides the well-known execute() method to perform the operations the command requires. When execute() is called, perhaps as the result of a menu-item click, a TitledApplicationDialog containing a tabbed dialog page appears. On the dialog's first tab, the OwnerGeneral form is displayed, allowing editing of general identification properties like firstName and lastName. On the dialog's second tab, the Address form is displayed, allowing you to edit the Owner's location.

When you click the dialog OK button, onFinish() is automatically called. Next, any buffered edits are committed to the backing Owner domain model object in ownerFormModel.commit(). Because the forms are backed by the Owner JavaBean, updates to it are easily persistable using an ORM tool such as Hibernate. The clinic business façade is responsible for updating the owner in the database in its onFinish() method.

Note also, because of Spring Rich's declarative validation system, the finish command is only enabled when all validation rules are satisfied. If a validation rule is violated, the dialog displays a descriptive error message and automatically disables the finish command. The framework manages all of this for you. Figure B-4 shows the Owner Properties form with the Last Name field failing validation; this causes the OK button to be disabled.

image from book
Figure B-4: Owner Properties window

Typically, you want to configure an initial view to display when the application starts. The default view is configured in the ApplicationLifecycleAdvisor bean; in our case, this is the petclinicLifecycleAdvisor bean shown in Listing B-13.

Listing B-13: petclinicLifecycleAdvisor Bean Definition

image from book
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"      ""> <beans>          <bean bold">application"          bold">Application">         <constructor-arg index="0">             <ref bean="petclinicLifecycleAdvisor"/>         </constructor-arg>     </bean>          <bean bold">petclinicLifecycleAdvisor"          symbol">¿             PetClinicLifecycleAdvisor">         <property name="windowCommandBarDefinitions">             <value>              org/springframework/richclient/samples/petclinic/ui/¿              commands-context.xml</value>         </property>         <property name="startingPageId">             <value>ownerManagerView</value>         </property>     </bean> </beans> 
image from book

These two beans, application and petclinicLifecycleAdvisor, are the final two beans that bind all the application's other beans together into one rich client application. The petclinicLifecycleAdvisor's startingPageId property specifies the bean that implements the PageDescriptor that is displayed when the application starts. By default, the instance of SingleViewPageDescriptor is created for the main application window.

Prettying Up the Application

The users of desktop applications are used to seeing nice icons, bitmaps, and consistent colors. The Spring Rich framework supports these elements of the user interface–implemented classes shown in Table B-1.

Table B-1: User Interface Configuration Classes




Displays user interface elements in a way that is consistent with the host OS.


Sets the UIManagerConfigurer instance to use JGoodies.


This bean uses the ResourceMapFactoryBean to load the image definitions.


This bean loads the icons from the ImageSource bean.

In general, all Spring Rich GUI elements created using implementations of ButtonFactory, ComponentFactory, ControlFactory, LabelInfoFactory, and MenuFactory have their visual properties configured using DI. The result of this is that you do not need to hard-code the visual settings in Java. A good example of this declarative configuration is the imageResourcesFactory bean in the richclient-application-context.xml file.

The other user interface beans (such as MessageSource) you already know from 18.

Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: