You are hereAria User Guide / Section V: Case Studies / Case Study: Mortgage
Case Study: Mortgage
Introduction
The Mortgage application which is outlined as part of this case study is taken from the tutorial which is available for download from the Aria project on the sourceforge site. The intention of the tutorial is the make the developer familiar with development elements of the Aria/Aria framework. The development of the application is split over several different tutorials which gives the developer a fully working version of the application at the end of each step.
In this case study we are going to examine the elements which make up the application in each tutorial. In summary the tutorials are as follows
Introduction
This step shows how to set up the basic structure of a Aria application and what files and libraries need to be included. It creates some simple pages and shows how to navigate the application. It shows how to add page events, validations, data bindings and dynamic bindings. The application is completely localized in the final step and the model is used to output captured data.
Advanced
This step builds upon the introductory tutorial and provides the user with a more generic navigation which allows new pages to be added very easily. It also shows how library functions can be used to help with the navigation. Custom validations are created which are loaded via a custom ValidationFactory. Framesets are customised and component registration is used to develop and load custom components. Custom dialogs are created which report on the types of errors being generated. Model data is saved from and restored to the model showing how easy it is to create applications where projects can be created and opened.
Aria Editor
This tutorial really replicates what was done in the first step except that the development is carried out with the help of the Aria Editor editor.
Aria
In introducing Aria the application is makes use of the routes which are available in the Aria library. This allows the developer to create applications which can easily connect to a servlet server making use of authentication and session management services. Data can be cached on the client and the application can be used to work with them offline and then synchronise with the server. Custom ServiceProxy classes are created which are used in transforming mode data.
Introduction tutorial
This tutorial begins by showing the project structure for a typical Aria application as shown in the diagram below.
![]() |
The run.bat file contains the text below.
The run.bat file
java -cp .;lib;AriaRuntimeCommon.jar;images;pages; resources;build;classes org.formaria.swing.Applet
The application is made up of frames which are defined in the frames.xml file as shown below.
The frames.xml file
<FrameSet> <Frame name="banner" constraint="NORTH" content="banner" width="640" height="96"/> <Frame name="content" constraint="CENTER" content="Welcome" width="640" height="288"/> <Frame name="navPanel" constraint="SOUTH" content="navPanel" width="640" height="96"/> </FrameSet>
Navigation
The project navigation was handled by the navPanel page in the navPanel frame. In order to do this two images where added to the navPanel page as shown below
navPanel.xml page
<Page class="org.formaria.mortgage.NavPanel" style="Heading"> <Components> <Image name="nextButton" x="564" y="51" w="19" h="18" content="right.gif"/> <Image name="prevButton" x="60" y="54" w="19" h="18" content="left.gif"/> </Components> <Events> <Event method="nextPage" target="nextButton" type="MouseHandler"/> <Event method="prevPage" target="prevButton" type="MouseHandler"/> </Events> </Page>
Two images are added and each has a MouseHandler event added to them. The nextPage and prevPage functions are defined in the NavPanel class as follows.
The NavPanel class
package org.formaria.mortgage; import org.formaria.aria.*; public class NavPanel extends Page { public void nextPage() { if ( wasMouseClicked()) navigateToPage( "next" ); } protected void navigateToPage( String key ) { Page target = ( Page )( ( Target ) pageMgr.getTarget( "content" ) ).getComponent( 0 ); String dest = ( String )target.getAttribute( key, null ); if ( dest != null ) pageMgr.showPage( dest, "content" ); } public void prevPage() { if ( wasMouseClicked() ) pageMgr.showPrevious(); } }
This navigation depended on the presence of the next attribute in the main content page
Bindings
Dynamic binding was used to address different nodes within the data model. On the personal page the binding are declared as follows.
The bindings on the personal page
... <Data> <Bind target="firstnameText" source="mortapp/${getCustomerID()}/firstname" output="mortapp/${getCustomerID()}/firstname" /> <Bind target="surnameText" source="mortapp/${getCustomerID()}/surname" output="mortapp/${getCustomerID()}/surname" /> <Bind target="dobText" source="${getCustomerID()}/dob" output="mortapp/${getCustomerID()}/dob" /> <Bind target="titleList" source="defaults/titleList" output="mortapp/${getCustomerID()}/title" /> </Data> ...
These bindings depend on the callback function getCustomerID in the Personal class as follows.
The callback function in the Personal page
public String getCustomerID() { return "customer" + ( String )currentCust.get(); }
This class takes care of tracking which customer is being currently processed allowing numerous customers to be added to the model in their own specific nodes.
Saving the model
The model data was then saved within the Finish class pageActivated function.
The Finish class
public class Finish extends Page { public void pageActivated() { try { String path = System.getProperty( "user.dir" ) + File.separator + "save.xml"; FileOutputStream fos = new FileOutputStream( path ); OutputStreamWriter osw = new OutputStreamWriter( fos, "UTF8" ); BufferedWriter bw = new BufferedWriter( osw ); DataSource.outputModel( bw, ( ( DataModel ) rootModel.get( "aria_state/mortapp" ) ) ); bw.flush(); bw.close(); } catch (Exception e) { System.out.println( "error" ); } } }
This provided us with the following output.
Model data saved from the application
<data id="mortapp"> <data id="customer1"> <data value="Joe" id="firstname" /> <data value="Bloggs" id="surname" /> <data value="14/12/1975" id="dob" /> <data value="Mr" id="title" /> </data> <data id="customer2"> <data value="Josephine" id="firstname" /> <data value="Bloggs" id="surname" /> <data value="01/04/1972" id="dob" /> <data value="Mrs" id="title" /> </data> <data id="finance"> <data value="250000" id="propvalue" /> <data value="200000" id="mortamt" /> </data> </data>
Localization
The entire application was localized through the use of language specific resource bundles. For example the personal page was localized simply by changing the content attributes for the components.
The localized personal page
<Page next="personal" class="org.formaria.mortgage.Welcome" resource=""> <Components> <Label x="10" y="5" w="80" h="20" style="prompt" content="WEL_LANG" alignment="Left" opaque="true"/> <Combo name="languageList" x="90" y="5" w="120" h="20"/> <Label x="130" y="84" w="350" h="60" style="Heading" content="WEL_TEXT" alignment="Left" opaque="true"/> <Panel x="0" y="150" w="650" h="50" style="banner/prompt"> <RadioButton name="soleRadio" x="226" y="15" w="100" h="20" style="banner/prompt" content="WEL_SOLE" alignment="Leading"/> <RadioButton name="jointRadio" x="330" y="15" w="100" h="20" style="banner/prompt" content="WEL_JOINT" alignment="Leading"/> </Panel> </Components> ...
The resources were entered into two resource bundles for English and French.
Extract from en.properties
WEL_LANG=Language WEL_TEXT=Welcome to ABC Bank's mortgage application. Before we proceed please note the following... WEL_SOLE=Sole WEL_JOINT=Joint BAN_TITLE=ABC Bank Mortgage Application BAN_CLOSE=Close Application PER_HEADING_1=Personal Details (First Applicant) PER_HEADING_2=Personal Details (Second Applicant) ...
And the French translation:
Extract from fr.properties
WEL_LANG=Langue WEL_TEXT=Bienvenue ? la demande de pr?t hypoth?caire de l'hypoth?que de la banque de ABC. Avant que nous veuillez proc?der note le suivant... WEL_SOLE=Unique WEL_JOINT=Joint BAN_TITLE=Demande de pr?t hypoth?caire D'Hypoth?que De Banque de ABC BAN_CLOSE=Application ?troite PER_HEADING_1=D?tails Personnels (Premier Demandeur) PER_HEADING_2=D?tails Personnels (Deuxi?me Demandeur) ...
A combo box was then placed on the welcome page which allowed the user to switch language. The selection of a language invoked the following code in the Welcome class.
Language switching code
public void changeLanguage() { if ( langClicked ) { String lang = ( String )languageList.getSelectedObject(); pageMgr.reset(); String langCode = lang.compareTo( "English" ) == 0 ? "en" : "fr"; project.setStartupParam( "Language", langCode ); pageMgr.loadFrames( "frames", true ); } }
This completed the introductory tutorial and the resulting application can be seen below
![]() |
Advanced Tutorial
This tutorial uses the application created in the introductory tutorial and begins by adding custom validations and a custom validation factory. The custom validation calls a function within the Page and returns the result of it. The custom validation is shown below.
The custom FunctionValidation factory
public class FunctionValidation extends BaseValidator { public FunctionValidation() { } public void validate( Object c, boolean forceMandatory ) throws Exception { Integer ret = ( Integer ) invokeMethod(); if ( ret.intValue() > LEVEL_IGNORE ) { errorLevel = ret.intValue(); throwException(); } } public void setup( XmlElement element ) { message = element.getAttribute( "msg" ); super.setup( element ); } }
In order to use this validation a custom validation factory need to be created as shown below.
The custom ValidationFactory class
public class ValidationFactory extends ValidationFactory { ... public Validator getValidation( String validationName, Method m, int mask, Object page ) { XmlElement ele = ( XmlElement ) validations.get( validationName ); String type = ele.getAttribute( "type" ); if ( type.compareTo( "function" ) == 0 ) { FunctionValidation validator = new FunctionValidation(); validator.setName( validationName ); validator.setValidationMethod( m, page ); validator.setup( ele ); return validator; } else { BaseValidator validator = (BaseValidator)super.getValidation( validationName, mask, page ); validator.setup( ele ); return validator; } } }
The getValidation function checks to see if the type being created is function . If it is it creates a new instance of the FunctionValidation class and returns it. If it isn't the super getValidation function is called in order to retrieve the validation.
In order for Aria to use this validation factory the following line needed to be added to the startup properties.
Startup property for the custom validation factory
ValidationFactory=org.formaria.mortgage.validation.ValidationFactory
Now the welcome page was amended to use the new validation.
XML declaration for the function validation
<Validation rule="createApp" target="jointRadio" method="checkCreateSetup"/>
And the welcome class was amended to include the checkCreateSetup function
The checkCreateSetup function
... public Integer checkCreateSetup() { int level = BaseValidator.LEVEL_IGNORE; if ( !jointRadio.isSelected() && !soleRadio.isSelected() ) level = BaseValidator.LEVEL_ERROR; return new Integer( level ); } ...
This function checks to see that at least one of the radio buttons on the welcome page has been selected. If everything is OK then LEVEL_IGNORE is returned otherwise LEVEL_ERROR is returned and the error message is displayed.
Bindings
The tutorial now goes on to show how the data model can be reopened once saved. This required the use of a custom data binding which was added to a list component on the welcome page.
Binding to the list component on the welcome page
... <List name="fileList" x="0" y="0" w="200" h="100" style="data"/> ... <Bind target="fileList" source="applications" output="temp/filename"/> ...
Now the saved files needed to be listed when the pageCreated function was called.
Loading files into the model
public void pageCreated() { ... loadFiles(); } ... private void loadFiles() { String dir = System.getProperty( "user.dir" ); appsMdl = ( BaseModel )rootModel.get( "applications" ); File folder = new File( dir + ";;files" ); File files[] = folder.listFiles(); for ( int i = 0; i < files.length; i++ ) { String name = files[ i ].getName(); BaseModel filMdl = new BaseModel ( appsMdl, name, name ); } updateBindings(); } ...
Child nodes containing the filenames are added to the applications model node and the updateBindings function of the page is called so that the list will refresh. Now the selected file could be opened and restored into the model as follows.
Opening and restoring the model state
... public void openFile() { String filename = ( String )fileList.getSelectedObject(); // Not shown here but simply reads the file into a string. String contents = getFileContents( filename ); restoreState( contents ); updateBindings(); } private void restoreState( String contents ) { ( ( BaseModel )rootModel.get( "aria_state/mortapp" ) ).clear(); ( ( BaseModel )rootModel.get( "mortapp" ) ).clear(); StringReader sr = new StringReader( contents ); XmlElement ele = XmlSource.read( sr ); if ( ele != null ) { OptionalDataSource ds = new OptionalDataSource(); ds.loadTable( ele, rootModel ); ds.loadTable( ele, ( ( BaseModel )rootModel.get( "aria_state" ) ) ); } } ...
The restoreState function is really of most importance here. It initialises the aria_state and mortapp nodes. It then loads the model into both of these. Again, updateBindings is called so as to refresh the state of the radio buttons on the welcome page.
The list can be seen on the welcome page below.
![]() |
Navigation
The navigation of the applicaton was improved by making use of the library functions which are part of Aria. To do this the navPanel page was changed to reference functions in the Navigator class as follows.
The amended MouseHandler events in the navPanel page
<Events> <Event method="org.formaria.mortgage.navigate.Navigator.next" target="nextButton" type="MouseHandler"/> <Event method="org.formaria.mortgage.navigate.Navigator.previous" target="prevButton" type="MouseHandler"/> </Events>
The Navigator page was created as a Singleton instance class and included the static functions next and previous . The pages to be included in the navigation were defined in a new navigation dataset as follows.
The navigation.xml file
<dataset id="navigation"> <page id="welcome" value="welcome" /> <page id="personal" value="personal" startrepeat="true" title="PER_HEADING"/> <page id="bank" value="bank" endrepeat="true" title="BANK_HEADING"/> <page id="finance" value="finance" title="FINANCE_TITLE"/> <page id="finish" value="finish" /> </dataset>
The Navigator class took care of keeping track of where the application was in the navigation sequence and of repeating the relevant pages where for the amount of customers being processed. Now each of the pages in the navigation could use the same class to return the correct customer id.
The generic CustomerPage class
public class CustomerPage extends Page { public String getCustomerID() { return "customer" + Navigator.getInstance().getCurrentCust(); } }
With this mechanism the navigation was greatly simplified and pages could be added very easily by referencing them in the navigation file. The amount of customers which could be process is now endless and all taken care of by the very simple Navigation class.
![]() |
Framesets and component registration
The framesets in were amended to include a status frame to show the user exactly where they were in the application navigation. A new frame declaration was added to the frames.xml file as follows.
The new declaration in frames.xml
<FrameSet> <Frame name="banner" constraint="NORTH" content="banner" width="640" height="96"/> <Frame name="appstatus" constraint="WEST" content="appstatus" width="150" height="500" /> <Frame name="content" constraint="CENTER" content="welcome" width="490" height="288" /> <Frame name="navPanel" constraint="SOUTH" content="navPanel" width="640" height="96"/> </FrameSet>
This frame was made invisible on the welcome page and visible on subsequent pages. It's visibilty was controlled by the Navigation class.
A custom component was then created which listed all of the pages to be processed and highlighted the current one. It was declared in the components.xml file as follows.
The components.xml file with the declaration for NavViewer
<Components> <Component name="NavViewer" class="org.formaria.mortgage.comp.NavViewer"> <Property name="Style" mode="Normal" type="both"/> <Property name="DefaultStyle" mode="Normal" type="both"/> <Property name="CurrentStyle" mode="Normal" type="both"/> <Property name="DoneStyle" mode="Normal" type="both"/> <Property name="NavModelPath" mode="Normal" type="both"/> </Component> </Components>
Now a NavViewer instance was added to the navPanel page as follows.
The AppStatus.xml file
<Page class="org.formaria.mortgage.AppStatus" style="Heading" resource=""> <Components> <NavViewer name="navViewer" x="0" y="0" w="145" h="400" Style="status" DefaultStyle="status/default" CurrentStyle="status/current" DoneStyle="status/done" NavModelPath="navigation"/> <Label x="149" y="0" w="1" h="500" style="banner" opaque="true"/> </Components> </Page>
Now the NavViewer class could take care of displaying navigation information as below.
![]() |
Dialogs
A custom dialog was introduced to display errors and warnings with different icons. This new dialog also allowed the user to proceed if only warnings were generated. The exception handler needed to be amended to store the errors and warnings in their own respective vector objects as below.
The ExceptionHandler class
public boolean handleException( Object comp, Exception ex, Object xvalidator ) { ... if ( validator.getLevel() == validator.LEVEL_ERROR ) errors.add( currentPage.translate( msg ) ); else warnings.add( currentPage.translate( msg ) ); return true; } public int accumulateMessages( boolean start, int level ) { pageValidation = start; if ( pageValidation ){ errors = new Vector(); warnings = new Vector(); } else { if ( errors.size() > 0 || warnings.size() > 0 ) return getDialogResult( level ); } return level; } private int getDialogResult( int level ) { ErrorMessage dlg = (ErrorMessage)ProjectManager.getPageManager().loadPage( "ErrorDlg", false ); dlg.setErrorMessages( errors, warnings ); int result = dlg.showDialog( currentPage ); if ( level == Validator.LEVEL_WARNING ) return result == Validator.LEVEL_IGNORE ? Validator.LEVEL_IGNORE : level; else return Validator.LEVEL_ERROR; }
The getDialogResult function creates the new ErrorMessage dialog and passes in the two vectors. The return value determines whether the page can proceed or not depending on what button the user clicked.
In the ErrorMessage class the relevant return value is set when one of the buttons is clicked as follows.
The ErrorMessage class
... public int showDialog( Container owner ) { super.showDialog( owner ); return ret; } public void cancelClicked() { ret = BaseValidator.LEVEL_WARNING; super.closeDlg(); } public void okClicked() { ret = BaseValidator.LEVEL_IGNORE; super.closeDlg(); } ...
- Printer-friendly version
- Login or register to post comments




