Metawidget supports multiple Web frameworks, so that you can choose the one that matches your preferred UI environment.
GwtMetawidget is a client-side, JavaScript widget for GWT. For an introduction to GwtMetawidget, see Section 1.2.2, “Web Address Book” and Section 1.3.10, “GWT Hosted Mode Examples”.
There are five steps to installing Metawidget within a GWT application:
Update the application's .gwt.xml module to include Metawidet:
<module> <inherits name="org.metawidget.GwtMetawidget" /> ... </module>
Include both metawidget-all.jar and additional/gwt/metawidget-all-sources.jar in the CLASSPATH during the GWTCompiler phase. This provides the GwtMetawidget component source.
Add metawidget-all.jar into WEB-INF/lib. This provides the GwtRemoteInspectorImpl servlet.
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>
Optionally configure the Metawidget, as described below.
A working example of all these steps can be found in addressbook-gwt.war included in the examples distribution. You may also find the src/examples/gwt/addressbook Maven POM and the src/examples/gwt/clientside Maven POM useful.
GwtMetawidget is preconfigured with sensible defaults for GWT. You can change this configuration either programmatically or by creating a metawidget.xml file in WEB-INF and adding an init-param to your web.xml:
<init-param> <param-name>config</param-name> <param-value>metawidget.xml</param-value> </init-param>
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.
The process is:
instantiate the business object on the client-side as normal (i.e. as JavaScript)
give the business object to GwtMetawidget (a client-side, JavaScript GWT Widget)
GwtMetawidget uses AJAX to pass the business object to the server
the server, using Java, runs all the Inspectors (including reflection and annotations)
the server returns the inspection results as an XML document
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.5.4, “Rebinding”.
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 9.5.4, “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.setInspector( new MyClientSideInspector() )
...which overrides the default GwtRemoteInspectorProxy. For an example of this technique see Section 1.3.9, “GWT Client Side Example”.
GWT Widgets do not distinguish between what a widget is versus how it is rendered. Instead, GwtMetawidget mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using LayoutConfig classes and standard CSS.
GwtMetawidget supports localization through the setDictionaryName method. This method takes a String containing the name of a JavaScript variable declared in the host HTML page. For example:
<script type="text/javascript"> var bundle = { "add": "Add", "addBusiness": "Add Business Contact", "addPersonal": "Add Personal Contact" };</script>
Keys are looked up based on the name of each business property.
HtmlMetawidgetTag is a JSP taglib. For an introduction to HtmlMetawidgetTag, see Section 1.2.2, “Web Address Book”.
There are three steps to installing HtmlMetawidgetTag within a JSP application:
Add metawidget-all.jar into WEB-INF/lib.
Add a tag library descriptor to the top of your JSP page:
<%@ taglib uri="http://metawidget.org/html" prefix="m" %> ... <m:metawidget value="foo"/> ...
Optionally configure the Metawidget, as described below.
HtmlMetawidgetTag is preconfigured with sensible defaults for JSP. You can change this configuration by creating a metawidget.xml file. By default HtmlMetawidgetTag looks for this file in WEB-INF, but you can configure its location in web.xml:
<context-param> <param-name>org.metawidget.jsp.tagext.CONFIG_FILE</param-name> <param-value>config/metawidget.xml</param-value> </context-param>
Alternatively, you can specify an explicit config file at the tag level:
<m:metawidget value="foo" config="alternate-metawidget.xml"/>
JSP tag libraries do not distinguish between what a widget is versus how it is rendered. Instead, HtmlMetawidgetTag mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using LayoutConfig classes and standard CSS.
HtmlMetawidgetTag supports localization through a ResourceBundle set at the tag level:
<fmt:setBundle basename="org.metawidget.shared.allwidgets.resource.Resources" var="bundle"/> <m:metawidget bundle="${bundle}"/>
Keys are looked up based on the name of each business property.
With regard to overriding widget creation, JSP lacks 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 HtmlMetawidgetTag. As a next best thing, Metawidget includes StubTag for wrapping arbitrary tags. It also supports wrapping arbitrary HTML. For example:
<m:metawidget value="contact"> <m:stub value="communications"> <table class="data-table"> ... </table> </m:stub> </m:metawidget>
For an example, see Section 1.2.2, “Web Address Book”.
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”.
There are four steps to installing UIMetawidget within a JSF application:
Add metawidget-all.jar into WEB-INF/lib.
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}"/> ...
Optionally configure the Metawidget, as described below.
UIMetawidget is preconfigured with sensible defaults for JSF. You can change this configuration by creating a metawidget.xml file. By default UIMetawidget looks for this file in WEB-INF, but you can configure its location in web.xml:
<context-param> <param-name>org.metawidget.faces.component.CONFIG_FILE</param-name> <param-value>config/metawidget.xml</param-value> </context-param>
Alternatively, you can specify an explicit config file at the tag level:
<m:metawidget value="#{foo}" config="alternate-metawidget.xml"/>
Or you can use JSF's binding attribute to configure Metawidget programmatically:
<m:metawidget value="#{foo}" binding="#{myBean.metawidget}"/>
Then in your managed bean:
public class MyManagedBean { public UIMetawidget getMetawidget() { // First-time init // // JSF spec: "When a component instance is first created (typically by virtue of being // referenced by a UIComponentELTag in a JSP page), the JSF implementation will retrieve the // ValueExpression for the name binding, and call getValue() on it. If this call returns a // non-null UIComponent value (because the JavaBean programmatically instantiated and // configured a component already), that instance will be added to the component tree that // is being created" UIMetawidget metawidget = new HtmlMetawidget(); initMetawidget( metawidget ); return metawidget; } public void setMetawidget( UIMetawidget metawidget ) { // POST-back init // // JSF spec: "When a component tree is recreated during the Restore View phase of // the request processing lifecycle, for each component that has a ValueExpression // associated with the name 'binding', setValue() will be called on it, passing the // recreated component instance" initMetawidget( metawidget ); } private void initMetawidget( UIMetawidget metawidget ) { ...configure Metawidget programmatically... } }
This approach can also be used to inspect Objects and Classes directly, without a value binding:
<m:metawidget binding="#{myBean.metawidget}"/>
Then in your managed bean:
public class MyManagedBean { ...as previous example... private void initMetawidget( UIMetawidget metawidget ) { metawidget.setValue( MyBusinessObject.class ); } }
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. CssStyleProcessor follows this convention. When expanding to a single widget (such as an HtmlInputText) the style and styleClass attributes are applied to it. When expanding to multiple widgets, all widgets have the same style and styleClass attributes applied to them. This can be useful as a way to 'tag' every widget.
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 (e.g. HTML or WML), they can equally be used to render the same widget to the same platform but in different ways. Renderers can be configured either per tag:
<m:metawidget ... rendererType="div"/>
Or globally in metawidget.xml:
<htmlMetawidget xmlns="java:org.metawidget.faces.component.html"> <rendererType> <string>myRenderer</string> </rendererType> ... </htmlMetawidget>
HtmlTableLayoutRenderer is the default LayoutRenderer. It further defines parameters such as tableStyle, labelStyle and columnClasses (see the the section called “HtmlTableLayoutRenderer” 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 Section 8.2.2, “JSF Layouts” for a complete list).
UIMetawidget supports localization through a ResourceBundle set either at the application level in faces-config.xml:
<faces-config> <application> <message-bundle>com.myapp.resource.Resources</message-bundle> </application> </faces-config>
Or set at the tag level:
<m:metawidget bundle="#{myBean.bundle}"/>
By default, JSF's UIData component supports rendering data of type java.util.List, javax.faces.model.DataModel or Java arrays. UIMetawidget's HtmlWidgetBuilder respects this and will generate an HtmlDataTable for each type. It will decide UIColumns based on inspecting the List or array's component type for properties marked required (if any).
This will give a basic out-of-the-box representation of a Collection, but it is expected developers will ultimately define their own WidgetBuilder to represent a Collection to their preference. This may include: supporting java.util.Set; allowing inline editing; supporting pagination and so on. For more information, see this blog entry.
JSF f:facet tags can be used within m:metawidget tags to adorn layouts. The exact name and interpretation of the facet is at the discretion of the Layout. For example HtmlTableLayoutRenderer recognizes:
<m:metawidget value="..."> <f:facet name="header"> ...instructions could go here... </f:facet> <f:facet name="footer"> ...button bar could go here... </f:facet> </m:metawidget>
There is a special case when the m:metawidget's value resolves directly to a single widget, rather than iterating over properties and creating multiple sub-widgets (eg. if value is pointed directly at a String property it will resolve to an HtmlInputText). In this case any facets (and converters and validators) apply not to the Layout but to the generated widget. This can be useful to declaratively attach behaviours:
<m:metawidget value="#{foo.name}"> <a4j:support event="onclick" onsubmit="alert('onclick')"/> </m:metawidget>
Or:
<m:metawidget value="#{foo.name}"> <f:validator validatorId="myValidator"/> </m:metawidget>
If you are generating a single widget but still want the facet to apply to the Layout, wrap it in a parent m:metawidget that has no value:
<m:metawidget> <m:metawidget value="#{foo.name}" rendererType="simple"/> <f:facet name="footer"> ...button bar could go here... </f:facet> </m:metawidget>
A parent m:metawidget with no value simply acts as a way to lay out children.
UIMetawidget supports JSF 2.0. It automatically detects whether JSF 2.0 classes are available (specifically javax.faces.event.PreRenderViewEvent) and switches to using them. However not all JSF 2.0 implementations properly support PreRenderViewEvent. You need at least Mojarra 2.2 (specifically a version that includes this fix) or MyFaces 2.0.3 (specifically a version that includes this fix). If you need to work with older versions, you must...
disable partial state saving by setting javax.faces.PARTIAL_STATE_SAVING to false in web.xml
disable PreRenderViewEvent support by setting org.metawidget.faces.component.DONT_USE_PRERENDER_VIEW_EVENT to true in web.xml; and
...but even then, Metawidget exercises the dynamic capabilites of JSF implementations more than most. If you start experiencing strange behaviour (such as components being re-ordered following POST back) upgrade to the latest version of JSF your environment permits.
SpringMetawidgetTag is a Spring taglib. For an introduction to SpringMetawidgetTag, see Section 1.2.2, “Web Address Book”.
There are three steps to installing SpringMetawidgetTag within a Spring application:
Add metawidget-all.jar into WEB-INF/lib.
Add a tag library descriptor to the top of your JSP page:
<%@ taglib uri="http://metawidget.org/spring" prefix="m" %> ... <m:metawidget path="fooCommand"/> ...
Optionally configure the Metawidget, as described below.
SpringMetawidgetTag is preconfigured with sensible defaults for Spring. You can change this configuration by creating a metawidget.xml file. By default SpringMetawidgetTag looks for this file in WEB-INF, but you can configure its location in web.xml:
<context-param> <param-name>org.metawidget.jsp.tagext.CONFIG_FILE</param-name> <param-value>config/metawidget.xml</param-value> </context-param>
Alternatively, you can specify an explicit config file at the tag level:
<m:metawidget path="fooCommand" config="alternate-metawidget.xml"/>
JSP tag libraries do not distinguish between what a widget is versus how it is rendered. Instead, SpringMetawidgetTag mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using LayoutConfig classes.
SpringMetawidgetTag supports localization through a ResourceBundle set either at the application level in servlet.xml:
<beans> <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"> <property name="basename"> <value>com.myapp.resource.Resources</value> </property> </bean> </beans>
Or set at the tag level:
<fmt:setBundle basename="com.myapp.resource.Resources" var="bundle"/> <m:metawidget bundle="${bundle}"/>
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. For example:
<m:metawidget path="contactCommand"> <m:stub path="communications"> <table class="data-table"> ... </table> </m:stub> </m:metawidget>
For an example, see Section 1.2.2, “Web Address Book”.
StrutsMetawidgetTag is a Struts taglib. For an introduction to StrutsMetawidgetTag, see Section 1.2.2, “Web Address Book”.
There are three steps to installing StrutsMetawidgetTag within a Struts application:
Add metawidget-all.jar into WEB-INF/lib.
Add a tag library descriptor to the top of your JSP page:
<%@ taglib uri="http://metawidget.org/struts" prefix="m" %> ... <m:metawidget property="fooForm"/> ...
Optionally configure the Metawidget, as described below.
StrutsMetawidgetTag is preconfigured with sensible defaults for Spring. You can change this configuration by creating a metawidget.xml file. By default StrutsMetawidgetTag looks for this file in WEB-INF, but you can configure its location in web.xml:
<context-param> <param-name>org.metawidget.jsp.tagext.CONFIG_FILE</param-name> <param-value>config/metawidget.xml</param-value> </context-param>
Alternatively, you can specify an explicit config file at the tag level:
<m:metawidget property="fooForm" config="alternate-metawidget.xml"/>
JSP tag libraries do not distinguish between what a widget is versus how it is rendered. Instead, StrutsMetawidgetTag mimics this by providing loosely coupled Layout classes. Each Layout can further be configured by using specific param tags.
StrutsMetawidgetTag supports localization through a ResourceBundle set either at the application level in struts-config.xml:
<struts-config> <message-resources parameter="com.myapp.resource.Resources" null="false"/> </struts-config>
Or set at the tag level:
<fmt:setBundle basename="com.myapp.resource.Resources" var="bundle"/> <m:metawidget bundle="${bundle}"/>
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 StrutsMetawidgetTag. As a next best thing, Metawidget includes StubTag for wrapping arbitrary tags. It also supports wrapping arbitrary HTML. For example:
<m:metawidget property="contactForm"> <m:stub property="communications"> <table class="data-table"> ... </table> </m:stub> </m:metawidget>
For an example, see Section 1.2.2, “Web Address Book”.
VaadinMetawidget is a Vaadin CustomComponent. For an introduction to VaadinMetawidget, see Section 1.2.2, “Web Address Book”.
There are two steps to installing VaadinMetawidget within a Vaadin application:
Add metawidget-all.jar into WEB-INF/lib.
Optionally configure the Metawidget, as described below.