Metawidget Reference Documentation

V 0.75


Table of Contents

Preface
Supported Technologies
1. Introduction to Metawidget
1.1. Part 1 - The First Metawidget Application
The Object
The Interface
The Output
Ordering The Fields
Inspectors
Combining Multiple Inspection Results
Controlling The Layout
Controlling Widget Creation
Configuring Inspectors Externally
Inspecting Different Sources
1.2. Part 2 - The Address Book Application
Desktop Address Book
Web Address Book
Mobile Address Book
1.3. Part 3 - Other Examples
Swing Applet Address Book Example
GWT Hosted Mode Address Book Example
Seam Example
Groovy Example
jBPM Example
Swing AppFramework Example
Scala Example
1.4. Conclusion
2. Architecture
2.1. Metawidgets
Interface
Customizing Look and Feel
Overriding Widget Creation
2.2. inspection-result
2.3. Widget Builders
Interface
Usage
Immutability
2.4. Inspectors
Interface
Usage
Immutability
2.5. metawidget.xml
Construct New Objects
Call Setter Methods
Construct Primitive Types
Understand Immutability
3. Metawidgets
3.1. Desktop Metawidgets
SwingMetawidget
3.2. Web Metawidgets
Hidden Fields
GwtMetawidget
UIMetawidget (JSF)
SpringMetawidget
StrutsMetawidget
3.3. Mobile Metawidgets
AndroidMetawidget
3.4. Implementing Your Own Metawidget
MetawidgetMixin
Android
Java Server Faces
Java Server Pages
Swing
4. Widget Builders
4.1. Desktop Widget Builders
SwingWidgetBuilder
SwingXWidgetBuilder
4.2. Web Widget Builders
DisplayTagWidgetBuilder
GwtWidgetBuilder
HtmlWidgetBuilder (JSF)
HtmlWidgetBuilder (JSP)
RichFacesWidgetBuilder
SpringWidgetBuilder
StrutsWidgetBuilder
4.3. Mobile Widget Builders
AndroidWidgetBuilder
4.4. Implementing Your Own WidgetBuilder
5. Inspectors
5.1. Property Inspectors
BaseObjectInspector
PropertyTypeInspector
Java5Inspector
5.2. Annotation Inspectors
HibernateValidatorInspector
JexlInspector
JpaInspector
MetawidgetAnnotationInspector
Troubleshooting
5.3. XML Inspectors
BaseXmlInspector
CommonsValidatorInspector
HibernateInspector
JexlXmlInspector
XmlInspector
5.4. Implementing Your Own Inspector
6. How To's
6.1. Order Fields
6.2. Remote Inspection
6.3. Combine Remote Inspections
7. Performance
7.1. JAR Size
7.2. Rebinding
8. Epilogue

Preface

Metawidget is an object/user interface mapping tool for Java environments. The term object/user interface mapping (OIM) refers to the technique of inspecting objects, at runtime, and creating User Interface (UI) widgets.

As much as possible, Metawidget does this without introducing new technologies. As shown in Figure 1, Metawidget inspects an application's existing back-end architecture (such as JavaBeans, annotations, XML configuration files) and creates widgets native to its existing front-end framework (such as Swing, Java Server Faces, Struts or Android).

Figure 1. Metawidget inspects existing back-ends and creates widgets native to existing front-ends

Metawidget inspects existing back-ends and creates widgets native to existing front-ends

Building great UIs is both art and science. Metawidget does not attempt to address the art, it only automates the science. That is to say, it does not overlap with those areas of UI design involving creativity and subjectivity - its goal is only to ease the creation of areas that are already rigidly defined. Typically, this means those areas that display data and those that collect data - these tend to be both commonplace and consistent (indeed, consistency is a desirable trait) so there is good opportunity for automation.

[Tip]Note
This Reference Documentation is included in the distribution as a PDF, a single HTML page or as multiple HTML pages, depending on your reading preference.

Supported Technologies

A primary goal of Metawidget is to work with your existing front-end and back-end architecture. Out of the box, Metawidget supports a broad range of both front-end and back-end technologies, and makes it easy to add your own.

Metawidget comes with a native UI component for each supported front-end. This support includes: Android (for mobile phones), Google Web Toolkit, Java Server Faces (including extensions such as Facelets and JBoss RichFaces), 'plain' Java Server Pages, Spring Web MVC, Struts and Swing (including extensions such as Beans Binding, JGoodies and MigLayout).

Metawidget can read business object information from any combination of supported back-end technologies. This support includes: annotations, Commons JEXL, Commons Validator, Groovy, Hibernate, Hibernate Validator, JavaBeans, Java Persistence Architecture (JPA), Javassist, JBoss jBPM, Scala and the Swing AppFramework.

Note it is not a goal of Metawidget that all widgets look the same on every front-end framework, or that all back-end technologies conform to some 'lowest common denominator': every technology has different features, and Metawidget takes advantage of this.

The next chapter presents a tutorial covering using Metawidget with a variety of front-ends and back-ends. Chapter 2 then follows with a more in-depth architectual overview. Chapters 3, 4 and 5 explore each supported front-end and back-end technology in detail. Finally, chapters 6 and 7 offer general advice and performance tips.

1. Introduction to Metawidget

This chapter is an introductory tutorial for new users of Metawidget. Before you begin, you need to download at least the binary distribution, and preferrably the source code distribution as well, from http://www.metawidget.org/download.html.

1.1 Part 1 - The First Metawidget Application

Part 1 starts with a simple Swing application and develops it in easy to understand steps. Metawidget supports many UI frameworks, not just Swing, but we start with Swing because it ships with Java SE and requires minimal setup.

This tutorial should take around 20 minutes. We recommend you use your preferred Java development environment. If you use an Integrated Development Environment (IDE), you will need to start a new Java project and add metawidget.jar to it. Otherwise, you just need to ensure metawidget.jar is on your classpath.

The Object

Metawidget is an object/user interface mapping tool (OIM), so first we need an object to map from - the O in OIM. Create a class in your project called Person with the following code:

package com.myapp;
	
public class Person {
	public String name;
	public int age;
	public boolean retired;
}

Note this tutorial uses public member variables for brevity. The recommended design is to use JavaBean property getter and setter methods. Metawidget supports both approaches.

The Interface

Next we need a User Interface framework - the I in OIM. Create a class in your project called Main with the following code:

package com.myapp;
	
import javax.swing.*;
import org.metawidget.swing.*;

public class Main {
	public static void main( String[] args ) {
		Person person = new Person();
	
		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setToInspect( person );
	
		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Note many IDEs include visual UI builders for dragging and dropping widgets. Metawidget integrates with these tools and Metawidget widgets can be dragged and dropped like any other. As we shall see, however, Metawidget widgets automatically fill themselves with child widgets at runtime, saving significant development time.

The Output

Run the code. You should see the screen in Figure 1.1.

Figure 1.1. SwingMetawidget rendering of Person class

SwingMetawidget rendering of Person class

The SwingMetawidget has automatically populated itself with child widgets at runtime. It has chosen JSpinner, JTextField and JCheckBox widgets based on the types of the properties of the Person class. This is the First Goal Of Metawidget:

[Important]First Goal Of Metawidget
Metawidget creates UI widgets by inspecting existing back-end architectures

By default, SwingMetawidget has laid out the JComponents using java.awt.GridBagLayout. Try resizing the window, and the JComponents will resize with it. If you've ever tried using GridBagLayout yourself, either through code or a visual UI builder, you'll know how fiddly it can be. Having Metawidget do it for you is a real time-saver.

Clearly this is not a complete UI. There are no Save or Cancel buttons, for example, and the JComponents appear uncomfortably tight to the left, top and right edges of the JFrame. This is explained by the Second Goal Of Metawidget:

[Important]Second Goal Of Metawidget
Metawidget does not try to 'own' the entire UI - it focusses on creating native sub-widgets for slotting into existing UIs

You slot Metawidget alongside your standard UI components, often combining several Metawidgets on the same form. We'll see how this works later.

Ordering The Fields

Currently the name, age and retired fields are arranged alphabetically in the UI - their order does not match the way they are defined in the Person class. This is because field ordering information is not retained within Java class files (as per the Java Language Specification).

To correct this, Metawidget needs to gather additional information. There are several ways to do this, but the simplest for now is to use the built-in Metawidget annotation @UiComesAfter.

[Tip]Note
The following code uses annotations, so you'll need Java SE 5. Metawidget itself can run on J2SE 1.4, but you'll need to the ordering information from a different source (see the section called “Inspecting Different Sources”)

Annotate the Person class as shown below (lines to add are shown in bold):

package com.myapp;

import org.metawidget.inspector.annotation.*;

public class Person {
	public String name;
	
	@UiComesAfter( "name" )
	public int age;
	
	@UiComesAfter( "age" )
	public boolean retired;
}

Again, such annotations can (and should) be applied to getter methods, but for brevity here they are applied directly to the member variable.

Run the code again. This time the fields appear in the correct order:

Figure 1.2. Fields in correct order

Fields in correct order

Inspectors

Introducing new annotations to improve the UI is not really in the spirit of the First Goal Of Metawidget. We'd much rather improve it by gathering information from existing sources. To demonstrate, add the following lines to Person (lines to add are shown in bold):

package com.myapp;

import org.metawidget.inspector.annotation.*;

public class Person {
	public String name;
	
	@UiComesAfter( "name" )
	public int age;
	
	@UiComesAfter( "age" )
	public boolean retired;
	
	@UiComesAfter( "retired" )
	public Gender gender;
	
	public enum Gender { Male, Female }
}

Run the code again:

Figure 1.3. New Gender field, but missing a component

New Gender field, but missing a component

Metawidget finds the new gender property, and renders a JLabel for it on the left, but doesn't know what JComponent to put on the right. This is because, by default, Metawidget inspects only JDK 1.4-compatible features, and JDK 1.4 doesn't include enums.

To recognise the enum, Metawidget needs to use a different Inspector. Metawidget comes with multiple Inspectors, each targeting different sources of information. Change the Main class to use a Java5Inspector (line to add is shown in bold):

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.java5.*;
import org.metawidget.swing.*;

public class Main {
	public static void main( String[] args ) {
		Person person = new Person();
	
		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setInspector( new Java5Inspector() );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code again. It does not yield the correct result - the gender enum appears correctly as a JComboBox but all the other fields have disappeared! What happened?

Figure 1.4. Correct Gender field, but missing other fields

Correct Gender field, but missing other fields

Metawidget Inspectors are very targeted in what they inspect. Java5Inspector looks for Java 5 language features - such as enums - but it does not look for anything else - such as JavaBean properties. Before we explicitly specified a Java5Inspector, Metawidget had been implictly using a PropertyTypeInspector for us.

Combining Multiple Inspection Results

What we need is to combine the results of Java5Inspector and PropertyTypeInspector before returning them to SwingMetawidget. We do this using CompositeInspector:

package com.myapp;
				
import javax.swing.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.java5.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.swing.*;

public class Main {
	public static void main( String[] args ) {
		Person person = new Person();
	
		SwingMetawidget metawidget = new SwingMetawidget();
		CompositeInspectorConfig config = new CompositeInspectorConfig()
			.setInspectors( 
				new MetawidgetAnnotationInspector(),
				new PropertyTypeInspector(),
				new Java5Inspector() );
		metawidget.setInspector( new CompositeInspector( config ) );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code again. This time both the original fields and the new gender JComboBox appear:

Figure 1.5. Using multiple inspectors

Using multiple inspectors

This idea of combining multiple Inspectors to inspect different characteristics of your existing application is very powerful. Metawidget comes with many pre-written Inspectors for many different architectures - from JPA and Hibernate Validator annotations, to struts-config.xml configuration files, to Groovy and Scala objects - Metawidget will gather and combine UI information from wherever it can find it.

Controlling The Layout

There are several ways to control the layout of the components. To demonstrate, Try adding the following fields to the Person class:

package com.myapp;

import org.metawidget.inspector.annotation.*;

public class Person {
	public String name;
	
	@UiComesAfter( "name" )
	public int age;
	
	@UiComesAfter( "age" )
	public boolean retired;

	@UiComesAfter( "retired" )
	public Gender gender;
	
	public enum Gender { Male, Female }
	
	@UiComesAfter( "gender" )
	@UiLarge
	public String notes;

	@UiComesAfter( "notes" )
	@UiSection( "Work" )
	public String employer;

	@UiComesAfter( "employer" )
	public String department;
}

This code produces the screen in Figure 1.6. Annotations have been used to define section headings and 'large' fields (ie. a JTextArea).

Figure 1.6. Additional fields and a section heading

Additional fields and a section heading

By default, SwingMetawidget lays out JComponents using org.metawidget.swing.layout.GridBagLayout. You can swap this for a different layout using SwingMetawidget.setLayoutClass, or you can fine-tune the existing one using SwingMetawidget.setParameter:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.swing.*;

public class Main {
	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		CompositeInspectorConfig config = new CompositeInspectorConfig()
			.setInspectors(
				new MetawidgetAnnotationInspector(),
				new PropertyTypeInspector(),
				new Java5Inspector() );
		metawidget.setInspector( new CompositeInspector( config ) );
		metawidget.setParameter( "numberOfColumns", 2 );
		metawidget.setToInspect( person );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The JComponents are now arranged across two columns as in Figure 1.7.

Figure 1.7. A two-column layout

A two-column layout

Again, if you've ever used java.awt.GridBagLayout by hand, you'll appreciate how much easier Metawidget makes this.

Controlling Widget Creation

There are several ways to control widget creation. Creation can be overridden by dropping child controls inside the SwingMetawidget. This approach works well both within code and within visual UI builders.

Modify the code to add a JComboBox to the Metawidget:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.swing.*;

public class Main {
	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		CompositeInspectorConfig config = new CompositeInspectorConfig()
			.setInspectors(
				new MetawidgetAnnotationInspector(),
				new PropertyTypeInspector(),
				new Java5Inspector() );
		metawidget.setInspector( new CompositeInspector( config ) );
		metawidget.setParameter( "numberOfColumns", 2 );		
		metawidget.setToInspect( person );
		JComboBox combo = new JComboBox();
		combo.setName( "retired" );
		metawidget.add( combo );

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The JComboBox appears in place of the retired JCheckBox, because it has the same name (eg. 'retired') as Metawidget would have given the JCheckbox.

Figure 1.8. The 'retired' field has been overridden

The 'retired' field has been overridden

To suppress a widget's creation entirely, simply supplying an empty JPanel will not work as Metawidget will still create an accompanying label in the left hand column. Instead, Metawidget includes special Stub widgets for this purpose:

package com.myapp;

import javax.swing.*;
import org.metawidget.inspector.*;
import org.metawidget.inspector.annotation.*;
import org.metawidget.inspector.propertytype.*;
import org.metawidget.inspector.composite.*;
import org.metawidget.swing.*;

public class Main {
	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		CompositeInspectorConfig config = new CompositeInspectorConfig()
			.setInspectors(
				new MetawidgetAnnotationInspector(),
				new PropertyTypeInspector() );
		metawidget.setInspector( new CompositeInspector( config ) );
		metawidget.setParameter( "numberOfColumns", 2 );
		metawidget.setToInspect( person );
		metawidget.add( new Stub( "retired" ));

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The retired field and its label will not appear.

Figure 1.9. The 'retired' field has been suppressed

The 'retired' field has been suppressed

Alternatively, you can use a @UiHidden annotation on the business class:

package com.myapp;

import org.metawidget.inspector.annotation.*;

public class Person {
	public String name;
	
	@UiComesAfter( "name" )
	public int age;
	
	@UiComesAfter( "age" )
	@UiHidden
	public boolean retired;

	@UiComesAfter( "retired" )
	public Gender gender;
	
	public enum Gender { Male, Female }

	@UiComesAfter( "gender" )
	@UiLarge
	public String notes;

	@UiComesAfter( "notes" )
	@UiSection( "Work" )
	public String employer;

	@UiComesAfter( "employer" )
	public String department;
}

In both cases, org.metawidget.swing.layout.GridBagLayout is smart enough to always give large JComponents like notes the full width of the JFrame.

Configuring Inspectors Externally

So far we have been instantiating our Inspectors in Java code. Whilst this approach is possible for all Inspectors, many UI frameworks employ visual UI builders or intermediate languages (such as JSPs) that make getting to the Java code cumbersome (ie. you have to derive custom widgets).

As an alternative, Metawidget supports external XML configuration. Create a file called metawidget.xml in the same folder as your Main class:

<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/metawidget-1.0.xsd">

<swingMetawidget xmlns="java:org.metawidget.swing">		
	<inspector>
		<compositeInspector xmlns="java:org.metawidget.inspector.composite"
			config="CompositeInspectorConfig">
			<inspectors>
				<list>
				<metawidgetAnnotationInspector
					xmlns="java:org.metawidget.inspector.annotation" />
				<propertyTypeInspector
					xmlns="java:org.metawidget.inspector.propertytype"/>
				<java5Inspector
					xmlns="java:org.metawidget.inspector.java5"/>
				</list>
			</inspectors>
		</compositeInspector>
	</inspector>
</swingMetawidget>

</metawidget>

Now update your Main class to use this file:

package com.myapp;

import javax.swing.*;
import org.metawidget.swing.*;

public class Main {
	public static void main( String[] args ) {
		Person person = new Person();

		SwingMetawidget metawidget = new SwingMetawidget();
		metawidget.setConfig( "com/myapp/metawidget.xml" );
		metawidget.setParameter( "numberOfColumns", 2 );
		metawidget.setToInspect( person );
		metawidget.add( new Stub( "retired" ));

		JFrame frame = new JFrame( "Metawidget Tutorial" );
		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
		frame.getContentPane().add( metawidget );
		frame.setSize( 400, 250 );
		frame.setVisible( true );
	}
}

Run the code. The output is the same as before, but this time we are configuring our Metawidget via external XML.

Visual UI builders can call SwingMetawidget.setConfig from the builder, with no coding required. Other UI frameworks (eg. JSPs, Android) have similar 'code free' approaches (eg. setting an attribute on a JSP tag, setting an attribute in an Android layout file) to setting the XML file.

Inspecting Different Sources

It could be argued UI-oriented annotations such as @UiComesAfter sit uncomfortably on a business class from a 'separation of concerns' perspective. It has advantages in that it keeps the metadata close to the data it refers to. It is also sufficiently abstract that it does not tie the code to any particular UI framework.

However, for those needing a different approach Metawidget can use different Inspectors to gather information from almost any source. One example is to use XmlInspector. Create a file called metawidget-metadata.xml in the same folder as your Main class:

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

	<entity type="com.myapp.Person">
		<property name="name" />
		<property name="age" />
		<property name="retired" />
		<property name="gender" />
		<property name="notes" large="true"/>
		<property name="employer" section="Work"/>
		<property name="department"/>
	</entity>

</inspection-result>

Note that XmlInspector does not need a comes-after attribute: XML nodes are inherently ordered, and CompositeInspector combines inspection results so that later results respect the ordering of earlier results. XmlInspector also does not need to specify a type attribute: we will still be using PropertTypeInspector to look up the types.

Update your metawidget.xml to use XmlInspector:

<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/metawidget-1.0.xsd">

<swingMetawidget xmlns="java:org.metawidget.swing">		
	<inspector>
		<compositeInspector xmlns="java:org.metawidget.inspector.composite"
			config="CompositeInspectorConfig">
			<inspectors>
				<list>
				<xmlInspector xmlns="java:org.metawidget.inspector.xml"
					config="XmlInspectorConfig">
					<inputStream>
					<resource>com/myapp/metawidget-metadata.xml</resource>
					</inputStream>						
				</xmlInspector>
				<propertyTypeInspector
					xmlns="java:org.metawidget.inspector.propertytype"/>
				<java5Inspector
					xmlns="java:org.metawidget.inspector.java5"/>
			</list>
			</inspectors>
		</compositeInspector>
	</inspector>
</swingMetawidget>

</metawidget>

Remove all the annotations from the Person class and run the code again. The ordering is still correct, and there is still a section heading, but this time it is being dictated by XmlInspector.

This idea of the same UI characteristics being derivable from different back-end sources is fundamental to Metawidget. There is a lot of metadata already lurking in back-end systems - it just needs extracting. For example, JpaInspector understands this...

import javax.persistence.Column;

public class Person {
	@Column( nullable = false )
	public String name;
}

...denotes name is a required field (could be rendered with a star after it in the UI). Equally, PropertyTypeInspector understands that...

public class Person {
	private String name;
	
	public String getName()	{
		return this.name;
	}
	
	// No setter
}

...signifies name is a read-only field (could be rendered as a label in the UI).

Metawidget comes with a range of Inspectors, and it is straightforward to write your own to inspect anything from XML configuration files to database schemas to annotations. The inspection process is loosely coupled from the widget creation process, so the same Inspector can supply information to multiple UI frameworks.

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.

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 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.10.

Figure 1.10. Desktop Address Book opening screen

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]Tip
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.11.

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

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.

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.12.

Figure 1.12. Desktop Address Book read-only mode

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.13.

Figure 1.13. Desktop Address Book edit mode

Desktop Address Book edit mode

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.setPropertyBindingClass. These binding implementations automatically map JComponent values to Object values, including performing the necessary conversions, further reducing the amount of boilerplate code required.

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 a '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.

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 typing:

ant example-faces-addressbook

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

[Tip]Tip
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.14.

Figure 1.14. Web Address Book opening screen

Web Address Book opening screen

As with the Desktop Address Book, the three search filter fields are created by Metawidget (this time UIMetawidget, GwtMetawidget, 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]Tip
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:

<m:metawidget value="#{contact.search}">
	<f:param name="tableStyleClass" value="table-form"/>
	<f:param name="columnClasses"
		value="table-label-column,table-component-column,required" />
	...
</m:metawidget>

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.15.

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

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).

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 (eg. 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%" layoutClass=""/></td>
			<td><mh:metawidget value="communication.value"
				style="width: 100%" layoutClass=""/></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). The default layout has been changed using layoutClass="" from a TableLayout to a plain layout (eg. one without a label column).

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.

Alternate Widget Libraries

This section only applies to JSF.

Metawidget factors all widget creation into Widget Builders. Like Inspectors, multiple Widget Builders 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/metawidget-1.0.xsd">

<htmlMetawidget xmlns="java:org.metawidget.faces.component.html">
	<parameter>
		<string>tableStyleClass</string>
		<string>table-form</string>
	</parameter>
	<parameter>
		<string>columnClasses</string>
		<string>table-label-column,table-component-column,required</string>
	</parameter>
	<parameter>
		<string>footerStyleClass</string>
		<string>buttons</string>
	</parameter>	
	<widgetBuilder>
	<compositeWidgetBuilder
		xmlns="urn:java:org.metawidget.widgetbuilder.composite"
		config="CompositeWidgetBuilderConfig">
		<widgetBuilders>
		<list>
		<richFacesWidgetBuilder
		xmlns="java:org.metawidget.faces.component.html.widgetbuilder.richfaces"/>
		<htmlWidgetBuilder
		xmlns="java:org.metawidget.faces.component.html.widgetbuilder"/>
		</list>
		</widgetBuilders>
	</compositeWidgetBuilder>
	</widgetBuilder>
	<inspector>		
		<compositeInspector
			xmlns="java:org.metawidget.inspector.composite"
			config="CompositeInspectorConfig">
			<inspectors>
			<list>
			<metawidgetAnnotationInspector
				xmlns="java:org.metawidget.inspector.annotation"/>
			<propertyTypeInspector
				xmlns="java:org.metawidget.inspector.propertytype"/>
			<java5Inspector
				xmlns="java:org.metawidget.inspector.java5"/>
			<facesInspector
				xmlns="java:org.metawidget.inspector.faces"/>			
			<xmlInspector
				xmlns="java:org.metawidget.inspector.xml"
				config="XmlInspectorConfig"/>
			</list>
			</inspectors>
		</compositeInspector>
	</inspector>
</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. 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).

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 version 1.1_r1 of the Android SDK from http://code.google.com/android/download.html. Then, change to the installation directory (usually android-sdk-windows-1.1_r1) and run the emulator by opening a command prompt and typing:

tools\emulator

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 typing:

ant example-android-addressbook

(ensuring your build.properties is setup 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, use the arrow pad on the emulator to move to the All button, then click the center of the arrow pad. Select the Address Book application and press the center of the arrow pad again. The emulator displays a search filter (at the top) and lists existing Address Book entries (at the bottom) as in Figure 1.16.

Figure 1.16. Mobile Address Book opening screen

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]Tip
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.17.

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

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"
	paramSectionStyle="@style/section">
	
	<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.18.

Figure 1.18. Mobile Address Book Communications Dialog

Mobile Address Book Communications Dialog

1.3 Part 3 - Other Examples

The Metawidget distribution includes other examples showcasing particular features on particular platforms. These additional examples are not a required part of the tutorial and may be skipped. However, you may find them useful depending on which platform you use.

Swing Applet Address Book Example

The Swing Applet Address Book Example demonstrates using Metawidget in applets. The example is pre-built for you in examples\swing\applet\addressbook. If you've downloaded the source code distribution, you can build it yourself by typing:

ant example-swing-addressbook-applet

To run the applet, open the index.html file in a Web browser. The code is identical to the Swing Address Book covered in Part 2 of this tutorial, except it uses org.metawidget.example.swing.applet.AddressBookApplet instead of org.metawidget.example.swing.addressbook.MainFrame.

The notable feature of the example is how the applet is packaged. Metawidget is highly modular and has no mandatory third-party JAR dependencies. The example-swing-addressbook-applet Ant task builds only those Inspectors necessary for the Address Book application. The resulting metawidget-applet.jar is then further compressed using pack200 to around 50KB. This small download size makes Metawidget very viable for Applet-based environments.

GWT Hosted Mode Address Book Example

The examples\gwt\addressbook-gwt.war discussed in Part 2 of this tutorial demonstrates GWT running in GWT Web mode. Developers may prefer instead to run the example in GWT hosted mode as in Figure 1.19.

Figure 1.19. Address Book Example running in GWT Hosted Mode

Address Book Example running in GWT Hosted Mode

There are two ways to set up a hosted mode project. For Windows developers, there is an automated Ant build included in the source distribution (not the binary distribution). To run it, type:

cd examples\web\gwt
ant

You will need to either configure GWT_HOME and METAWIDGET_HOME environment variables or pass -Dgwt.home and -Dmetawidget.home arguments to Ant. The build will create an Eclipse project which can be imported by using Eclipse's File -> Import menu and choosing "Existing Projects into Workspace" (as described in the GWT Getting Started Guide).

Alternatively, to set up a hosted mode project manually:

  • Create a new GWT project using the usual method (described in the GWT Getting Started guide). First create a folder called, say, HostedAddressBook. Within that folder, run...

    projectCreator -eclipse HostedAddressBook

    ...and then...

    applicationCreator
    	-eclipse HostedAddressBook org.metawidget.example.gwt.addressbook.client.AddressBook
  • Delete the contents of HostedAddressBook\src\org\metawidget\example.

  • From the Metawidget source distribution, copy from the examples\java\org\metawidget\example folder the gwt and shared folders, and the GwtAddressBook.gwt.xml file, into HostedAddressBook\src\org\metawidget\example

  • From the Metawidget source distribution, copy from the examples\web\gwt\addressbook\WEB-INF folder the metawidget.xml and metawidget-metadata.xml files into HostedAddressBook/src

  • From the Metawidget source distribution, copy from the lib folder the ejb3-persistence.jar, hibernate-validator.jar and javax.servlet.jsp.jar files into a new folder called HostedAddressBook/lib

  • From the Metawidget binary distribution, copy metawidget.jar and examples\gwt\metawidget-gwt-client.jar into HostedAddressBook\lib

  • Modify HostedAddressBook\AddressBook.launch. Change...

    ...
    <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS"
    	value="-out www org.metawidget.example.gwt.addressbook.AddressBook/AddressBook.html"/>					
    ...

    ...to...

    ...
    <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS"
    	value="-out www org.metawidget.example.GwtAddressBook/AddressBook.html"/>					
    ...
  • In Eclipse, use the File -> Import menu and choose "Existing Projects into Workspace" (as described in the GWT Getting Started Guide). Select HostedAddressBook

  • In Eclipse, edit the Build Path of the new project and add all 5 JARs from HostedAddressBook\lib

You should now be able to run the GWT Address Book sample application in hosted mode.

Seam Example

The Seam Booking Example demonstrates updating an existing Seam application to use Metawidget, reducing boilerplate code. The example requires you to have previously downloaded Seam 2.1.1.GA and JBoss 4.2.3.GA, and you should be familiar with the existing Seam Booking application.

The example is located in examples\faces\seam\booking. It is not pre-built. To build it, change to the examples\faces\seam\booking folder and type:

ant

To run it, type:

cd \Applications\jboss-4.2.3.GA
bin\run

Open a Web browser to http://localhost:8080/seam-booking. The updated Metawidget Seam Booking Example looks very similar to the original, as in Figure 1.20, but uses significantly less boilerplate code.

Figure 1.20. Seam Booking with Metawidget

Seam Booking with Metawidget

The files modified for adding Metawidget support are in examples\faces\seam\booking. Most of the UI code in view\book.xhtml, view\confirm.xhtml and view\hotelview.xhtml has been replaced with a single Metawidget tag. Some annotations have been added to Hotel.java and Booking.java, though Metawidget also leverages the existing JPA and Hibernate Validator ones.

The example further demonstrates packaging Metawidget for multi-tier environments. The pack-as-frontend-backend Ant task in Metawidget's build.xml creates two JAR files: metawidget-frontend.jar for deployment in the WAR, and metawidget-backend.jar for deployment in the EJB layer. This ensures a clean separation between frontend and backend code, and is important for avoiding WAR/EJB classloading problems.

Groovy Example

The Seam Groovy Booking Example demonstrates updating an existing Seam Groovy application to use Metawidget, reducing boilerplate code. The example is a more advanced version of the previous Seam section, so you should work through that first.

The example is located in examples\faces\seam\groovybooking. It is not pre-built. To build it, change to the examples\faces\seam\groovybooking folder and type:

ant

To run it, type:

cd \Applications\jboss-4.2.3.GA
bin\run

Open a Web browser to http://localhost:8080/jboss-seam-groovybooking. As with the previous section, the updated Metawidget Seam Groovy Booking Example looks very similar to the original, but uses significantly less boilerplate code. Also, this time we are using Groovy to define our business classes. The biggest impact this has is in metawidget.xml, where the Inspectors have been configured to use a Groovy property style instead of a JavaBean property style.

Metawidget supports pluggable 'property styles' for JavaBean and Groovy property styles. Groovy properties differ from JavaBean properties in that their annotations are tied to the private member variable, rather than the getters and setters. The use of Groovy is configured per Inspector, as in examples\faces\seam\groovybooking\resources\WEB-INF\metawidget.xml:

<propertyTypeInspector config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<propertyStyle>
		org.metawidget.inspector.impl.propertystyle.groovy.GroovyPropertyStyle
	</propertyStyle>
</propertyTypeInspector>

jBPM Example

The Seam DVD Store Example demonstrates updating an existing Seam jBPM application to use Metawidget, reducing boilerplate code. The example requires you to have previously downloaded Seam 2.1.1.GA and JBoss 4.2.3.GA, and you should be familiar with the existing Seam DVD Store application.

The example is located in examples\faces\seam\dvdstore. It is not pre-built. To build it, change to the examples\faces\seam\dvdstore folder and type:

ant

To run it, type:

cd \Applications\jboss-4.2.3.GA
bin\run

Open a Web browser to http://localhost:8080/seam-dvdstore. As with the previous two sections, the updated Metawidget Seam DVD Store example looks very similar to the original, as in Figure 1.21, but uses significantly less boilerplate code.

Figure 1.21. Seam DVD Store with Metawidget

Seam DVD Store with Metawidget

This time, as well as generating UIComponents for business objects such as com.jboss.dvd.seam.Customer, Metawidget inspects jBPM files like newuser.jpdl.xml and checkout.jpdl.xml to generate the correct UICommand buttons for each screen.

Swing AppFramework Example

The Swing AppFramework Car Demo demonstrates using Metawidget with the Swing AppFramework. Metawidget can use Swing AppFramework's @Action annotation to identify actions, both amongst a business object's properties and in external controllers, and automatically generate JButtons for them.

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

ant example-swing-appframework

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

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

java -jar appframework-swing.jar

The opening screen displays two fields to allow you to enter the make and type of a car. You can also optionally add an owner by clicking the Add an Owner button, or save the car using the Save button.

The Add an Owner button is generated by Metawidget based on the addOwner method in the Car class (which has been annotated @org.jdesktop.application.Action). The Save button is generated based on the save method in the CarApplication class (also annotated @Action). Two different Metawidgets are used in the example: one pointed at the Car class, the other at the CarApplication class.

Metawidget supports pluggable 'action styles'. The use of Swing AppFramework is configured per Inspector, as in examples/src/java/org/metawidget/example/swing/appframework/metawidget.xml from the source distribution:

<metawidgetAnnotationInspector
	config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<actionStyle>
		org.metawidget.inspector.impl.actionstyle.swing.SwingAppFrameworkActionStyle
	</actionStyle>
</metawidgetAnnotationInspector>

As a further feature, after the Add an Owner button is clicked it disappears. This is acheived by using JexlInspector to introduce an expression language for Swing similar to JSP's EL. The method is annotated...

@Action( name = "add" )
@UiJexlAttribute( name = HIDDEN, value = "${this.owner != null}" )
public void addOwner() {
	mOwner = new Owner();
	fireActionEvent( "addOwner" );
}

...such that the button gets hidden when the car has an owner.

Scala Example

The Scala Animal Races Example demonstrates using SwingMetawidget together with Scala and MigLayout.

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

ant example-swing-animalraces

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

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

java -jar animalraces-swing.jar

The screen displays fields to allow you to change the name, speed and type of each animal as well buttons to start and stop the race.

Figure 1.22. Scala and MigLayout with Metawidget

Scala and MigLayout with Metawidget

Animal Races' whimsical User Interface demonstrates how Metawidget's goal of not 'owning' the UI allows multiple Metawidgets to be combined for unconventional UIs. There are three Metawidgets across the top (one for each animal in the race), and a fourth Metawidget for the buttons at the bottom.

The top three Metawidgets all use MigLayout. Because Metawidget does not hide the underlying UI framework, using MigLayout allows the Animal Races code to easily pad the Metawidget:

metawidget.setLayoutClass( MigLayout.class );
metawidget.setToInspect( animal );
((MigLayout) metawidget.getLayout()).setLayoutConstraints( new LC().insets( "10" ));

The Animal Races code is written purely in Scala, located at examples/src/scala/org/metawidget/example/swing/animalraces/AnimalRaces.scala. It uses ScalaPropertyStyle to allow Metawidget to inspect Scala-based business objects:

<metawidgetAnnotationInspector xmlns="java:org.metawidget.inspector.annotation"
		config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
		<propertyStyle>
			org.metawidget.inspector.impl.propertystyle.scala.ScalaPropertyStyle
		</propertyStyle>			
	</metawidgetAnnotationInspector>

In addition, it uses BeanUtilsBinding.PROPERTYSTYLE_SCALA to bind JComponents to Scala-based business objects:

val metawidget = new SwingMetawidget()
metawidget.setPropertyBindingClass( classOf[ BeanUtilsBinding ])
metawidget.setParameter( "propertyStyle", BeanUtilsBinding.PROPERTYSTYLE_SCALA )
metawidget.setToInspect( animal )

1.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.

2. Architecture

There is a large diversity of front-end and back-end technologies. Metawidget strives to unify this diversity with two over-arching concepts: WidgetBuilders that create widgets for specific front-end frameworks (SwingWidgetBuilder, HtmlWidgetBuilder, etc.) and Inspectors that inspect specific back-end architectures (PropertyTypeInspector, JpaInspector, etc.). These two concepts are co-ordinated by a platform-specific Metawidget class.

2.1 Metawidgets

Metawidget comes with a native component for each popular UI framework.

Interface

Most UI frameworks require widgets inherit one of their base classes, such as javax.swing.JComponent or javax.faces.UIComponent. As Java does not support multiple inheritance, this means there cannot be a common 'Metawidget base class' per se.

In addition, all Metawidgets support roughly the same functionality but different UI frameworks have different in-built capabilities. For example, JSF has UIComponent.setRenderer for choosing different layouts for the same widget, whereas SwingMetawidget has to roll its own setLayout method. This means there cannot be a common 'Metawidget interface' either.

Therefore, Metawidgets are not required to extend any base class or implement any interface. However, they all follow roughly the same design, with roughly the same method names:

  1. setToInspect is called to set the Object for inspection

  2. buildWidgets is called to begin the process. It first calls Inspector.inspect to return a DOM of inspection results

  3. buildWidgets calls WidgetBuilder.buildWidget to choose a suitable widget for the top-level element of the DOM (based on its @type attribute). If WidgetBuilder.buildWidget returns such a widget, skip to 6

  4. if WidgetBuilder.buildWidget returns null for the top-level element, call buildCompoundWidget to iterare over each child of the top-level element.

  5. for each child, call WidgetBuilder.buildWidget and add the returned widget to the Metawidget. If WidgetBuilder.buildWidget returns null for a child, create a nested Metawidget.

  6. widget creation can be overridden if there are existing child widgets with suitably matching attributes

  7. as a final step, the created widgets are passed to a layout and/or binding mechanism. Layout and binding can be configured by arbitrary parameters passed through the Metawidget to the layout/binding. Layouts can further by adorned with facet widgets passed through the Metawidget.

For those looking to write their own Metawidget (say, for a currently unsupported platform) there is a MetawidgetMixin class that implements the above steps 2-6 for you, see the section called “MetawidgetMixin”. All of the supplied Metawidgets are implemented using this class.

Customizing Look and Feel

As much as possible, Metawidgets defer to the existing Look and Feel technology of their native UI framework. For example, HtmlMetawidget uses HTML/CSS, SwingMetawidget uses Swing Look-and-Feels, and AndroidMetawidget uses Android styles and themes.

The one area Metawidget does control is how the widgets it creates are laid out. Typically this is in a tabular 'one column for the label, one column for the widget' format, but this is pluggable.

Metawidgets come with different Layout classes (LayoutRenderer classes for JSF) that can arrange the widgets in different ways, and these are set on the Metawidget in a framework-specific way. For example, JSF uses <m:metawidget rendererType=""> whereas SwingMetawidget uses setLayoutClass. Where possible, the layout classes defer back to the capabilities of the native framework. For example, Swing's GroupLayout or Android's TableLayout.

Some layouts will add localized labels and section headings to the widgets, whereas other layouts may leave them unadorned. Different Layouts may support different parameters (for example, a TableLayout may support numberOfColumns) and these are passed from the Metawidget to the layout in a generic fashion. For example, JSF uses <f:param name="" value=""> tags, whereas SwingMetawidget has a setParameter(name, value) method.

Overriding Widget Creation

Metawidget tries to automate much of the widget creation, but provides many hooks to customize the process:

  • stub child widgets can be used to suppress widget creation entirely or to replace automatic widget creation with one or more other widgets with different bindings

  • layouts are pluggable

  • parameters set on a Metawidget pass through to the chosen layout/binding to configure settings such as sectionStyle and numberOfColumns

  • facet child widgets pass through to the chosen layout as decorations (such as button bars)

  • WidgetBuilders can be plugged in to the pipeline to fine-tune widget creation

2.2 inspection-result

The inspection-result XML format is the 'glue' that holds everything together: the Metawidgets request it, the Inspectors provide it, and the WidgetBuilders base their choice of widgets on it.

It is a very simple format. As an example:

<inspection-result version="1.0">
	<entity type="com.myapp.Person">
		<property name="name" required="true"/>
		<property name="age" minimum-value="0"/>
	</entity>
</inspection-result>

Only a handful of XML attributes are mandatory (see inspection-result-1.0.xsd). Most, such as retired and minimum-value, are provided at the discretion of the Inspector and recognised at the discretion of the WidgetBuilder. This loose coupling allows Inspectors to evolve independently for new types of metadata, and WidgetBuilders to evolve independently with new types of widgets.

2.3 Widget Builders

WidgetBuilders decouple the process of choosing widgets based on inspection results. This section covers WidgetBuilders in general. For in-depth documentation of individual WidgetBuilders see Chapter 4, Widget Builders.

Interface

All WidgetBuilders must implement the WidgetBuilder interface. This is a simple interface that defines only one method:

W buildWidget( String elementName, Map<String, String> attributes, M metawidget )

Where W is a widget type (such as JComponent or UIComponent) and M is a Metawidget type (such as SwingMetawidget or UIMetawidget).

Each WidgetBuilder must look to the elementName, which is typically just 'property' or 'action' from the inspection-result, and to the various attributes and instantiate an appropriate widget. WidgetBuilders can use the given metawidget to help them if needed (for example, to access a UI context with which to instantiate widgets). Typically the WidgetBuilders do not need to configure the widget beyond simply instantiating it: the job of setting ids, attaching validators, configuring bindings and so forth is done by the Metawidget.

Usage

Unless explicitly specified, each Metawidget will instantiate a default WidgetBuilder to match the target platform. For example, SwingMetawidget will by default instantiate a SwingWidgetBuilder.

This default behaviour can be overridden either in code:

metawidget.setWidgetBuilder( new MyWidgetBuilder() );

Or via metawidget.xml

<swingMetawidget xmlns="java:org.metawidget.swing">
	<widgetBuilder>
		<myWidgetBuilder xmlns="java:com.myapp"/>
	</widgetBuilder>
</swingMetawidget>

This allows easy plugging in of third-party widget libraries. Note that overriding the default means the default is no longer instantiated. In the example above, this would mean MyWidgetBuilder is used but SwingWidgetBuilder is not. This is usually not what you want, because MyWidgetBuilder will be focussed on a particular third party library and will want to leave standard widgets to SwingWidgetBuilder.

To achieve this, use CompositeWidgetBuilder:

metawidget.setWidgetBuilder( new CompositeWidetBuilder( new CompositeWidgetBuilderConfig()
	.setWidgetBuilders(
		new MyWidgetBuilder(), new SwingWidgetBuilder()
	)));

Or via metawidget.xml

<swingMetawidget xmlns="java:org.metawidget.swing">
<widgetBuilder>
	<compositeWidgetBuilder
		xmlns="urn:java:org.metawidget.widgetbuilder.composite"
		config="CompositeWidgetBuilderConfig">
		<widgetBuilders>
			<list>
			<myWidgetBuilder xmlns="java:com.myapp"/>
			<swingWidgetBuilder xmlns="java:org.metawidget.swing.widgetbuilder"/>
			</list>
		</widgetBuilders>
	</compositeWidgetBuilder>
</widgetBuilders>
</swingMetawidget>

Immutability

All WidgetBuilders are required to be threadsafe and immutable. This means you only need a single instance of a WidgetBuilder for your entire application. If you are using metawidget.xml then ConfigReader takes care of this for you, but if you are instantiating WidgetBuilders in Java code you should reuse instances.

2.4 Inspectors

Inspectors decouple the process of generating inspection results from back-end metadata. This section covers Inspectors in general. For in-depth documentation of individual Inspectors see Chapter 5, Inspectors.

Interface

All Inspectors must implement the Inspector interface. This is a simple interface that defines only one method:

String inspect( Object toInspect, String type, String... names );

Each Inspector must look to the type parameter and the names array. These form a path into the business object domain model. For example the type may be 'com.myapp.Person' and the names may be 'address' and 'street'. This would form a path into the domain model of com.myapp.Person/address/street (ie. return information on the 'street' property within the 'address' property of the 'Person' type).

Depending on the type of Inspector, it may use the given toInspect to access the runtime object of the given type. Or it may ignore the toInspect and look up information on the given type from an XML file or a database schema.

Usage

Unless explicitly specified, each Metawidget will instantiate a default Inspector. Typically this will be a CompositeInspector composed of a MetawidgetAnnotationInspector and a PropertyTypeInspector.

This default behaviour can be overridden either in code:

metawidget.setInspector( new MyInspector() );

Or via metawidget.xml

<swingMetawidget xmlns="java:org.metawidget.swing">
	<inspector>
		<myInspector xmlns="java:com.myapp"/>
	</inspector>
</swingMetawidget>

This allows easy plugging in of alternate Inspectors. Note that overriding the default means the default is no longer instantiated. In the example above, this would mean MyInspector is used but MetawidgetAnnotationInspector and PropertyTypeInspector are not. This is usually not what you want, because MyInspector will be focussed on a particular type of metadata and will want to leave other metadata to other inspectors.

To achieve this, use CompositeInspector:

metawidget.setInspector( new CompositeInspector( new CompositeInspectorConfig()
	.setWidgetBuilders(
		new MetawidgetAnnotationInspector(),
		new PropertyTypeInspector(),
		new MyInspector()
	)));

Or via metawidget.xml

<swingMetawidget xmlns="java:org.metawidget.swing">
<inspector>
	<compositeInspector
		xmlns="urn:java:org.metawidget.inspector.composite"
		config="CompositeInspectorConfig">
		<inspectors>
			<list>
				<metawidgetAnnotationInspector
					xmlns="java:org.metawidget.inspector.annotation" />
				<propertyTypeInspector
					xmlns="java:org.metawidget.inspector.propertytype"/>			
				<myInspector xmlns="java:com.myapp"/>
			</list>
		</inspectors>
	</compositeInspector>
</inspector>
</swingMetawidget>

Immutability

All Inspectors are required to be threadsafe and immutable. This means you only need a single instance of an Inspectors for your entire application. If you are using metawidget.xml then ConfigReader takes care of this for you, but if you are instantiating Inspectors in Java code you should reuse instances.

2.5 metawidget.xml

metawidget.xml is used to configure a Metawidget without writing Java code. This can be useful in environments with intermediate languages that shield the developer from the raw code, such as JSPs or Facelets. It can also be useful as a single place for configuring multiple Metawidgets, such as on multiple pages of a Web application.

The metawidget.xml format, as parsed by org.metawidget.inspector.ConfigReader, is specialised for configuring Metawidget instances. The following sections explore some of the features of the XML format.

Construct New Objects

The XML format can be used to construct new instances of objects. The XML element name is the Java class name and the XML namespace is the Java package. The following example constructs a org.metawidget.swing.SwingMetawidget.

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

	<swingMetawidget xmlns="java:org.metawidget.swing"/>
	
</metawidget>			

Call Setter Methods

Within an object, the XML format can call setXXX methods. These are generally single-parameter, but multi-parameter methods are also supported. The following example calls the setOpaque and setParameter methods of SwingMetawidget. The latter takes two arguments:

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

	<swingMetawidget xmlns="java:org.metawidget.swing">
		<opaque>
			<boolean>true</boolean>
		</opaque>
		<parameter>
			<string>numberOfColumns</string>
			<int>2</int>
		</parameter>		
	</swingMetawidget>
	
</metawidget>			

Construct Primitive Types

As alluded to in the previous section, when calling setXXX methods the XML format can construct and pass simple types. The previous example used boolean, string and int. Also supported are:

Element name Java type
<boolean> Java boolean primitive
<bundle> uses ResourceBundle.getBundle to construct a ResourceBundle
<class> Java Class
<file> uses FileInputStream to open the file as an InputStream
<int> Java int primitive
<null> Java null value
<pattern> Java Pattern
<resource> uses Class.getResourceAsStream to open the resource as an InputStream
<string> Java String
<url> uses URL.openStream to open the URL as an InputStream

Understand Immutability

Metawidget dictates all Inspectors and WidgetBuilders be threadsafe and immutable. This is an important design decision as it means a single instance of an Inspector or WidgetBuilder can be reused across an entire application. Immutabilty is enforced by not having any setXXX methods on the Inspectors or WidgetBuilders themselves. Rather, the setXXX methods are called on Config objects, which are then passed to the Inspector or WidgetBuilder's constructor. Once constructed, the Inspector/WidgetBuilder cannot be changed.

The XML format understands this distinction by way of a config attribute. The following example configures an immutable Inspector. The setInspectors method is called on org.metawidget.inspector.composite.CompositeInspectorConfig and then passed to CompositeInspector:

<compositeInspector xmlns="java:org.metawidget.inspector.composite"
	config="CompositeInspectorConfig">
	<inspectors>
		<list>
			<metawidgetAnnotationInspector
				xmlns="java:org.metawidget.inspector.annotation" />
			<propertyTypeInspector
				xmlns="java:org.metawidget.inspector.propertytype"/>
		</list>
	</inspectors>
</compositeInspector>

Having constructed an immutable object, the XML format can reliably cache the instance and reuse it for future configurations.

3. Metawidgets

Metawidget ships with native widgets for different UI frameworks. Whilst all Metawidget widgets are broadly similar, they are tailored to take advantage of their native environment. This chapter examines each Metawidget widget in detail.

3.1 Desktop Metawidgets

SwingMetawidget

SwingMetawidget is a Swing component. For an introduction to SwingMetawidget, see Section 1.1, “Part 1 - The First Metawidget Application” and the section called “Desktop Address Book”.

Installation

There are two steps to installing SwingMetawidget within a Swing application:

  1. Add metawidget.jar to your CLASSPATH.

  2. Add an metawidget.xml to your CLASSPATH (or you may prefer to configure the Metawidget programmatically, as detailed in Section 1.1, “Part 1 - The First Metawidget Application”).

Property Binding

Swing does not include an automatic JComponent to Object binding mechanism, but Metawidget supports third-party alternatives via setPropertyBindingClass.

Beans Binding

org.metawidget.swing.propertybinding.beansbinding.BeansBinding supports the various Beans Binding update strategies by calling metawidget.setParameter( UpdateStrategy.class, UpdateStrategy.READ_WRITE ). If set to READ or READ_WRITE, the object being inspected must provide PropertyChangeSupport.

Action Binding

Swing supplies javax.swing.Action for binding JButtons to backing classes, and this is typically combined with Java-based reflection to support runtime binding. This is exactly what the default action binding, ReflectionBinding, does.

However, Metawidget makes action bindings pluggable to support other use cases. In particular, use cases where there is no backing class, and instead the JButton should invoke, say, an RPC call. Implement your own pluggable binding by extending BaseActionBinding and use it by calling:

myMetawidget.setActionBindingClass( myActionBinding.class );

Customizing Look and Feel

Since inception, Swing has had built-in, and extensive, Look and Feel support. Metawidget does not overlap this. For layouts, Swing supports a multitude of LayoutManagers. Metawidget leverages these, but automates them to construct UIs automatically.

3.2 Web Metawidgets

Hidden Fields

Many Web applications store their data at the HttpServletRequest level, not at the HttpSession level. Using session-level state (or, ideally, a UI framework that supports some kind of 'conversation'-level state) is safer than passing variables to and from the client in hidden HTML fields. However, Web Metawidgets support that approach for those that need it through setCreateHiddenFields( true ).

GwtMetawidget

GwtMetawidget is a client-side, JavaScript widget for GWT. Despite the limitations of the JavaScript environment, it supports reflection, annotations, pluggable layouts and data binding. For an introduction to GwtMetawidget, see the section called “Web Address Book” and the section called “GWT Hosted Mode Address Book Example”.

Installation

There are five steps to installing Metawidget within a GWT application:

  1. Update the application's .gwt.xml module to include Metawidet:

    <module>								
    	<inherits name="org.metawidget.GwtMetawidget" />
    	...
    </module>
  2. Include both metawidget.jar and examples\gwt\metawidget-gwt-client.jar in the CLASSPATH during the GWTCompiler phase. This provides the GwtMetawidget component.

  3. Add metawidget.jar into WEB-INF/lib. This provides the GwtRemoteInspectorImpl servlet.

  4. Update the application's web.xml to include GwtRemoteInspectorImpl:

    <web-app>
    	...
    	<servlet>
    		<servlet-name>metawidget-inspector</servlet-name>
    		<servlet-class>
    			org.metawidget.inspector.gwt.remote.server.GwtRemoteInspectorImpl
    		</servlet-class>
    	</servlet>
    
    	<servlet-mapping>
    		<servlet-name>metawidget-inspector</servlet-name>
    		<url-pattern>/metawidget-inspector</url-pattern>
    	</servlet-mapping>
    </web-app>
  5. Add an metawidget.xml into WEB-INF. Or, if you're running under the GWT Development Shell, add an metawidget.xml to the root of a CLASSPATH entry.

A working example of all five steps can be found in addressbook-gwt.war included in the binary distribution. You may also find the example-gwt-addressbook Ant task in the source distribution's build.xml useful.

Reflection and Annotations

GwtMetawidget leverages Metawidget's separate Inspector/renderer architecture and AJAX to perform server-side inspection as in Figure 3.1. This allows GwtMetawidget to reflect properties and inspect annotations of business objects, even though JavaScript supports neither.

Figure 3.1. GwtMetawidget uses AJAX to perform server-side inspection

GwtMetawidget uses AJAX to perform server-side inspection

The process is:

  1. instantiate the business object on the client-side as normal (ie. as JavaScript)

  2. give the business object to GwtMetawidget (a client-side, JavaScript GWT Widget)

  3. GwtMetawidget uses AJAX to pass the business object to the server

  4. the server, using Java, runs all the Inspectors (including reflection and annotations)

  5. the server returns the inspection results as an XML document

  6. GwtMetawidget uses JavaScript to render the HTML widgets

Note that steps 3 and 5 (the AJAX call to and from the server) are the most costly in terms of performance. Techniques to improve GWT performance are discussed in Section 7.2, “Rebinding”.

Pluggability

Like other Metawidgets, GwtMetawidget supports pluggable inspectors, layout managers and binding implementations. However, the other Metawidgets do this using Class.newInstance, which JavaScript does not support.

Instead, GwtMetawidget supplies GWT Generators in the form of org.metawidget.gwt.generator.FactoryGenerator. At compile-time, each generator scans all the classes included in the application and statically generates code to instantiate them. This is transparent to the application code, which can simply do:

metawidget.setLayoutClass( TableLayout.class )

Property Binding

Like most other Metawidgets, GwtMetawidget supports property binding. Property binding generally requires reflection, and GWT recommends using Generators to achieve this. As of GWT 1.5, however, much of the burden of implementation rests on the developer.

GwtMetawidget automates this burden by supplying a SimpleBinding implementation. This implementation is pluggable, so may be swapped out as and when later releases of GWT more fully support data binding.

SimpleBinding expects every domain object to be wrapped with a SimpleBindingAdapter. The supplied SimpleBindingAdapterGenerator automates this process. To configure it, add the following to the application-name.gwt.xml file...

<generate-with
	class="org.metawidget.gwt.generator.propertybinding.simple.SimpleBindingAdapterGenerator">
	<when-type-assignable class="org.metawidget.example.shared.addressbook.model.Contact"/>
</generate-with>

...and in the application code...

metawidget.setPropertyBindingClass(SimpleBinding.class);
SimpleBindingAdapter<Contact> adp = (SimpleBindingAdapter<Contact>) GWT.create(Contact.class);
SimpleBinding.registerAdapter( Contact.class, adp );

Action Binding

GWT supplies com.google.gwt.user.client.ui.ClickListener for binding Buttons to backing classes. It is futher possible to combine this with a little JSNI trickery to support runtime binding. This is exactly what the default action binding, JsniBinding, does.

However, Metawidget makes action bindings pluggable to support other use cases. In particular, use cases where there is no backing class, and instead the Button should invoke, say, an RPC call. Implement your own pluggable binding by extending BaseActionBinding and use it by calling...

myMetawidget.setActionBindingClass( myActionBinding.class );

...which overrides the default JsniBinding. Behind the scenes, GwtMetawidget has a FactoryGenerator which takes care of generating JavaScript code to instantiate your ActionBinding.

Client-Side Inspection

As noted in the section called “Reflection and Annotations” by default GwtMetawidget uses server-side Inspectors. This allows the full power of Java-based reflection but carries the performance cost of an AJAX call. This cost can be mitigated by using rebinding (see Section 7.2, “Rebinding”), but there is another way: inspection can be performed client-side, with no AJAX calls.

Setting up a client-side Inspector is very easy. The default GwtMetawidget Inspector is GwtRemoteInspectorProxy, which is itself a client-side Inspector (one that makes a remote call to GwtRemoteInspectorImpl). To replace this default, simply implement your own Inspector:

public class MyClientSideInspector
	implements Inspector {
	public String inspect( Object toInspect, String type, String... names )
		throws InspectorException {
		return ...some XML string...
	}
}

Make sure this Inspector is located under the client folder of your GWT application so that it is compiled by the GWTCompiler into JavaScript. Use this Inspector by doing...

myGWTMetawidget.setInspectorClass( MyClientSideInspector.class )

...which overrides the default GwtRemoteInspectorProxy. Behind the scenes, GwtMetawidget has a FactoryGenerator which takes care of generating JavaScript code to instantiate your inspector.

That's it! Of course, JavaScript-based inspection is severely limited compared to Java-based inspection, but you may be able to find tricks that work for your environment. For example, you can try...

for( var property in myObject ) { alert( myObject[property] ); }

...or perhaps you already have the XML you need available somewhere else in your client-side code.

UIMetawidget (JSF)

UIMetawidget is a Java Server Faces component. For an introduction to UIMetawidget, see the section called “Web Address Book” and the section called “Seam Example”.

Installation

There are three steps to installing UIMetawidget within a JSF application:

  1. Add metawidget.jar into WEB-INF/lib.

  2. Add an metawidget.xml into WEB-INF.

  3. Add a tag library descriptor to the top of your JSP page...

    <%@ taglib uri="http://metawidget.org/faces" prefix="m" %>
    	...
    	<m:metawidget value="#{foo}"/>
    	...

    ...or Facelets page...

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"
    	...
    	xmlns:m="http://metawidget.org/faces">
    	...
    	<m:metawidget value="#{foo}"/>
    	...

Customizing Look and Feel

One of JSF's most important Look and Feel technologies is CSS. Metawidget supports several approaches to suit different needs.

By convention, JSF's HTML widgets (HtmlInputText, HtmlSelectBooleanCheckbox, etc) define style and styleClass attributes for applying CSS styles and classes to their output. HtmlMetawidget follows this convention. When expanding to a single widget (such as an HtmlInputText) the CSS styles are applied to it. When expanding to multiple widgets, all widgets have the same CSS styles applied to them.

Another important JSF Look and Feel technology is Renderers. Whilst often Renderers are discussed in the context of rendering the same widget to different platforms (eg. HTML or WML), they can equally be used to render the same widget to the same platform but in different layouts.

HtmlTableLayoutRenderer is the default LayoutRenderer. It further defines parameters such as tableStyle, labelStyle and columnStyleClasses parameters (see the JavaDoc for a complete list). The latter is a comma separated list of CSS style classes to be applied to table columns. The first style class is the label column, the second the widget column, and the third the 'required' column. Further style classes may be used for multi-column layouts. You can get quite far using, for example:

.table-component-column input { width: 100%; }

..this approach has the advantage of automatically applying to every widget, so overridden widgets do not have to explicitly set styleClass information. However, not all Web browsers support fine-grained CSS selectors such as...

.table-component-column input[type="button"] { width: auto; }

...in which case it may be better to switch to using styleClass on HtmlMetawidget itself.

Other supplied LayoutRenderers include div and simple (see the JavaDoc, and the META-INF/faces-config.xml in metawidget.jar for a complete list).

Validation

By default, UIMetawidget uses StandardValidator, which adds required, RangeValidator and LengthValidator to all widgets.

You can implement your own validators by extending org.metawidget.faces.component.validator.Validator, or disable validation by setting validatorClass="".

Setting Global Defaults

If you find yourself setting the same attributes and parameters on every Metawidget tag, consider subclassing HtmlMetawidget and setting the defaults there:

public class MyDefaultedMetawidget extends HtmlMetawidget {
	...
	public MyDefaultedMetawidget() {
		getAttributes().put( "styleClass", "myStyleClass" );
		setValidatorClass( ... );
	}

	public void encodeBegin( FacesContext context )	{
		if ( FacesUtils.findParameterWithName( this, "tableStyleClass" ) == null ) {
			UIParameter parameter = new UIParameter();
			parameter.setName( "tableStyleClass" );
			parameter.setValue( "table-style" );
			getChildren().add( parameter );
		}
		
		super.encodeBegin( context );
	}
}

SpringMetawidget

SpringMetawidget is a Spring taglib. For an introduction to SpringMetawidget, see the section called “Web Address Book”.

Installation

There are three steps to installing SpringMetawidget within a Spring application:

  1. Add metawidget.jar into WEB-INF/lib.

  2. Add an metawidget.xml into WEB-INF.

  3. Add a tag library descriptor to the top of your JSP page:

    <%@ taglib uri="http://metawidget.org/spring" prefix="m" %>
    	...
    	<m:metawidget value="#{foo}"/>
    	...

Customizing Look and Feel

JSP-based technologies do not distinguish between what a widget is versus how it is rendered. Instead, SpringMetawidget mimics this by providing loosely coupled Layout classes. Each layout can further be configured by using specific param tags.

Overriding Widget Creation

With regard to overriding widget creation, JSP-based technologies such as Spring lack some component-based features. Specifically, whilst it is possible for JSP tags to reference their parent (using TagSupport.findAncestorWithClass), they have no way to enumerate their children. Therefore, it is not possible to directly support arbitrary child tags within SpringMetawidgetTag. As a next best thing, Metawidget includes StubTag for wrapping arbitrary tags. It also supports wrapping arbitrary HTML.

StrutsMetawidget

StrutsMetawidget is a Struts taglib. For an introduction to StrutsMetawidget, see the section called “Web Address Book”.

Installation

There are three steps to installing StrutsMetawidget within a Struts application:

  1. Add metawidget.jar into WEB-INF/lib.

  2. Add an metawidget.xml into WEB-INF.

  3. Add a tag library descriptor to the top of your JSP page:

    <%@ taglib uri="http://metawidget.org/struts" prefix="m" %>
    	...
    	<m:metawidget value="#{foo}"/>
    	...

Customizing Look and Feel

JSP-based technologies do not distinguish between what a widget is versus how it is rendered. Instead, StrutsMetawidget mimics this by providing loosely coupled Layout classes. Each layout can further be configured by using specific param tags.

Overriding Widget Creation

With regard to overriding widget creation, JSP-based technologies such as Struts lack some component-based features. Specifically, whilst it is possible for JSP tags to reference their parent (using TagSupport.findAncestorWithClass), they have no way to enumerate their children. Therefore, it is not possible to directly support arbitrary child tags within SpringMetawidgetTag. As a next best thing, Metawidget includes StubTag for wrapping arbitrary tags. It also supports wrapping arbitrary HTML.

Troubleshooting

I get "Cannot find bean org.apache.struts.taglib.html.BEAN in any scope"

StrutsMetawidget creates native Struts widgets, such as <html:text>, but does not create the surrounding Struts form. Make sure your Metawidget tag is enclosed in a <html:form> tag and the Struts HTML taglib is included at the top of the page.

3.3 Mobile Metawidgets

AndroidMetawidget

AndroidMetawidget is an Android widget. For an introduction to AndroidMetawidget, see the section called “Mobile Address Book”.

3.4 Implementing Your Own Metawidget

Metawidget creates widgets native to a particular UI framework. Having to implement your own Metawidget should be far less common than having to implement your own Inspector or WidgetBuilder, but if your chosen UI framework is not supported 'out of the box' you may need to implement your own.

Metawidgets are not required to extend any base class or implement any interface. However, it is recommended developers familiarize themselves with existing Metawidgets (such as UIMetawidget) to make their API similar. Whilst there is no one Metawidget base class, a number of convenience classes are provided:

MetawidgetMixin

Mixins are a frequently used workaround for multiple inheritance in Java. Classes extend the mixin as an inner class and override its methods as needed.

All the built-in Metawidgets use MetawidgetMixin to ease their implementation. The mixin provides pre-built functionality such as deciding when to use single versus compound widgets, support for overriding widgets, incorporating stubs, and changing between read-only and active modes.

Android

Android already defines a separation between Views and ViewGroups. org.metawidget.android.widget.layout.Layout and its subclasses automate the use of existing Android ViewGroups.

Java Server Faces

For frameworks based on JSF, org.metawidget.faces.component.UIMetawidget provides base widget functionality. See org.metawidget.faces.component.html.HtmlMetawidget for example usage.

JSF already defines a clean separation between widgets and their renderers. org.metawidget.faces.renderkit.LayoutRenderer and its subclasses leverage this to support different layouts.

Java Server Pages

For frameworks based on JSP, org.metawidget.jsp.tagext.MetawidgetTag and the more commonly used org.metawidget.jsp.tagext.html.BaseHtmlMetawidgetTag provide base taglib functionality. See StrutsMetawidgetTag for example usage.

MetawidgetTag also defines a clean separation between choosing widgets and laying them out. org.metawidget.jsp.tagext.Layout and its subclasses can perform the layout for all JSP-based frameworks.

Swing

Swing already defines a clean separation between widgets and layout managers. org.metawidget.swing.layout.Layout and its subclasses automate the use of existing Swing LayoutManagers.

4. Widget Builders

This chapter covers each WidgetBuilder in detail. For an explanation of how WidgetBuilders fit into the overall architecture of Metawidget, see Chapter 2, Architecture

Throughout this section when we refer to, say, 'fields with a maximum value' this is a shorthand way of saying 'fields for which the Inspectors returned a maximum-value attribute'. Quite which Inspector returned the value, and based on what, is covered in Chapter 5, Inspectors: it could be based on a Hibernate Validator @Max annotation, or an intRange block in a Commons Validator XML file, or some other source.

4.1 Desktop Widget Builders

SwingWidgetBuilder

SwingWidgetBuilder is the default WidgetBuilder for SwingMetawidget. It instantiates the following widgets for the following types of fields:

Widget Type of field
javax.swing
JButton Actions (except read-only actions)
JCheckBox Primitive booleans and required Booleans
JComboBox Fields with lookups
JLabel Read-only fields (except Collections and masked fields)
JPanel Read-only masked fields
JPasswordField Masked fields (except when read-only)
JSlider Primitives (except boolean and char) that have both a minimum and a maximum value
JSpinner Primitives (except boolean and char) that have only one or neither minimum/maximum value
JTextArea Large String fields. The JTextArea is automatically wrapped in a JScrollPane
JTextField Primitives (except booleans), Characters, Dates, Strings, and fields of unknown type
org.metawidget.swing
Stub Hidden fields, read-only actions and Collections

SwingXWidgetBuilder

SwingXWidgetBuilder is a pluggable WidgetBuilder for the SwingX library. It is intended to be used in conjunction with the default SwingWidgetBuilder. It instantiates the following widgets for the following types of fields:

Widget Type of field
org.jdesktop.swingx
JXDatePicker Dates

4.2 Web Widget Builders

DisplayTagWidgetBuilder

DisplayTagWidgetBuilder is a pluggable WidgetBuilder for the DisplayTag library. It is intended to be used in conjunction with the JSP HtmlWidgetBuilder, SpringWidgetBuilder or StrutsWidgetBuilder. It instantiates the following widgets for the following types of fields:

Widget Type of field
org.displaytag.tags
TableTag Collections and arrays (except if hidden, or if have a lookup). The columns in the table are based on inspecting the component type of the array or the parameterized type of the Collection (if neither can be determined, the table with only have a single column)

GwtWidgetBuilder

GwtWidgetBuilder is the default WidgetBuilder for GwtMetawidget. It instantiates the following widgets for the following types of fields:

Widget Type of field
com.google.gwt.user.client.ui
Button Actions (except read-only actions)
CheckBox Primitive booleans and required Booleans
Label Read-only fields (except Collections and masked fields)
ListBox Fields with lookups
PasswordTextBox Masked fields (except when read-only)
SimplePanel Read-only masked fields
TextArea Large String fields
TextBox Primitives (except booleans), Characters, Dates, Strings, and fields of unknown type. If the field has a maximum length, calls setMaxLength
org.metawidget.gwt.client.ui
Stub Hidden fields, read-only actions and Collections

HtmlWidgetBuilder (JSF)

HtmlWidgetBuilder is the default WidgetBuilder for the JSF HtmlMetawidget. It instantiates the following widgets for the following types of fields:

Widget Type of field
javax.faces.component.html
HtmlCommandButton Actions (except read-only actions)
HtmlDataTable Lists, DataModels and arrays (except if hidden, or if have a lookup). The columns in the table are based on inspecting the component type of the array or the parameterized type of the List (if neither can be determined, the table with only have a single column)
HtmlInputHidden Hidden fields when HtmlMetawidget.setCreateHiddenFields is set to true
HtmlInputSecret Masked fields (except when read-only)
HtmlInputText Primitives (except booleans), Characters, Dates, Strings, and fields of unknown type. If the field has a maximum length, calls setMaxlength
HtmlInputTextarea Large String fields
HtmlOutputText Read-only fields (except Collections and masked fields)
HtmlSelectBooleanCheckbox Primitive booleans and required Booleans
HtmlSelectManyCheckbox Lists or Arrays with a lookup
HtmlSelectOneListbox Fields with lookups
org.metawidget.faces.component
HtmlLookupOutputText Read-only fields with lookup labels
UIStub Hidden fields, read-only actions, read-only masked fields and Collections

HtmlWidgetBuilder (JSP)

HtmlWidgetBuilder is the default WidgetBuilder for the JSP HtmlMetawidgetTag. Since JSP has only a light component model (ie. HTML tags such as <input> and <select> are just Strings, not modelled as JSP tags), HtmlWidgetBuilder simply returns String-based fragments of HTML. It returns the following fragments for the following types of fields:

Widget Type of field
<input type="checkbox"> Primitive booleans and required Booleans
<input type="hidden"> Hidden fields when HtmlMetawidgetTag.setCreateHiddenFields is set to true
<input type="password"> Masked fields (except when read-only)
<input type="submit"> Actions (except read-only actions)
<input type="text"> Primitives (except booleans), Characters, Dates, Strings, and fields of unknown type. If the field has a maximum length, adds maxlength="..."
<select> Fields with lookups
raw text Read-only fields (except Collections and masked fields). If HtmlMetawidgetTag.setCreateHiddenFields is set to true, further adds a <input type="hidden"> so that something gets POSTed back
<textarea> Large String fields
org.metawidget.jsp.tagext
StubTag Hidden fields, read-only actions, read-only masked fields and Collections

RichFacesWidgetBuilder

RichFacesWidgetBuilder is a pluggable WidgetBuilder for the JSF UIMetawidget. It is intended to be used in conjunction with the default the JSF HtmlWidgetBuilder. For an example, see the section called “Alternate Widget Libraries”. RichFacesWidgetBuilder instantiates the following widgets for the following types of fields:

Widget Type of field
org.richfaces.component.html
UICalendar Dates
UIInputNumberSlider Primitives (except boolean and char) that have a maximum and minimum value
UIInputNumberSpinner Primitives (except boolean and char) and Numbers that have only one or neither minimum/maximum value

SpringWidgetBuilder

SpringWidgetBuilder is the default WidgetBuilder for SpringMetawidgetTag, albeit used in conjunction with the JSP HtmlWidgetBuilder. Like HtmlWidgetBuilder, it simply returns fragments of HTML. The JSP component model is too light to support returning tags containing child tags (ie. a SelectTag containing OptionTags). SpringWidgetBuilder returns the following widgets for the following types of fields:

Widget Type of field
org.springframework.web.servlet.tags.form
CheckboxTag Primitive booleans and required Booleans
HiddenInputTag Hidden fields when HtmlMetawidgetTag.setCreateHiddenFields is set to true
InputTag Primitives (except booleans), Characters, Dates, Strings, and fields of unknown type. If the field has a maximum length, calls setMaxlength
PasswordInputTag Masked fields (except when read-only)
raw text Read-only fields (except Collections and masked fields). If HtmlMetawidgetTag.setCreateHiddenFields is set to true, further adds a HiddenInputTag so that something gets POSTed back
SelectTag Fields with lookups
TextareaTag Large String fields
org.metawidget.jsp.tagext
StubTag Actions

StrutsWidgetBuilder

StrutsWidgetBuilder is the default WidgetBuilder for StrutsMetawidgetTag, albeit used in conjunction with the JSP HtmlWidgetBuilder. Like HtmlWidgetBuilder, it simply returns fragments of HTML. The JSP component model is too light to support returning tags containing child tags (ie. a SelectTag containing OptionTags). StrutsWidgetBuilder returns the following widgets for the following types of fields:

Widget Type of field
org.apache.struts.taglib.html
CheckboxTag Primitive booleans and required Booleans
SelectTag Fields with lookups
HiddenTag Hidden fields when HtmlMetawidgetTag.setCreateHiddenFields is set to true
PasswordTag Masked fields (except when read-only)
raw text Read-only fields (except Collections and masked fields).If HtmlMetawidgetTag.setCreateHiddenFields is set to true, further adds a HiddenTag so that something gets POSTed back
TextTag Primitives (except booleans), Characters, Dates, Strings, and fields of unknown type. If the field has a maximum length, calls setMaxlength
TextareaTag Large String fields
org.metawidget.jsp.tagext
StubTag Actions

4.3 Mobile Widget Builders

AndroidWidgetBuilder

AndroidWidgetBuilder is the default WidgetBuilder for AndroidMetawidget. It instantiates the following widgets for the following types of fields:

Widget Type of field
android.widget
CheckBox Primitive booleans and required Booleans
DatePicker Dates (provided they are not-nullable)
EditText Primitives (except booleans), Characters, Strings, and fields of unknown type. If the field is masked, sets a PasswordTransformationMethod. If the field is numeric, sets a DigitsKeyListener. If the field is large, calls setMinLines. If the field has a maximum length, sets a InputFilter.LengthFilter. If the field is a Date, sets a DateKeyListener.
PasswordTextBox Masked fields (except when read-only)
Spinner Fields with lookups
TextView Read-only fields (except Collections). If the field is masked, sets View.INVISIBLE
org.metawidget.gwt.client.ui
Stub Actions, hidden fields, read-only actions and Collections

4.4 Implementing Your Own WidgetBuilder

The pluggable nature of WidgetBuilders makes it easy to add your own. Because CompositeWidgetBuilder can be used to chain WidgetBuilders together, you only need worry about supporting your particular component library's widgets. You can simply return null for all other types of fields and rely on another WidgetBuilder further down the chain to instantiate one of the usual widgets.

For example, RichFacesWidgetBuilder only instantiates the 3 RichFaces components, and returns null for everything else.

A convenience base class, BaseWidgetBuilder, is provided.

5. Inspectors

This chapter covers each Inspector in detail. For an explanation of how Inspectors fit into the overall architecture of Metawidget, see Chapter 2, Architecture

5.1 Property Inspectors

BaseObjectInspector

BaseObjectInspector underlies many of the Inspectors that inspect objects (as opposed to, say, config files). It supports pluggable PropertyStyles and ActionStyles.

PropertyStyle

The PropertyStyle interface allows pluggable, fine-grained control over what is considered a 'property'

Different environments may have different approaches to defining what constitutes a property. For example, JavaBean-properties are convention-based, whereas Groovy has explicit property support. Equally, some environments may have framework-specific, base class properties that should be filtered out and excluded from the list of 'real' business model properties.

The default property style is JavaBeanPropertyStyle. To change it within metawidget.xml:

<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
	config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<propertyStyle>
	org.metawidget.inspector.impl.propertystyle.struts.StrutsActionFormPropertyStyle
	</propertyStyle>
</propertyTypeInspector>

To change it programmatically:

BaseObjectInspectorConfig config = new BaseObjectInspectorConfig();
config.setPropertyStyle( StrutsActionFormPropertyStyle.class );
metawidget.setInspector( new PropertyTypeInspector( config ) );

JavaBeanPropertyStyle

The JavaBeanPropertyStyle is the default property style used by all BaseObjectInspector subclasses (which includes all annotation inspectors).

This property style recognizes JavaBean-convention getXXX, setXXX and isXXX methods, as well as public member variables. In addition, it maintains a cache of reflected classes for performance.

[Tip]Note
When using getter methods and private members, make sure you annotate the getter not the private member. JavaBeanPropertyStyle cannot find annotations on private members, because the JavaBean specification does not define a way to determine which private members belong to which getters.

StrutsActionFormPropertyStyle

The StrutsActionFormPropertyStyle provides special handling for Struts ActionForms.

ActionForm properties essentially follow JavaBean-convention (this class extends JavaBeanPropertyStyle), but should not include properties from the Struts classes, (eg. getServlet, getServletWrapper and getMultipartRequestHandler in ActionForm and getDynaClass in DynaActionForm).

GroovyPropertyStyle

The GroovyPropertyStyle recognizes GroovyBean properties.

Groovy tries hard to make its GroovyBean properties compatible with JavaBean getters/setters, and indeed one can almost use JavaBeanPropertyStyle to read them. Unfortunately, GroovyBeans differ in that:

  • annotations defined on properties are only attached to the (generated) private member variable, not the (generated) getter/setter methods.

  • GroovyBeans define an implicit getMetaClass method which, although matching the JavaBean signature, should not be treated as a business model property.

JavassistPropertyStyle

The JavassistPropertyStyle extends JavaBeanPropertyStyle and makes use of Javassist for those environments that have it available.

Javassist is used to inspect the debug line numbering information embedded in JVM bytecode to sort getters/setters according to their original declaration order in the source code. This saves business objects having to use @UiComesAfter (or an XML file, or some other method) to impose an ordering.

However, a danger of this approach is that if the business objects are ever recompiled without debug line numbering information (eg. when moving from development to production) the UI fields will lose their ordering. Such a subtle bug may not be picked up, so as a safeguard JavassistPropertyStyle 'fails hard' with an InspectorException if line numbers are not available.

JavassistPropertyStyle uses the following sorting algorithm:

  • superclass public fields come first, sorted by name.

  • superclass methods come next, sorted by getter line number (or, if no getter, setter line number).

  • public fields come next, sorted by name.

  • methods come last, sorted by getter line number (or, if no getter, setter line number).

Note this algorithm is less flexible than @UiComesAfter, which can interleave superclass and subclass properties. However, it is possible to use both @UiComesAfter and JavassistPropertyStyle together to get the best of both worlds.

ActionStyle

The ActionStyle interface allows pluggable, fine-grained control over what is considered an 'action'.

Different environments may have different approaches to defining what constitutes an action. For example, the Swing AppFramework uses an @org.jdesktop.application.Action annotation.

The default property style is MetawidgetActionStyle. To change it within metawidget.xml:

<metawidgetAnnotationInspector config="org.metawidget.inspector.impl.BaseObjectInspectorConfig">
	<actionStyle>
		org.metawidget.inspector.impl.actionstyle.swing.SwingAppFrameworkActionStyle
	</actionStyle>
</metawidgetAnnotationInspector>

To change it programmatically:

BaseObjectInspectorConfig config = new BaseObjectInspectorConfig();
config.setActionStyle( SwingAppFrameworkActionStyle.class );
metawidget.setInspector( new MetawidgetAnnotationInspector( config ) );

Note these action styles only apply to BaseObjectInspector and its subclasses. This covers most annotation-recognising Inspectors (eg. JpaInspector, HibernateValidatorInspector) but not XML-based Inspectors. For example, JbpmInspector recognizes actions in JBoss jBPM files without any concept of an 'action style'.

MetawidgetActionStyle

The default Metawidget action style recognizes any method annotated with @UiAction. Action methods must not accept any parameters in their signature.

SwingAppFrameworkActionStyle

The SwingAppFrameworkActionStyle recognises Swing AppFramework's @Action annotation as denoting an action.

PropertyTypeInspector

PropertyTypeInspector extends BaseObjectInspector, and so inherits its features. In addition, it returns the following attributes for the following business properties:

Metawidget Attribute Property Type
lookup lookup of 'true, false' if the type is Boolean
lookup-labels lookup of 'Yes, No' if the type is Boolean. This will generally be localized by the Metawidget
no-setter if the property has no setXXX method. Note no-setter is distinct from read-only, because it is common to have no setter for a complex type (eg. Person.getAddress) but this shouldn't make all its contents (eg. Address.getStreet) read-only.
no-getter if the property has no getXXX method
type declared type of the property
actual-type if the actual type differs from the declared type (ie. it is a subclass)

Java5Inspector

Java5Inspector extends BaseObjectInspector, and so inherits its features. In addition, it returns the following attributes for the following business properties:

Metawidget Attribute Java5 feature
lookup values of enums, as returned by .name()
lookup-labels labels of enums, as returned by .toString()
parameterized-type if the property is using generics

5.2 Annotation Inspectors

Beyond the base issue of inspecting an object and its properties, a number of inspectors are focussed on third-party annotations. These annotation inspectors all extend BaseObjectInspector, and so inherit its features, but in addition they inspect the following frameworks.

HibernateValidatorInspector

HibernateValidatorInspector inspects Hibernate Validator annotations. It returns the following attributes for the following business properties:

Metawidget Attribute Hibernate Validator Annotation
maximum-fractional-digits @Digits(fractionalDigits=...)
maximum-integer-digits @Digits(integerDigits=...)
maximum-length @Length(min=...)
maximum-value @Max
minimum-length @Length(min=...)
minimum-value @Min
required @NotNull or @NotEmpty

JexlInspector

JexlInspector inspects @UiJexlAttribute annotations and sets arbitrary attributes based on the result of evaluating an Apache Commons JEXL expression. It can be used to introduce declarative UI scripting into environments that lack their own expression language (ie. JSP has an EL, Swing does not). For example:

import org.metawidget.inspector.commons.jexl.*;

public class Person {
	public boolean retired;
	@UiJexlAttribute( name = "hidden", value = "!this.retired" )
	public BigDecimal pension;
}

This code returns a hidden attribute based on evaluating the JEXL expression !this.retired (where this refers to the runtime instance of the Person being inspected). It could be used to show/hide the pension field in response to the retired checkbox being checked.

The JEXL expression language also supports branching statements. For example:

import org.metawidget.inspector.commons.jexl.*;

public class PersonController {

	@UiJexlAttribute( name = "label", value = "if ( this.readOnly ) 'Back'" )
	public void cancel() { ... }
}

This code overrides the label of an action to be either 'Back' or 'Cancel', depending on whether the Person was being edited. It is taken from the Swing Address Book sample (see the section called “Desktop Address Book”).

JpaInspector

JpaInspector inspects Java Persistence Architecture annotations. It returns the following attributes for the following business properties:

Metawidget Attribute JPA Annotation
hidden @Id, unless JpaInspectorConfig.setHideIds is false
large @Lob
maximum-length @Column(length=...)
required @Column(nullable=false) or @ManyToOne(optional=false)

MetawidgetAnnotationInspector

As much as possible, Metawidget tries to inspect metadata from existing sources, without introducing new concepts. Where that is not sufficient, MetawidgetAnnotationInspector adds a handful of annotations:

Metawidget Attribute Metawidget Annotation
n/a (order of fields) @UiComesAfter
section @UiSection - denotes the start of a logical grouping in the UI
label @UiLabel - denotes the label to used in the UI. Can be a resource key if the UI is using resource bundles, or an EL expression if the UI has an expression language (ie. JSF)
large @UiLarge - denotes the field should be 'large' in the UI (ie. a multi-line textbox)
hidden @UiHidden - denotes a value should be hidden in the UI. The value may still be rendered on the client, depending on the Metawidget (ie. for Web clients, may use a HTML hidden field)
read-only @UiReadOnly - denotes a value should be read-only in the UI
masked @UiMasked - denotes a value should be masked in the UI (eg. a password field)
any @UiAttributes and @UiAttribute - a 'catch all' for denoting arbitrary UI metadata

Troubleshooting

I get "java.lang.TypeNotPresentException"

If you are using Sun's implementation of Java, Metawidget's annotation support requires Java 5.0u6 or later, which includes a fix for this bug (Bug Parade ID: 6322301).

My inspector is not finding my annotations

Annotations are designed to 'silently fall away' in environments that do not support them: they never throw ClassDefNotFoundError. For example, if a JPA-annotated class is transferred to an application tier without ejb3-persistence.jar (or equivalent) in its classpath, the JPA annotations will disappear.

If this is the cause, either add the appropriate JAR to the tier, or consider implementing a remote Inspector (see Section 6.1, “Order Fields”).

5.3 XML Inspectors

Whilst we don't necessarily encourage the use of XML-based metadata, if you already have XML configuration files in your architecture Metawidget will take advantage of them.

Once nice feature of XML is that ordering of child elements (such as <property name="">) is explicit, so XML-based Inspectors make great 'first Inspectors' for use within CompositeInspector (eg. you don't need to also use @UiComesAfter).

BaseXmlInspector

BaseXmlInspector's config class, BaseXmlInspectorConfig, uses a setInputStream method to specify the location of the XML. This allows a variety of options for sourcing the XML. For example:

<xmlInspector xmlns="java:org.metawidget.inspector.xml"
	config="XmlInspectorConfig">
	<inputStream>
		<resource>com/myapp/metawidget-metadata.xml</resource>
	</inputStream>
</xmlInspector>

And:

<xmlInspector xmlns="java:org.metawidget.inspector.xml"
	config="XmlInspectorConfig">
	<inputStream>
		<url>http://myserver.com/my-xml.xml</url>
	</inputStream>
</xmlInspector>

As well as specifying multiple files (which will all be inspected as one):

<xmlInspector xmlns="java:org.metawidget.inspector.xml"
	config="XmlInspectorConfig">
	<inputStreams>
		<list>
			<url>http://myserver.com/my-xml-1.xml</url>
			<url>http://myserver.com/my-xml-2.xml</url>
		</list>			
	</inputStreams>
</xmlInspector>

CommonsValidatorInspector

CommonsValidatorInspector inspects Apache Commons Validator validation.xml files. It returns the following attributes for the following business properties:

Metawidget Attribute Validator XML
maximum-length <field depends="maxlength" /><var><var-name>maxlength</var-name>...
maximum-value <field depends="intRange" /><var><var-name>max</var-name>... (or floatRange or doubleRange)
minimum-length <field depends="minlength" /><var><var-name>minlength</var-name>...
minimum-value <field depends="intRange" /><var><var-name>min</var-name>... (or floatRange or doubleRange)
required <field depends="required" />

HibernateInspector

HibernateInspector inspects Hibernate hibernate.cfg.xml and mapping.hbm.xml files. For the former, it iterates over <session-factory>'s <mapping> elements and inspects all mapping files. It returns the following attributes for the following business properties:

Metawidget Attribute Hibernate XML
hidden <id />
large <property type="clob" />
maximum-length <property length="..." />
parameterized-type <bag type="..." /> or <list type="..." /> or <set type="..." />
required <property not-null="true" />

JexlXmlInspector

JexlXmlInspector inspects files in inspection-result-1.0.xsd format looking for XML attributes wrapped in ${...} notation. It processes these attributes as JEXL expressions before returning them. For example:

<inspection-result>

	<entity type="com.myapp.Person">
		<property name="pension" hidden="${!this.retired}"/>
	</entity>

</inspection-result>

JexlXmlInspector is effectively equivalent to JexlInspector but uses XML files instead of annotations.

XmlInspector

XmlInspector inspects files in inspection-result-1.0.xsd format. It can be used when no other Inspector is available for the given attribute.

Some attributes accept multiple values, such as lookup. These can be supplied as a comma-separated string. The values will be trimmed for whitespace. If the values themselves contain commas, they can be escaped with the \ character.

5.4 Implementing Your Own Inspector

Metawidget inspects a wide variety of back-end architectures. If your chosen back-end architecture is not supported 'out of the box', you may need to implement your own Inspector.

All Inspectors must implement the org.metawidget.inspector.Inspector interface:

public interface Inspector {
	String inspect( Object toInspect, String type, String... names )
		throws InspectionException;
}

The interface has only one method: inspect. Its parameters are:

  • an Object to inspect. This may be null, or can be ignored for Inspectors working off static metadata (such as config files)

  • a type. This must match the given Object, or some attribute in the inspected config file

  • a list of names to be traversed beneath the type

At a high level, the inspect parameters can be thought of as an XPath type/name/name into the object graph of the given Object (Metawidget doesn't use actual XPaths, though, for reasons discussed in the developer blogs).

The returned String must be an XML document conforming to inspection-result-1.0.xsd. To assist development, deploy your Inspector within CompositeInspector and use CompositeInspectorConfig.setValidating(true) to automatically validate the returned DOM during testing.

A number of convenience base classes are provided for different Inspectors:

  • For inspecting annotations and properties, BaseObjectInspector assists in iterating over public member variables, getter/setter methods and GroovyBean properties. See PropertyTypeInspector or JpaInspector for example usage.

    For inspecting XML files, BaseXmlInspector assists in opening and traversing through XML. See StrutsInspector for example usage.

When implementing your own Inspector, try to avoid technology-specific XML attribute names. For example, FacesInspector has an annotation @UiFacesNumberConverter. This annotation certainly has technology-specific parts to it, as it names a JSF Converter that only applies in JSF environments, so it is reasonable to name the XML attribute faces-converter-class. However, NumberConverters also use other properties about the field, such as the maximum number of integer digits. Such properties are not JSF-specific (eg. we can source the same property from Hibernate Validator's @Digits annotation), so are better named 'neutrally' (eg. maximum-integer-digits).

6. How To's

This section contains 'How To's' (or 'Recipes') for various scenarios you may encounter when using Metawidget.

6.1 Order Fields

Metawidget supports several ways to control the order of fields in the UI, depending on your architecture and your preference:

  • Annotate the fields with @UiComesAfter and use MetawidgetAnnotationInspector as the first inspector in your CompositeInspector chain.

  • Use one of the XML-based inspectors (such as XmlInspector or HibernateInspector) as the first inspector in your CompositeInspector chain. XML nodes are inherently ordered.

  • Compile your business model with debug information turned on, and use JavassistPropertyStyle. This approach uses Javassist to extract line numbering information, and order the fields in source file order.

  • Write your own Inspector.

  • Write your own PropertyStyle. For example, JavassistPropertyStyle extends JavaBeanPropertyStyle and reorders the fields using debug information.

6.2 Remote Inspection

Metawidget inspects back-end metadata and creates front-end UI widgets. If your application is split over multiple tiers, however, sometimes the back-end metadata is not accessible from the front-end. For example, annotations (such as JPA ones) are designed to 'fall away' if the class is transferred to a tier without JPA in its classpath. Equally, configuration files may not be accessible across tiers.

Metawidget supports these situations. Because each remoting environment is different, however, you will need to add a little code yourself. Every Inspector returns an XML string, which is inherently serializable and safe to pass across tiers. Therefore, to run inspection remotely:

  • create a back-end class suited to your environment, such as an EJB Session Bean. Have the back-end class instantiate an Inspector - either programmatically:

    XmlInspectorConfig config = new XmlInspectorConfig();
    config.setInputStream( getClass().getResourceAsStream( "metawidget-backend-metadata.xml" ));
    Inspector inspector = new XmlInspector( config );

    Or by using a metawidget.xml file and the ConfigReader helper class:

    inspector = ConfigReader.configure( "backend-metawidget.xml", Inspector.class );
  • have the back-end class declare the Inspector interface. Implement the interface by delegating to the inspector you just instantiated.

  • depending on your environment, it may futher be necessary to create a front-end class (a proxy). Have it declare the Inspector interface. Implement the interface by remoting to the back-end class and returning the XML string.

  • set the inspector on the Metawidget by using the setInspector method (rather than the setConfig method).

An example of this technique can be seen in GwtRemoteInspectorProxy and GwtRemoteInspectorImpl.

[Tip]Tip
All Inspectors are both thread-safe and immutable. Therefore you only need one Inspector for your entire application. Some remoting architectures support 'singletons' or 'service beans' well suited to this.

6.3 Combine Remote Inspections

If your architecture is strongly separated, some metadata may only be available in one tier (eg. JPA annotations in the back-end) and some only available in another tier (eg. struts-config.xml in the front-end).

For this, CompositeInspector supplies an overloaded method outside the normal Inspector interface. The overloaded CompositeInspector.inspect method takes an additional XML string parameter, which it uses as a starting point for meta-inspection.

Therefore, to combine metadata from different tiers:

  • create a front-end class that implements the Inspector interface

  • implement the interface by first remoting to the back-end class and returning the XML string, as before

  • next, delegate to a CompositeInspector to inspect the front-end, passing it the XML string from the back-end as a starting point

7. Performance

Performance is very important to Metawidget. Whilst generating a UI dynamically - as opposed to hard coding it statically - is always likely to involve a performance tradeoff, Metawidget supports a number of techniques to help minimize this.

7.1 JAR Size

Metawidget has no mandatory third-party JAR dependencies and is highly modular. This allows the standard metawidget.jar to be repackaged for different environments to save JAR size.

For example, the example-swing-addressbook-applet Ant task builds examples\swing\applet\addressbook\metawidget-applet.jar. This JAR includes only the SwingMetawidget and those Inspectors necessary for the Address Book example application. After further compression using pack200, it comes to around 45KB.

7.2 Rebinding

For Metawidgets that do not use automatic binding, the general approach is to call setToInspect and then setValue to populate the generated widgets with values. This technique has an implicit side effect: the values can also be repopulated as many times as required with new source objects, without re-calling setToInspect. This allows the Metawidget to be generated once and reused many times, mitigating the performance cost of generation.

For Metawidgets that do use automatic binding, however, setValue is never used. Setting new values requires re-calling setToInspect (and re-running generation) for every new source object.

To avoid this Metawidget supports a second, lightweight version of setToInspect called rebind. This function, like save, is only relevant when using automatic binding. Using rebind, a Metawidget can update the values in its generated widgets without re-running generation. This allows the Metawidget to be generated once and reused many times.

The downside of rebind is that the rebound source object must have exactly the same set of field names as the original object, else the call will fail. It becomes the responsibility of the caller to ensure this consistency.

For an example of rebinding, see the GWT Address Book sample application.

8. Epilogue

That concludes the Metawidget Reference Documentation.

For further documentation - including forums, bug reports, a Wiki community area and information on how to contribute - please visit http://www.metawidget.org.

Thank you for using Metawidget!