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.
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.
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 , and buttons
are created by SwingMetawidget based on annotated methods in the ContactDialog class.
![]() | 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 . The screen displays a form for filling out Personal Contact information as in Figure 1.13.
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:
![]() | 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.
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.
Click . The labels are transformed into editable widgets by using
Metawidget's setReadOnly(false), as in Figure 1.15.
The data from the PersonalContact object is automatically inserted into the
JComponents. It is also automatically saved back when clicking .
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.
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.
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).
![]() | 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.
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 ,
and 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).
![]() | 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 . The page displays a form for filling out Personal Contact information as in Figure 1.17.
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).
This section only applies to Spring and Struts.
Within the Communications table, implementing 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)
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.
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.
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).
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.
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.
![]() | 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 from the Android menu. The page displays a form for filling out Personal Contact information as in Figure 1.20.
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.
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.