3.2 Web Metawidgets

3.2.1 GwtMetawidget

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

3.2.1.1 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. You can (optionally) configure the default Inspector. To do this, add a metawidget.xml file into WEB-INF, and an init-param to your web.xml:

    <init-param>
    	<param-name>config</param-name>
    	<param-value>metawidget.xml</param-value>
    </init-param>

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.

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

GwtMetawidget uses AJAX to perform server-side inspection

Figure 3.1. 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 9.3, “Rebinding”.

3.2.1.3 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.setLayout( new FlexTableLayout() )

3.2.1.4 Client-Side Inspection

As noted in Section 3.2.1.2, “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 9.3, “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 ) {
		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.

For an example of this technique see Section 1.3.8, “GWT Client Side Example”.

3.2.2 HtmlMetawidget (JSP)

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

3.2.3 UIMetawidget (JSF)

UIMetawidget is a Java Server Faces component. For an introduction to UIMetawidget, see Section 1.2.2, “Web Address Book” and Section 1.3.2, “Seam Example”.

3.2.3.1 Installation

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

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

  2. 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}"/>
    	...
  3. You can (optionally) configure the Metawidget by adding a metawidget.xml file into WEB-INF.

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

3.2.4 SpringMetawidget

SpringMetawidget is a Spring taglib. For an introduction to SpringMetawidget, see Section 1.2.2, “Web Address Book”.

3.2.4.1 Installation

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

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

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

    <%@ taglib uri="http://metawidget.org/spring" prefix="m" %>
    	...
    	<m:metawidget value="#{foo}"/>
    	...
  3. You can (optionally) configure the Metawidget by adding a metawidget.xml file into WEB-INF.

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

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

3.2.5 StrutsMetawidget

StrutsMetawidget is a Struts taglib. For an introduction to StrutsMetawidget, see Section 1.2.2, “Web Address Book”.

3.2.5.1 Installation

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

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

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

    <%@ taglib uri="http://metawidget.org/struts" prefix="m" %>
    	...
    	<m:metawidget value="#{foo}"/>
    	...
  3. You can (optionally) configure the Metawidget by adding a metawidget.xml file into WEB-INF.

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

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

3.2.5.4 Troubleshooting

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