1.2 Part 2 - The Address Book Application

Part 2 explores a more substantial application, and shows how Metawidget can be used to map the same back-end to multiple front-ends. We will develop an Address Book application with desktop-based, Web-based and mobile-based UIs.

This tutorial should take around 45 minutes. To save time, we use the pre-built example applications located in the examples folder. Also to save time, we will not focus on any one front-end framework in detail. For detailed framework-specific instructions, please see Chapter 3, Metawidgets.

1.2.1 Desktop Address Book

The Desktop Address Book is essentially a larger version of the Swing application developed in Part 1 - it just has more business objects and more Swing widgets.

The application is pre-built for you in examples\swing\addressbook-swing.jar or, if you've downloaded the source code distribution, you can build it yourself by changing to the examples folder of the source distribution and typing:

ant example-swing-addressbook

This is a self-executing JAR. For convenience, it has MAINFEST.MF dependencies hard-wired into it to ..\..\metawidget.jar and lib\beansbinding.jar among others, so it's best not to move it to a different folder (if you do, you'll need to manually put metawidget.jar and beansbinding.jar on your classpath).

Run the code by navigating to the examples\swing folder and typing:

java -jar addressbook-swing.jar

The opening screen displays a search filter (at the top) and lists existing Address Book entries (at the bottom) as in Figure 1.12.

Desktop Address Book opening screen

Figure 1.12. Desktop Address Book opening screen


The three search filter fields (Firstname, Surname and Type) are created by SwingMetawidget based on the ContactSearch business class. This includes populating the Type dropdown based on the ContactType enum. The Search, Add Personal Contact and Add Business Contact buttons are created by SwingMetawidget based on annotated methods in the ContactDialog class.

[Tip]Note
To view the source code for the examples, such as the code for the ContactSearch, ContactType and ContactDialog classes, download the Metawidget source code distribution or browse it online at http://metawidget.svn.sourceforge.net/viewvc/metawidget/trunk/examples/src.

Click Add Personal Contact. The screen displays a form for filling out Personal Contact information as in Figure 1.13.

Desktop Address Book 'Add Personal Contact' screen

Figure 1.13. Desktop Address Book 'Add Personal Contact' screen


All the form fields are created by SwingMetawidget based on the PersonalContact business class. This class is itself derived from the Contact business class. It includes some Metawidget annotations for dropdown values and section headings.

Note the code only has one JDialog class (ContactDialog), but is capable of supporting both PersonalContact and BusinessContact UIs. The fields in the UI change depending on the object passed to ContactDialog at runtime. This is the Third Goal Of Metawidget:

[Important]Third Goal Of Metawidget
Metawidget performs inspection at runtime, detecting types and subtypes dynamically

The Address field is created as a nested SwingMetawidget. This is the default behaviour when Metawidget encounters datatypes it does not know how to represent with any other UI widget. The Communications field has been overridden with a manually specified JTable.

In addition, JTable.setCellEditor uses SwingMetawidget to render single JComponents as CellEditors. This includes automatically populating dropdown values.

1.2.1.1 Read-Only Mode

The Desktop Address Book uses Metawidget's setReadOnly(true) method to display read-only screens. Return to the main screen, and double-click on an existing contact (such as Homer Simpson). The same ContactDialog is used, but this time all the widgets are read-only labels as in Figure 1.14.

Desktop Address Book read-only mode

Figure 1.14. Desktop Address Book read-only mode


Click Edit. The labels are transformed into editable widgets by using Metawidget's setReadOnly(false), as in Figure 1.15.

Desktop Address Book edit mode

Figure 1.15. Desktop Address Book edit mode


1.2.1.2 Binding

The data from the PersonalContact object is automatically inserted into the JComponents. It is also automatically saved back when clicking Save.

Swing does not define a JComponent to Object mapping mechanism, so by default SwingMetawidget only supplies setValue and getValue methods for manually fetching values. This situation is no worse than a normal Swing application, but Metawidget can do better.

SwingMetawidget directly supports third-party binding alternatives such as Apache BeanUtils and Beans Binding (JSR 295) via SwingMetawidget.addWidgetProcessor. These binding implementations automatically map JComponent values to Object values, including performing the necessary conversions, further reducing the amount of boilerplate code required.

1.2.1.3 Localization

All text within the application has been localized to the org.metawidget.example.shared.addressbook.resource.Resources resource bundle. Text created manually (such as the buttons) uses typical Swing localization code (eg. bundle.getString). Text created by SwingMetawidget uses SwingMetawidget.setBundle, which internally defers to bundle.getString.

Localization is very easy with Metawidget. For field names, if no resource bundle is supplied, Metawidget uses an 'uncamel-cased' version of the name. If a bundle is supplied, Metawidget uses the field name as the bundle key. For section labels, if a bundle is supplied, Metawidget uses a 'camel-cased' version of the label as the key.

This means developers can initially build their UIs without worrying about resource bundles, then turn on localization support later.

1.2.2 Web Address Book

As there are a large number of Java Web application frameworks to choose from, this example comes written in five of the most popular: Google Web Toolkit (GWT), Java Server Faces (JSF), Java Server Pages (JSP), Spring Web MVC and Struts. We recommend you follow along using the one most relevant to you.

Web-based applications are inherently more difficult to setup and run than desktop-based applications because they require a container application. For this tutorial, we will use Apache Tomcat 6 (Tomcat), as it is one of the easier containers to get running. Tomcat can be downloaded from http://tomcat.apache.org.

Take a fresh install of Tomcat. The Address Book WAR is pre-built for you in either examples\faces\addressbook-faces.war, examples\gwt\addressbook-gwt.war, examples\jsp\addressbook-jsp.war, examples\spring\addressbook-spring.war or examples\struts\addressbook-struts.war. If you've downloaded the source code distribution, you can build it yourself by changing to the examples folder of the source distribution and typing:

ant example-faces-addressbook

(replacing faces with gwt, jsp, spring or struts as appropriate).

[Tip]Note
For most web environments, deploying Metawidget is as simple as adding metawidget.jar to WEB-INF\lib. For GWT, you'll also need to include metawidget.jar and examples\gwt\metawidget-gwt-client.jar in the CLASSPATH during your GWTCompiler step.

Copy the WAR into Tomcat's webapps folder, start Tomcat, and open a Web browser to http://localhost:8080/addressbook-faces. The home page displays a search filter (at the top) and lists existing Address Book entries (at the bottom) as in Figure 1.16.

Web Address Book opening screen

Figure 1.16. Web Address Book opening screen


As with the Desktop Address Book, the three search filter fields are created by Metawidget (this time UIMetawidget, GwtMetawidget, HtmlMetawidgetTag, SpringMetawidgetTag or StrutsMetawidgetTag) based on the ContactSearch business class:

<m:metawidget value="#{contact.search}">
	...
</m:metawidget>

Again, this includes populating the Type dropdown and localizing the text. The Search, Add Personal Contact and Add Business Contact buttons are either manually specified in the JSP page (for GWT, Spring and Struts) or created by UIMetawidget based on annotated methods in the ContactBean (for JSF).

[Tip]Note
As with the Desktop Address Book, all source code for the examples can be found in the source code distribution under examples/src/java/org/metawidget/example. All Web-specific resources (such as JSP files) can be found under examples/src/web.

The look of the Web page relies entirely on HTML and CSS technologies. These are configured in metawidget.xml:

<htmlMetawidget>
	<parameter>
		<string>tableStyleClass</string>
		<string>table-form</string>
	</parameter>
	<parameter>
		<string>columnClasses</string>
		<string>table-label-column,table-component-column,table-required-column</string>
	</parameter>
	...

Only the layout of 'one column for the label, one column for the widget' is dictated by Metawidget, and that is again pluggable and configurable.

Click Add Personal Contact. The page displays a form for filling out Personal Contact information as in Figure 1.17.

Web Address Book 'Add Personal Contact' screen

Figure 1.17. Web Address Book 'Add Personal Contact' screen


All the form fields are created by Metawidget based on the PersonalContact business class. The section headings are the same, but have this time been rendered as HTML.

The Address field is a nested Metawidget. The Communications field has been overridden in the JSP page with a manually specified table. UIMetawidget understands a manually-specified widget to override an automatic one if it has the same value binding as the automatic widget would have (GwtMetawidget, SpringMetawidget and StrutsMetawidget do something similar):

<m:metawidget value="#{contact.current}">
	...		
	<h:dataTable value="#{contact.current.communications}">
		...
	</h:dataTable>						
	...							
<m:metawidget>

JSF has built-in support for executing actions on table rows. In order to use it, however, the Set returned by Contact.getCommunications must be wrapped into a DataModel. This is handled by ContactController.getCurrentCommunications, but this presents a problem: the mapping for the HtmlDataTable must be #{contact.currentCommunications}, but the mapping required to override UIMetawidget's automatic widget creation is #{contact.current.communications}.

UIMetawidget supplies UIStub for these situations. Stubs have a binding, but do nothing with it and render nothing. They can be used either to suppress widget creation entirely (a stub with an empty body) or to replace the automatic widget creation with one or more other widgets with different bindings:

<m:metawidget value="#{contact.current}">
	...		
	<m:stub value="#{contact.current.communications}">
		<h:dataTable value="#{contact.currentCommunications}">
			...
		</h:dataTable>						
	</m:stub>						
	...							
<m:metawidget>

JSP, Spring, Struts lack some component-based features found in Swing and JSF. Specifically, whilst it is possible for tags to reference their parent (using TagSupport.findAncestorWithClass), they have no way to interrogate their children. Therefore, it is not possible to directly support arbitrary child tags within HtmlMetawidget, SpringMetawidget and StrutsMetawidget.

Instead, we wrap the overridden Communications field in Metawidget's Stub tag. Metawidget and its Stub tags have explicit support for co-ordinating the overriding of widget creation:

<m:metawidget property="contactForm">
	...		
	<m:stub property="communications">
		<table class="data-table">
			...
		</table>
	</m:stub>						
	...							
<m:metawidget>

GwtMetawidget uses stubs around GWT widgets like FlexTable, but can use the overriding widget directly if it supports the HasName interface (eg. TextBox, CheckBox, etc).

1.2.2.1 Mixing Metawidgets

This section only applies to Spring and Struts.

Within the Communications table, implementing Add Communication calls for a design decision. Struts does not support multiple ActionForms per Action, so we are unable to combine PersonalContactForm with a CommunicationForm (as we did in the JSF). Spring has a similar limitation of not supporting multiple commandNames per form. Instead, we need to either:

  • add fields from Communication to PersonalContactForm, and ignore them when saving the PersonalContact; or

  • output plain HTML tags (ie. independent of Spring and Struts) and handle them manually

Both approaches would be valid. For this tutorial, we choose the latter as it allows us to introduce HtmlMetawidget (a Metawidget for plain HTML/JSP webapps that don't use Struts or Spring) and demonstrate mixing two Metawidgets on the same page:

<m:metawidget property="contactForm">
	...
	<m:stub property="communications">
		<table class="data-table">
		...
		<tr>
			<jsp:useBean id="communication"
							class="org.metawidget.example.shared.addressbook.model.Communication"/>		
			<td><mh:metawidget value="communication.type" style="width: 100%" /></td>
			<td><mh:metawidget value="communication.value" style="width: 100%" /></td>
		</tr>
		...
		</table>
	</m:stub>
	...
</m:metawidget>

The two different tag prefixes m: and mh: denote different tag libraries. HtmlMetawidget is very similiar to StrutsMetawidget, but has to use jsp:useBean to manually instantiate the bean (rather than letting Struts do it). Within metawidget.xml, the default layout for HtmlMetawidget has been set to org.metawidget.jsp.tagext.layout.SimpleLayout (ie. a plain layout, without a label column)

1.2.2.2 Expression Based Lookups

This section does not apply to GWT.

In the Desktop Address Book, the title dropdown was populated by a static lookup attribute in metawidget-metadata.xml. JSP and JSF-based technologies can do better, because they have a built-in scope-based component model and Expression Language.

Contact.getTitle is annotated using @UiFacesLookup and @UiSpringLookup (and ContactForm.getTitle is annotated using @UiStrutsLookup). These are used at runtime to create dynamic lookups.

These annotations, unlike the ones we have used so far, are UI-framework specific so you may prefer to declare them in metawidget-metadata.xml. Before doing so, however, you should understand we are still not introducing runtime dependencies into our business classes: an important feature of annotations is they 'fall away gracefully' if their implementing class is not found. Annotations never throw ClassDefNotFoundError.

1.2.2.3 Alternate Widget Libraries

This section only applies to JSF.

Metawidget factors all widget creation into WidgetBuilders. Like Inspectors, multiple WidgetBuilders can be combined using a CompositeWidgetBuilder to support third-party component libraries. In this section we will override Metawidget's default and introduce a RichFacesWidgetBuilder alongside the standard JSF HtmlWidgetBuilder.

Go into Tomcat's webapps\addressbook-faces folder (the exploded WAR) and edit WEB-INF/metawidget.xml:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="1.0"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
	...
	<inspector>		
		<compositeInspector xmlns="java:org.metawidget.inspector.composite"
								config="CompositeInspectorConfig">
			<inspectors>
			<array>
				<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
				<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"/>
				<java5Inspector xmlns="java:org.metawidget.inspector.java5"/>
				<facesInspector xmlns="java:org.metawidget.inspector.faces"/>			
				<xmlInspector xmlns="java:org.metawidget.inspector.xml" config="XmlInspectorConfig"/>
			</array>
			</inspectors>
		</compositeInspector>
	</inspector>
	<widgetBuilder>
		<compositeWidgetBuilder xmlns="java:org.metawidget.widgetbuilder.composite"
			config="CompositeWidgetBuilderConfig">
			<widgetBuilders>
				<array>
					<overriddenWidgetBuilder xmlns="java:org.metawidget.faces.component.widgetbuilder"/>
					<readOnlyWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
					<richFacesWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder.richfaces"/>
					<htmlWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
				</array>
			</widgetBuilders>
		</compositeWidgetBuilder>
	</widgetBuilder>
	<layout>
		<outputTextLayoutDecorator xmlns="java:org.metawidget.faces.component.html.layout"
			config="OutputTextLayoutDecoratorConfig">
			<layout>
				<simpleLayout xmlns="java:org.metawidget.faces.component.layout"/>
			</layout>
			<styleClass>
				<string>section-heading</string>
			</styleClass>
		</outputTextLayoutDecorator>
	</layout>	
</htmlMetawidget>

</metawidget>

Now restart Tomcat, refresh your Web browser and click on Homer Simpson. Notice how the Date of Birth field for Personal Contacts is now a RichFaces date picker widget, and the Number of Staff field for Business Contacts is a RichFaces slider widget.

Going futher, Metawidget's pluggable layouts make it easy to support third-party layout components. Edit WEB-INF/metawidget.xml again:

<metawidget xmlns="http://metawidget.org"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	version="1.0"
	xsi:schemaLocation="http://metawidget.org http://metawidget.org/xsd/metawidget-1.0.xsd">

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
	...
	<inspector>		
		<compositeInspector xmlns="java:org.metawidget.inspector.composite"
								config="CompositeInspectorConfig">
			<inspectors>
			<array>
				<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"/>
				<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"/>
				<java5Inspector xmlns="java:org.metawidget.inspector.java5"/>
				<facesInspector xmlns="java:org.metawidget.inspector.faces"/>			
				<xmlInspector xmlns="java:org.metawidget.inspector.xml" config="XmlInspectorConfig"/>
			</array>
			</inspectors>
		</compositeInspector>
	</inspector>
	<widgetBuilder>
		<compositeWidgetBuilder xmlns="java:org.metawidget.widgetbuilder.composite"
			config="CompositeWidgetBuilderConfig">
			<widgetBuilders>
				<array>
					<overriddenWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
					<readOnlyWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
					<richFacesWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder.richfaces"/>
					<htmlWidgetBuilder xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
				</array>
			</widgetBuilders>
		</compositeWidgetBuilder>
	</widgetBuilder>
	<layout>
		<tabPanelLayoutDecorator xmlns="java:org.metawidget.faces.component.html.layout.richfaces"
			config="TabPanelLayoutDecoratorConfig">
			<layout>
				<simpleLayout xmlns="java:org.metawidget.faces.component.layout"/>
			</layout>
		</tabPanelLayoutDecorator>
	</layout>	
</htmlMetawidget>

</metawidget>

Restart Tomcat, refresh your Web browser and click on Homer Simpson again. Notice how the Contact Details and Other sections are laid out as tabs within a RichFaces TabPanel as in Figure 1.18.

Web Address Book using JBoss RichFaces

Figure 1.18. Web Address Book using JBoss RichFaces


This demonstrates how easy it is to leverage widget libraries with Metawidget (this example cheats a bit, as we've pre-added the RichFaces JARs into WEB-INF\lib and some lines into web.xml, but you get the idea).

1.2.3 Mobile Address Book

For the Mobile Address Book we use the Android platform. Android has uniquely strong support for reflection and annotations, and guarantees the availability of key packages such as org.w3c.dom. This affords Metawidget excellent runtime access to inspect the O in OIM.

Like Web-based applications, Mobile applications require a container to run. Download the Android SDK (Metawidget has been tested against versions 1.1, 1.5, 1.6 and 2.0) from http://code.google.com/android/download.html. Then change to the installation directory (usually android-sdk-windows) and run the emulator by opening a command prompt and typing:

tools\android create avd -n my_avd -t 1

Replacing -t 1 with the Android version you're using (eg. -t 2 for 1.5, -t 3 for 1.6, -t 4 for 2.0). Android 1.1 doesn't use AVDs, so you can skip this step. Next type:

tools\emulator -avd my_avd

The emulator may take a little while to start. Once finished, it will display the phone's desktop. The Address Book APK is pre-built for you in examples\android\addressbook-android.apk. If you've downloaded the source code distribution, you can build it yourself by changing to the examples folder of the source distribution and typing:

ant example-android-addressbook

(ensuring your build.properties is set correctly to point to the Android SDK).

Next, open a second command prompt, change to the Android installation directory and type:

tools\adb install <metawidget folder>\examples\android\addressbook-android.apk

This deploys the APK into the emulator. To run it, click the emulator's Menu button and then choose the Address Book application. The emulator displays a search filter (at the top) and lists existing Address Book entries (at the bottom) as in Figure 1.19.

Mobile Address Book opening screen

Figure 1.19. Mobile Address Book opening screen


As with the Desktop and Web Address Books, the three search filter fields are created by Metawidget (this time AndroidMetawidget) based on the ContactSearch business class. Again, this includes populating the Type dropdown.

[Tip]Note
As with the Desktop Address Book, all source code for the examples can be found in the source code distribution under examples/src/java/org/metawidget/example. All Android-specific resources (such as XML files) can be found under examples/src/android.

The look of the screen relies entirely on Android XML layout files, styles and themes. Only the 'one column for the label, one column for the widget' layout is dictated by Metawidget, and that is pluggable and configurable.

Choose Add Personal from the Android menu. The page displays a form for filling out Personal Contact information as in Figure 1.20.

Mobile Address Book 'Add Personal Contact' screen

Figure 1.20. Mobile Address Book 'Add Personal Contact' screen


UIs in Android are typically defined in XML layout files, though they can also be built programmatically. AndroidMetawidget supports both approaches. For example, the Personal Contact screen is defined in contact.xml, and contains a Metawidget defined in much the same way as in JSP (including configuring section style and overriding widget creation):

<view class="org.metawidget.android.widget.AndroidMetawidget" android:id="@+id/metawidget"
	config="@raw/metawidget">
	
	<view class="org.metawidget.android.widget.Stub" tag="communications">
			
		<ListView android:id="@id+/communications" ... />
	 			
		<Button android:id="@+id/buttonAddCommunication"
			android:text="@string/addCommunication" ... />
	
	</view>
 
</view>

Within CommunicationDialog, a Metawidget is defined programatically in much the same way as in Swing:

mMetawidget = new AndroidMetawidget( activity );
mMetawidget.setConfig( R.raw.config );				
...
mMetawidget.setToInspect( mCommunication );

This produces the dialog box in Figure 1.21.

Mobile Address Book Communications Dialog

Figure 1.21. Mobile Address Book Communications Dialog


1.2.4 Conclusion

That concludes the introductory tutorial. In summary, we now:

  • have seen how to build an application whose UI is largely dictated by its business classes, not by hand-written UI code

  • significantly reduced the amount of UI code needed in our applications

  • have seen how to build an application that targets multiple platforms. If we were to add a new field to one of the business classes (say, numberOfChildren to PersonalContact), it would automatically appear and be functional on every platform.